目录
一、computed
在vue2中,我们也已经使用过computed属性了
在Vue3的Composition API中,则是需要在setup函数中使用computed方法来编写一个计算属性
computed :
- 方式一:接收一个getter函数,并为 getter 函数返回的值,返回一个不变的 ref 对象
- 方式二:接收一个具有 get 和 set 的对象,返回一个可变的(可读写)ref 对象
<template>
<div>
<h1>{
{ resName }}</h1>
<button @click="change">change resName</button>
</div>
</template>
<script>
import { ref, computed } from 'vue';
export default {
setup() {
let firstName = ref('hello');
let lastName = ref('world');
// 方式一
// let resName = computed(() => firstName.value + lastName.value);
//方式二
let resName = computed({
get() {
return firstName.value + ' ' + lastName.value;
},
set(newValue) {
const names = newValue.split(' ');
firstName.value = names[0];
lastName.value = names[1];
}
});
const change = () => {
resName.value = 'abc ddd';
console.log(resName);
};
return {
resName,
change
};
}
};
</script>
效果
二、侦听数据的变化之watchEffect
在vue2中,是使用watch选项来侦听data或者props数据的变化,而在vue3中,有两种方式
1. watchEffect的基本使用
watchEffect :
- 自动收集响应式数据的依赖
- watchEffect传入的函数会被立即执行一次,并且在执行的过程中会收集依赖
- 只有收集的依赖发生变化时,watchEffect传入的函数才会再次执行
代码
<template>
<div>
<h1>{
{ name }} - {
{ age }}</h1>
<button @click="changeName">changeName</button>
<button @click="changeAge">changeAge</button>
</div>
</template>
<script>
import { ref, watchEffect } from 'vue';
export default {
setup() {
let name = ref('star');
let age = ref(18);
const changeName = () => (name.value === 'star' ? (name.value = 'xuanyu') : (name.value = 'star'));
const changeAge = () => age.value++;
watchEffect(() => {
// 因为这里只使用了name,所以只会监听name,如果把age也写进来,那么两个都会监听
console.log('name:', name.value);
});
return { name, age, changeName, changeAge };
}
};
</script>
<style lang="scss" scoped></style>
效果
2. watchEffect的停止监听
假如,某些情况想要停止监听,那么可以获取watchEffect的返回值函数,调用该函数即可
代码
<template>
<div>
<h1>{
{ name }} - {
{ age }}</h1>
<button @click="changeName">changeName</button>
<button @click="changeAge">changeAge</button>
</div>
</template>
<script>
import { ref, watchEffect } from 'vue';
export default {
setup() {
let name = ref('star');
let age = ref(18);
const changeName = () => (name.value === 'star' ? (name.value = 'xuanyu') : (name.value = 'star'));
// 获取返回值
const stopWatchEffect = watchEffect(() => {
// 自动监听age
console.log('age:', age.value);
});
const changeAge = () => {
age.value++;
if (age.value > 22) {
// 停止监听
stopWatchEffect();
}
};
return { name, age, changeName, changeAge };
}
};
</script>
<style lang="scss" scoped></style>
效果
3. watchEffect清除副作用
什么是清除副作用
- 比如在开发中可能需要在侦听函数中执行网络请求,但是在网络请求还没有达到的时候,停止了侦听器,或者侦听器侦听函数被再次执行了
- 那么上一次的网络请求应该被取消掉,这个时候就可以清除上一次的副作用
如何使用
- 在给watchEffect传入的函数被回调时,其实可以获取到一个参数:onInvalidate
- 当副作用即将重新执行 或者 侦听器被停止 时会执行该函数传入的回调函数
- 可以在传入的回调函数中,执行一些清除工作
代码
<template>
<div>
<h1>{
{ name }} - {
{ age }}</h1>
<button @click="changeName">changeName</button>
<button @click="changeAge">changeAge</button>
</div>
</template>
<script>
import { ref, watchEffect } from 'vue';
export default {
setup() {
let name = ref('star');
let age = ref(18);
const changeName = () => (name.value === 'star' ? (name.value = 'xuanyu') : (name.value = 'star'));
// 获取返回值
const stopWatchEffect = watchEffect((onInvalidate) => {
// 自动监听age
console.log('age:', age.value);
const timer = setTimeout(() => {
console.log('1s后执行');
}, 1000);
// 取消副作用,清除上一次的定时器
onInvalidate(() => {
clearTimeout(timer);
});
});
const changeAge = () => {
age.value++;
if (age.value > 22) {
// 停止监听
stopWatchEffect();
}
};
return { name, age, changeName, changeAge };
}
};
</script>
<style lang="scss" scoped></style>
效果
4. watchEffect的执行时机
- pre : 默认值,它会在元素 挂载 或者 更新 之前执行
- post : 元素 挂载 或者 更新 之后执行
- sync : 强制同步一起执行,效率很低,不推荐
一般来说,如果需要等dom挂载完后操作dom元素的时候,传post即可
代码
<template>
<div>
<h1 ref="hRef">star</h1>
</div>
</template>
<script>
import { ref, watchEffect } from 'vue';
export default {
setup() {
// 先声明一个null的ref,等到元素挂载后,会自动赋值过来
const hRef = ref(null);
watchEffect(
() => {
console.log(hRef.value);
},
{
// 设定元素挂载再执行这个函数
flush: 'post'
}
);
return {
hRef
};
}
};
</script>
<style lang="scss" scoped></style>
三、 侦听数据的变化之watch
watch :
- watch需要侦听特定的数据源,并在回调函数中执行副作用
- 默认情况下它是惰性的,只有当被侦听的源发生变化时才会执行回调
- 与watchEffect的比较,watch允许我们懒执行副作用(第一次不会直接执行)
- 更具体的说明当哪些状态发生变化时,触发侦听器的执行
- 访问侦听状态变化前后的值
1. 侦听单个数据源
watch侦听函数的数据源有两种类型:
- 一个getter函数:但是该getter函数必须引用可响应式的对象(比如reactive或者ref)
- 直接写入一个可响应式的对象,reactive或者ref(比较常用的是ref)
01-传入getter函数
用来监听ref活着reactive对象中的具体属性可使用
02-传入响应式对象
- ref对象
<template>
<div>
<h1>
{
{ star }}
</h1>
<button @click="change">change</button>
</div>
</template>
<script>
import { ref, reactive, watch } from 'vue';
export default {
setup() {
/**
* 一、如果ref响应式对象中的是基本数据类型
*/
const star = ref('123');
// const star = ref('123');
// 改变
const change = () => (star.value = 'coder');
// 那么直接这样传入即可,拿到的是value值本身
watch(star, (newValue, oldValue) => {
console.log('newValue:', newValue, 'oldValue:', oldValue);
});
/**
* 二、如果ref对象中的是对象数据,那么也需要结构 () => ({...star.value}),如果不结构,拿到的是ref对象
*/
const star = ref({ name: '123', age: 14 });
// 改变
const change = () => (star.value.name = 'coder');
// 那么需要结构,如果不结构,拿到的是ref对象
watch(
() => ({ ...star.value }),
(newValue, oldValue) => {
console.log('newValue:', newValue, 'oldValue:', oldValue);
}
);
return {
star,
change
};
}
};
</script>
<style lang="scss" scoped></style>
- reactive对象
<template>
<div>
<h1>
{
{ star.name }}
</h1>
<button @click="change">change</button>
</div>
</template>
<script>
import { ref, reactive, watch } from 'vue';
export default {
setup() {
const star = reactive({ name: '123', age: 15 });
// 改变
const change = () => (star.name = 'coder');
/**
* 一、如果直接传入reactive对象,那么打印出的也是两个reactive对象
*/
watch(star, (newValue, oldValue) => {
console.log('newValue:', newValue, 'oldValue:', oldValue);
});
/**
* 二、如果希望获得普通对象,结构一下
*/
watch(
() => ({ ...star }),
(newValue, oldValue) => {
console.log('newValue:', newValue, 'oldValue:', oldValue);
}
);
return {
star,
change
};
}
};
</script>
<style lang="scss" scoped></style>
2. 侦听多个数据源
传入数组即可
代码
<template>
<div>
<h1>
{
{ star.name }}
</h1>
<button @click="change">change</button>
</div>
</template>
<script>
import { ref, reactive, watch } from 'vue';
export default {
setup() {
// ref对象
const coder = ref('know');
// reactive对象
const star = reactive({ name: '123', age: 15 });
// 改变
const change = () => {
star.name = 'coder';
coder.value = 'unKnow';
};
// 传入数组即可 // 对应结构
watch([coder, () => ({ ...star })], ([newCoder, newStar], [oldCoder, oldStar]) => {
console.log(newCoder, newStar, '----', oldCoder, oldStar);
});
return {
star,
change
};
}
};
</script>
<style lang="scss" scoped></style>
效果
3. watch的选项
配置第三个参数 :
- deep : 是否深度监听
-
immediate : 是否立即执行
代码
<template>
<div>
<h1>
{
{ info.name }}
</h1>
</div>
</template>
<script>
import { ref, reactive, watch } from 'vue'
export default {
setup() {
// reactive对象
const info = reactive({
name: '123',
age: 15,
friend: { name: '456' }
})
watch(
() => {
const obj = { ...info }
// 因为有两层,需要自己结构下,否则返回的是proxy
obj.friend = { ...obj.friend }
return obj
},
(newValue, oldValue) => {
console.log(newValue, oldValue)
},
{
// 深度监听
deep: true,
// 立即执行
immediate: true
}
)
return {
info
}
}
}
</script>
<style lang="scss" scoped></style>