关于vue3:从Vue30的watchEffect没有触发ref数组变化说起

75次阅读

共计 1853 个字符,预计需要花费 5 分钟才能阅读完成。

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

正文完
 0