从Vue3.0的watchEffect没有触发ref数组变化说起
Vue3.0加入了watchEffect,刚好项目使用到了,很好用,但是发现了一个现象:watchEffect没有触发ref的数组变化,直接上代码。
代码执行,setTimeout后,console.log没有重新执行。
为什么呢?
因为网上基本查不到有用的资料,我去翻了源码。找到了原因,上代码(我做了简洁化处理)
function ref(value) {
return createRef(value, false);
}
function createRef(rawValue, shallow) {
if (isRef(rawValue)) {
return rawValue;
}
return new RefImpl(rawValue, shallow);
}
class RefImpl {
constructor(value, _shallow) {
this._value = https://www.it610.com/article/_shallow ? value : toReactive(value);
}
get value() {
trackRefValue(this);
return this._value;
}
set value(newVal) {
newVal = this._shallow ? newVal : toRaw(newVal);
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal;
this._value = this._shallow ? newVal : toReactive(newVal);
triggerRefValue(this, newVal);
}
}
}
看代码可以看出来,ref实际执行了createRef方法,该方法返回了一个RefImpl的实例。
也就是说,ref实际返回了一个RefImpl的实例。该RefImpl类劫持了get,set,做了处理。
换句话说,ref其实在一定程度上,是没有使用Proxy的,并不是像网上文章所说:"Vue3其实都在用Proxy,所有数据都是靠Proxy去实现响应的"。(当然,toReactive方法里面使用了)
看到了这一步,我们发现,其实list是RefImpl的一个实例,这个实例在push过程中,并不会触发set,所以自然无法执行console.log
那么,新问题来了,为什么不会触发set?
问得好,你可以想一下,为什么我们用const定义一个数组,再往数组里面加东西,不会报错。
下一个问题,如果,我是下面的代码,会怎么样?
watchEffect里面加入了length,其他都不变,你会发现,setTimeout后也会执行console了,这又是为什么?
class RefImpl {
constructor(value, _shallow) {
this._value = https://www.it610.com/article/_shallow ? value : toReactive(value);
// 答案在这个toReactive里面
}
}
const toReactive = (value) => {
return isObject(value) ? reactive(value) : value
};
也就是说,toReactive其实是判断了,如果是对象,还会被reactive包裹,reactive内部就是Proxy劫持了get,set。代码我就不贴了,有点长
也就是说,list的[1,2,3,4],被Proxy劫持了,被get和set都会有执行对应的回调。当第一次获取length的时候触发了Proxy的get,而这个时候watchEffect和Proxy可以理解为绑定了,有了联系,所以之后push的时候,watchEffect就被调用了。
好了,完结撒花。
【从Vue3.0的watchEffect没有触发ref数组变化说起】课外题,watchEffect会这样,那么watch会怎么样?
推荐阅读
- 热闹中的孤独
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 放屁有这三个特征的,请注意啦!这说明你的身体毒素太多
- 一个人的旅行,三亚
- 布丽吉特,人生绝对的赢家
- 慢慢的美丽
- 尽力
- 一个小故事,我的思考。
- 家乡的那条小河
- Docker应用:容器间通信与Mariadb数据库主从复制