Vue3.0退出了watchEffect,刚好我的项目应用到了,很好用,然而发现了一个景象:watchEffect没有触发ref的数组变动,间接上代码。
<script setup>import { ref, watchEffect } from 'vue';const list = ref([1,2,3])watchEffect(() => { console.log(list.value)})setTimeout(() => { list.value.push(4)}, 5000)</script>
代码执行,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 = _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定义一个数组,再往数组外面加货色,不会报错。
下一个问题,如果,我是上面的代码,会怎么样?
<script setup>import { ref, watchEffect } from 'vue';const list = ref([1,2,3])watchEffect(() => { console.log(list.value.length) // 加一个length})setTimeout(() => { list.value.push(4)}, 5000)</script>
watchEffect外面退出了length,其余都不变,你会发现,setTimeout后也会执行console了,这又是为什么?
class RefImpl { constructor(value, _shallow) { this._value = _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就被调用了。
好了,完结撒花。
课外题,watchEffect会这样,那么watch会怎么样?