前提条件
vue3版本 vue v3.4.30
代码地址: https://cdn.bootcdn.net/ajax/libs/vue/3.4.30/vue.global.js
watch实现原理分析
testComponet组件定义
在组件中定义了一个watch
var testComponet={
name:"test-component",
props: {
testpmsg1: String,
onPutinfo:Function,
staticprop: String,
bindproptest: String
},
data:function(){
return {
cmsg:"shengbinqian",
cdata:"zhongguoren",
url:"http://www.baidu.com"
}
},
watch: {
cmsg:function(newValue,oldValue){
console.log("cmsg changed: ",oldValue,newValue);
}
},
methods: {
testdata1:function(str){
console.log("testdata-"+str);
this.$emit("chanage","你好我是子组件信息chanage");
},
testdata2:function(str){
this.$emit("putinfo","你好我是子组件信息putinfo");
}
},
template:`<div class='border:solid 1px red'>
<div>我是子组件testComponent---{
{cmsg}}----bindproptest: {
{bindproptest}}</div>
<div><slot name="other" :url="url"></slot></div>
<div><button @click="testdata1" >chanageEmit</button></div>
<div><button @click="testdata2" >putinfoEmit</button></div>
</div>`,
setup:function(props,ctx){
let data={tname:"qianqian"};
//如果父组件的模版定义中,设置了子组件的ref属性,那么在父组件中就可以获取到这个子组件的实例代理对象
//但是如果子组件要想暴露它想暴露的数据,那么就可以适用expose()方法,如果设置了这个方法,那么父组件就只能访问这些expose的数据
//起到一个屏蔽的作用
//ctx.expose(data);
console.log("testComponet-setup");
}
};
在testComponet组件定义对象中定义了一个 watch,用于侦听cmsg数据。watch要实现的效果就是当cmsg变化时,会触发下面这个函数的执行。
cmsg:function(newValue,oldValue){
console.log("cmsg changed: ",oldValue,newValue);
}
更细致的说就是 cmsg响应式数据的deps(用于保存响应数据所有的副作用对象ReactiveEffect) 收集了一个副作用对象 ReactiveEffect ,然后这个对象(ReactiveEffect )的scheduler 属性封装了上面那个响应函数(cmsg:function(newValue,oldValue)),那么有个问题是? 这个依赖关系是在什么时候收集的呢? 要收集依赖,肯定是要 触发cmsg 响应式数据的 get方法,然后这个代理方法就会有收集依赖的代码。
分析:
由于组件定义对象的watch: {} 会在applyOptions方法中进行解析,在这个方法中会触发createrWatcher方法,然后在这个方法中会把这个cmsg的key 封装成一个getter方法,getter方法的内容就是()=>publicThis[key(cmsg)],这个publicThis就是intance.proxy对象,是一个代理,在getter拦截方法中会收集依赖,那么就要在合适的时候触发这个方法(getter)执行就可以收集依赖(就是收集副作用对象ReactiveEffect),同时把上面定义的响应函数封装到ReactiveEffect对象中的scheduler 属性中就可以,同时ReactiveEffect 创建是把fn属性设置为getter方法,然后vue3会在doWatch方法中主动触发一次ReactiveEffect.run()方法,在这个run方法中就会执行fn,也就是getter方法,那么这样就把依赖收集成功了。
//applyOptions中关键代码
if (watchOptions) {
///侦听器 也是
for (const key in watchOptions) {
//循环处理每个定义的watch数据
createWatcher(watchOptions[key], ctx, publicThis, key);
}
}