watch的实现原理
watch本质观测一个响应式数据的变化,当数据变化时,会执行对应的回调函数。举个例子
1 | watch(obj,() => { |
watch本质上是利用了effect和scheduler选项
1 | function watch(source, cb) { |
为了让watch函数具有通用性,我们需要封装一个通用的读取函数,用来建立响应式联系
1 | function traverse(value, seen = new Set()) { |
于是我们就可以把source.foo
改成traverse(source.foo)
watch还可以接收一个get函数
让我们增强一下watch函数
1 | function watch(source, cb) { |
到此,watch函数还差一个很重要的能力,即在回调函数中读取新值和旧值,进行这个改造需要利用effect的lazy选项
1 | function watch(source, cb) { |
立即执行的watch
当watch的immediate选项为true时,回调函数会在watch被创建时立即执行一次
1 | function watch(source, cb, options = {}) { |
这样就实现了回调函数的立即执行功能.由于函数是立即执行的,oldValue的值是undefined,这是符合预期的.
过期的副作用
竞态问题也是watch函数需要解决掉一个问题.
1 | let finalData |
如果连续触发两次回调函数,先发送请求A再发送请求B,但是由于网络问题,请求B先返回了,那么请求A的响应就会覆盖请求B.但是B是后发送的,B才是我们想要拿到的响应.这个时候,我们就说触发请求A的这个副作用函数过期了
再Vue.js中,watch函数还能接收第三个参数onInvalidate
,这是一个函数,类似于事件监听器,我们可以用onInvalidate函数注册一个回调,这个回调函数会在当前副作用函数过期时执行
1 | watch(obj, async (newValue, oldValue, onInvalidate) => { |
那么onInvalidate
函数应该如何实现呢?再watch内部每次监测到变更的时候,在副作用函数重新执行之前,首先会调用onInvalidate
函数注册的过期回调.
1 | function watch(source, cb, options = {}) { |
之前有篇文章介绍过Vue3中watch函数的使用 Vue3中的watch函数
这样一来,当副作用函数第二次被触发之前,会调用onInvalidate
函数注册的过期回调,使得第一次执行的副作用函数内闭包的变量expired
变为true,从而阻止了赋值操作.
到此为止,响应系统的作用与实现——原始值的响应式方案这一大部分完美落幕! 完结散花
写于西13