详细解释Vue源代码vm的内部原理 $watch()
虚拟机的详细用法见官网。$watch()。
一般用法如下:
script const app=new Vue({ El : ' # app },data : { a : { b : { c : ' c ' } } },Mounted () {this。$ watch(function(){返回这个。a.b.c},这个。handle,{deep: true,immediate : true//默认值将初始化并执行该句柄一次})},methods: { handle (newVal,old val){ console . log(this . a)console . log(new val,oldVal) },change value(){ this . a . b . c=' change ' } })/script
可以看到数据属性的整个一个对象都是Observed,只要是Observed,就会有一个__ob__标记(即observe instance),可以看到__ob__中有dep,前面提到dep存在于observe instance中,subs存储对应属性的依赖关系。好,回到正文,如果vm。$watch()在源代码中实现。
内部实施原则
//判断是否是对象导出函数isplayanobject(obj : any): boolean { return _ tostring . call(obj)='[object object]' }源代码位置: vue/src/core/instance/state . js。
//$watch方法允许我们观察数据对象的属性,并在属性改变时执行回调。//接受三个参数: expOrFn(要观察的属性)、cb、options(可选配置对象)//cb可以是回调函数,也可以是纯对象(这个对象应该包含handle属性。)//options: {deep,immediate},deep指深度观察,immediate立即执行返回//$watch()的本质或创建Watcher实例对象。vue . prototype . $ watch=Function(exporff n : string | Function,cb:有选项吗?对象):函数{//vm指向当前Vue实例对象Const VM : Component=This If(isplayanobject(cb)){//如果CB是纯对象返回createWatcher(vm,ExpOrFn,CB,options)}//get options options=options | | { }//set user : true,表示这是用户自己创建的。Options.user=true //创建一个Watcher实例const watcher=new watcher (VM,exporfn,CB,options) if (options。立即){//如果立即为真,立即执行回调。尝试{//此时只有新值,没有旧值。你可以在上面的截图中看到未定义。//至于为什么这个新值会传递watcher.value,请参见代码cb.call (VM,watcher。value)} catch(错误){handleerror(错误,VM,`立即监视程序的回调' $ {监视程序。expression }“`) } }//返回一个函数,这个函数的执行将释放当前观察者对属性的观察。返回函数unwatchFn () {//执行distandown(). watcher . distandown()} }关于watcher.js
源代码路径: Vue/src/core/observer/watcher . js
导出默认类Watcher { vm: Component表达式:字符串;cb:功能;id:号码;deep:布尔值;user:布尔值;lazy:布尔型;sync:布尔值;dirty:布尔值;active:布尔值;deps: ArrayDepnewDeps: ArrayDep描述:简单集;newDepIds: SimpleSet:年之前?功能;getter:函数;value: any构造函数(vm:component,//组件实例对象expOrFn: string | Function,//要观察的表达式cb3360function,//观察到的表达式值变化时要执行的回调选项?对象,//选项为当前观察者对象的isRenderWatcher? boolean //标识观察者实例是否是呈现函数的观察者。{//每个观察者实例对象都有一个vm实例属性。该属性指示该观察者属于哪个组件。VM=VM if(isrenderwatcher){//此参数仅在mountComponent函数中创建渲染函数观察器时为真//组件实例的_watcher属性的值引用组件的渲染函数观察器VM。_ watcher=this} VM。_观察者。push (this)//options//deep:当前观察器实例对象是否是深度观察//通常,在使用Vue或vm的watch选项时。$watch函数来观察某些数据,//可以将deep选项的值设置为true来深入观察数据。//user:用于标识当前观察者实例对象是开发人员定义的还是内部定义的。//无论是Vue的watch选项还是vm。$watch函数,它们都是通过实例化Watcher类实现的。//sync:告诉Watcher在数据发生变化时是否同步求值并执行回调。//before3360可以理解为观察器实例的钩子。数据更改后,触发更新前,//调用创建渲染函数的观察器实例对象时传递的before选项。if (options) { this.deep=!options.deep this.user=!options.user this.lazy=!options.lazy this.sync=!options . sync this . before=options . before } else { this . deep=this . user=this . lazy=this . sync=false }//Cb :回调this . CB=cbthis . id=uid//uid for batting this . active=true//避免收集重复的依赖项。并去掉无用的依赖项this . dirty=this.lazy//for懒人观察者this . deps=[]this . newdeps=[]this . desids=newset()this . newdesids=newset()this . expression=process . env . node _ env!==“生产”?expOrFn.toString() : '' //检测expOrFn的类型。//this.getter函数最终将是一个函数,如果(export fn===' function '){ this。getter=exporfn } else { this。getter=parsepath (exporfn) if(!this . getter){ this . getter=noop process . ENV . NODE _ ENV!=='production' warn('未能监视路径: '${expOrFn}' ` 'Watcher只接受简单的点分隔路径若要完全控制,请改用函数,vm)}} //评估this.value=this.lazy?undefined : this . get()}/* * * evaluation : collection dependency * evaluation有两个目的*第一个是可以触发访问器属性的get interceptor函数*第二个是获取观察到的目标的值*/get () {//将当前Watcher实例推送到dep.target push target (this) let值//缓存vm const vm=this.vm try {//获取值=this.getter.call(vm,vm)} catch(e){ if(this . user){ handleError(e,VM, ' getter for watcher ' $ { this . expression } ' `)} else { throw e } }最后{ //'touch '每个属性,这样它们都被跟踪为//Dependencies用于深度监视if (this。 deep){//递归读取被观察属性//的所有子属性的值,这样被观察属性的所有子属性都会被收集到观察者那里,从而达到深度观察的目的。
traverse(value)} popTarget()此。cleanupdeps()}返回值} /** *记录自己都订阅过哪些DeP */AdDeP(DeP : DeP){ const id=DeP。id//NewDepd :避免在一次求值的过程中收集重复的依赖if(!这个。新部门。has(id)){ this。新部门。add(id)//记录当前看订阅这个dep this.newDeps.push(dep) //记录自己订阅了哪些dep if(!这个。DEPDs。has(id)){//把自己订阅到dep.addSub(this) } } } /** *清理以进行依赖项收集*/cleanupDeps(){让我=这个。德普斯。长度while(I-){ const dep=这个。德普斯[我]如果(!这个。新部门。has(dep。id)){ dep。removeSub(this)}//NewDepds属性和新部门属性被清空//并且在被清空之前把值分别赋给了描述属性和依赖的列表属性//这两个属性将会用在下一次求值时避免依赖的重复收集。
让tmp=这个。德普斯。depds=这个。纽德普斯。newdepds=tmp这个。纽德普斯。clear()tmp=this。德普斯这个。德普斯=这个。纽德普斯这个。newdeps=tmp这个。纽德普斯。长度=0 }/* * *订阅者接口。*当依赖关系改变时将被调用*/update () { /*伊斯坦布尔忽略else */if(这个。懒){这个。dirty=true } else if(this。sync){//指定同步更新this.run() } else { //异步更新队列queueWatcher(this) } } /** *调度程序作业接口。*将由调度程序调用*/run(){ if(this。active){ const value=this。get()//对比新值价值和旧值这个值是否相等//是对象的话即使值不变(引用不变)也需要执行回调//深度观测也要执行如果(值!==this.value || //即使//值相同深度观察者和对象/数组上的观察者也应该激发,因为该值可能//已经发生了变化isObject(value) || this.deep ) { //设置新值常量旧值=这个。重视这一点。值=值if(this。用户){//意味着这个观察者是开发者定义的,所谓开发者定义的是指那些通过看选项或$手表函数定义的观察者尝试{ this.cb.call(this.vm,value,oldValue) } catch (e) { //回调函数在执行的过程中其行为是不可预知,出现错误给出提示handleError(e,this.vm,'对观察器${this.expression}“`”)的回调)} } else { this.cb.call(this.vm,value,old value)} } }/* * *计算观察器的值。*这只适用于懒惰的观察者*/evaluate(){ this。值=这个。get().*/depend(){让我=这个。德普斯。长度而(我-){这个。德普斯[我].depend() } } /** *把看守人实例从从当前正在观测的状态的依赖列表中移除*/down(){ if(this。活动){//该观察者是否激活状态if(!这个。虚拟机。_ isbeingdestore){//_ isbeingdestore ed一个标识,为真说明该组件实例已经被销毁了,为假说明该组件还没有被销毁//将当前观察者实例从组件实例对象的虚拟机。_观察者数组中移除移除(this.vm._watchers,this) } //当一个属性与一个观察者建立联系之后,属性的资料执行防止实例对象会收集到该观察者对象让我=这个。德普斯。长度而(我-){这个。德普斯[我].removeSub(this) } //非激活状态这个。active=false } } } export const unicoderegexp=/a-za-z \ u00 B7 \ u00 c0-\ u00 D6 \ u00 D8-\ u00 F6 \ u00 F8-\ u 037d \ u 037 f-\ u1fff \ u200 c-\ u200 d \ u203 f-\ u 2040 \ u 2070-\ u 218 f \ U2 c00-\ U2 fef \ u 3001-\ u路径为密钥路径(属性路径)处理' a.b.c '(即虚拟机。a . b . c)=a[b[c]]导出函数解析路径(路径:字符串): any { if(BaiRe。test(path)){ return } const segments=path。拆分(' .)返回函数{ for(让I=0;长度;i ) { if(!obj)返回obj=obj[段[i]] }返回obj }}以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。
版权声明:详细解释Vue源代码vm的内部原理 $watch()是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。