Vue3.0增加了watchEffect,正好在项目中用到,很好用,但是发现一个现象:watchEffect没有触发ref数组的变化,直接上传代码。代码执行,setTimeout后,console.log不再重新执行。为什么?因为网上基本找不到有用的资料,所以就去找源码。我找到了原因,上面的代码(我简化了它)}returnnewRefImpl(rawValue,shallow);}classRefImpl{constructor(value,_shallow){this._value=_shallow?价值:toReactive(价值);}getvalue(){trackRefValue(this);返回这个._value;}设置值(newVal){newVal=this._shallow?新值:toRaw(新值);如果(hasChanged(newVal,this._rawValue)){this._rawValue=newVal;this._value=this._shallow?newVal:toReactive(newVal);triggerRefValue(这个,newVal);}}}查看代码可以看出,ref其实是执行了createRef方法,返回的是RefImpl的一个实例。也就是说,ref实际上返回的是RefImpl的一个实例。RefImpl类劫持获取、设置和处理它。也就是说,ref在一定程度上并没有使用Proxy。不像网上文章说的那样:“Vue3其实是在使用Proxy,所有的数据都是通过Proxy来响应的”。(当然是在toReactive方法中使用了)看到这一步,我们发现list其实就是RefImpl的一个实例。这个实例在push过程中并没有触发set,自然不可能执行console.log。那么,新的问题来了,为什么不设置trigger呢?问得好,你可以想想为什么我们要用const来定义一个数组,然后往数组里加东西而不报错。下一个问题,如果,我是下面的代码,会发生什么?<脚本设置>从'vue'导入{ref,watchEffect};constlist=ref([1,2,3])watchEffect(()=>{console.log(list.value.length)//添加长度})setTimeout(()=>{list.value.push(4)},5000)长度添加到watchEffect中,其他不变。你会发现console也会在setTimeout之后执行,这又是为什么?classRefImpl{constructor(value,_shallow){this._value=_shallow?价值:toReactive(价值);//答案在这个toReactive}}consttoReactive=(value)=>{returnisObject(value)?反应性(价值):价值};也就是说其实是判断toReactive,如果是对象,就会被reactive包裹起来,reactive内部就是Proxy劫持了get和set。代码就不贴了,有点长。也就是说,list的[1,2,3,4]被Proxy劫持了,相应的回调会通过get和set来执行。第一次获取长度时,触发Proxy的get。这时候watchEffect和Proxy可以理解为绑定连接,所以后面push的时候调用watchEffect。嗯,开花结束了。课外题,watchEffect会这样,那watch会怎么样呢?
