watchEffect
watch、watchEffect 不同点
const count = ref(0)
watchEffect(() => console.log(count.value))
// -> logs 0 组件初始化的时候就会执行一次
setTimeout(() => {
count.value++
// -> logs 1 依赖发生变化在执行
}, 100)
-
第一点我们可以从示例代码中看到 watchEffect 不需要指定监听的属性,他会自动的收集依赖, 只要我们回调中引用到了 响应式的属性, 那么当这些属性变更的时候,这个回调都会执行,而 watch 只能监听指定的属性而做出变更(v3开始可以同时指定多个)。
-
第二点就是 watch 可以获取到新值与旧值(更新前的值),而 watchEffect 是拿不到的。
-
第三点是 watchEffect 如果存在的话,在组件初始化的时候就会执行一次用以收集依赖(与computed同理),而后收集到的依赖发生变化,这个回调才会再次执行,而 watch 不需要,因为他一开始就指定了依赖。
-
在watchEffect里操作响应式数据,不会引起无限循环监听,这虽然很显而易见,但是也在此说一句。
-
多个watchEffect的执行顺序是watchEffect的书写顺序。
watchEffect(() => {
console.log('b:', s.value.b);
});
watchEffect(() => {
console.log('a - b:', s.value.a + '-' + s.value.b);
});
watchEffect(() => {
console.log('value:', s.value);
});
停止侦听
如果 watchEffect 是在 setup 或者 生命周期里面注册的话,在组件取消挂载的时候会自动的停止掉
const stop = watchEffect(() => {
/* ... */
})
// later
stop()
side effect 副作用
什么是副作用: 如在开发中我们需要在侦听函数中执行网络请求(http请求),但是在网络请求还没有达到的时候,我们停止了侦听器,也就是不会在请求第二次,避免多次请求也就是节流
副作用主要有:DOM更新、watchEffect、watch、computed、、、、
你没看错,既然更新视图层才是主作用,那么视图层更新到DOM上在Vue眼里是副作用,而且,变更响应式数据触发执行computed和触发执行watchEffect当然也是副作用。所以watchEffect本身就是副作用。
watchEffect(() => {
// 异步api调用,返回一个操作对象
const apiCall = someAsyncMethod(props.userID)
onInvalidate(() => {
// 取消异步api的调用。
apiCall.cancel()
})
})
例子
根据 pageSize limit 查询列表项;
列表项查询每次的查询时间需要3s;
通过 watchEffect 实现列表项数据的自动查询;
同时需要实现UI的有效更新;在更新前pageSize OR limit 参数发生了多次变化, 只显示最终效果
import{
watchEffect,
ref,
createApp,
} from "vue"
// 查询数据
function fetchData(pageSize, limit){
return new Promise(resolve => {
// 模拟查询数据需要的时间
setTimeout(() => {
resolve(`查询第 ${pageSize} 页, 每页数量 ${limit}`)
}, 3000)
})
}
let app = createApp({
setup(){
let pageSize = ref(1);
let limit = ref(10);
let list = ref(null);
watchEffect(async (onInvalidate) => {
let cancel = false;
onInvalidate(() => {
cancel = true;
})
let result = await fetchData(pageSize.value, limit.value)
// 已经失效就不更新
if(cancel) return ;
list.value = result;
})
return {
pageSize,
limit,
list,
}
},
watchEffect的执行时机
//如:<h2 ref="x"></h2>
import {watchEffect,ref} from 'vue'
setup(){
const x=ref(null) //将这个ref绑定到指定标签或组件上
watchEffect(()=>{
console.log(x)
})
return {
x
}
}
- 此时会打印两个结果,第一次为null,第二次才为h2元素,这是因为setup函数在执行时就会立即执行传入的副作用函数,这个时候DOM并没有挂载,所以打印为null
- 而当DOM挂载时,会给x的ref对象赋值新的值,副作用函数会再次执行,打印出来对应的元素
- 调整watchEffect的执行时机,如果我们希望在第一次的时候就打印出来对应的元素呢?
- 这个时候我们需要改变副作用函数的执行时机:它的默认值是pre,调整为post
watchEffect(()=>{
console.log(x)
},{
flush:'post' //在组件更新完成之后操作
})
Vue 2使用this.$nextTick()去获取组件更新完成之后的DOM,和上面是一个意思。
watchEffect、computed 的不同
computed和watchEffect相同的地方是会自动收集依赖,在值更新时会触发回调,会初始化调用一次。但是在触发初始化的时机是不一样的**,如果computed的值没有被使用,是不会触发回调的**,只有在该值被使用的时候才会触发回调,但watchEffect是在setup的时候就会初始化。
Vue 3 watch
官方说,watch也有停止侦听,清除副作用、副作用刷新时机和侦听器调试行为
Vue 3 computed特点:
Vue 3跟Vue 2的computed的差别在于,Vue 2是所有计算属性都是根对象的属性,Vue 3是计算属性都是独立变量
- computed默认接收getter函数,也可以接收一个对象,对象里有get和set方法。set方法接收一个val参数。初学者可能会忘记写getter函数,只写计算表达式,要注意这点。
- computed一定返回ref对象,所以并不需要在计算函数里给返回值添加响应式,这属于画蛇添足。
参考1