从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会怎么样?

    推荐阅读