Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue的响应原理:Observe、Watcher、Dep关系 #4

Open
terribleness opened this issue Mar 9, 2018 · 0 comments
Open

Vue的响应原理:Observe、Watcher、Dep关系 #4

terribleness opened this issue Mar 9, 2018 · 0 comments

Comments

@terribleness
Copy link
Owner

前面两篇讲述了Vue运行时---第一步扩展实例方法(点击链接)Vue运行时---第二步扩展静态方法(点击链接),今天第三节解读## Vue中双向数据绑定的原理。
我们先回到’instance/index’文件中,如下图

image

我们第一节已经说到 initMixin(Vue)只做了一件事,就是给Vue扩展了实例方法_init

image

接下来我们进入Vue比较重要的环节,比如我们举一个例子,
image

定义了data和template,我们这节就要搞清楚data值变化和template生成的html是如何相应变化的。如图一,new Vue之后,进入function Vue构造函数,执行this._init(options),这节其实也就是主要要搞清楚_init 方法,我们现在看到图二中_init 方法,

image

1、 我们先看到第一个红框处,mergeOptions是方法是用来合并options参数的,第一句resolveConstructorOptios(vm.constructor)将得到第二节中说道buildInComponents默认参数,并用传入的options取代默认参数。
2、 initLifecycle(vm)
我们找到’instance/lifeCycle’文件如下:

image

我们可以看到是初始化工作,第一句options.parent在buildInComponent中没有,如果自己传递了那么vm.$parent就是你传递额parent参数。如果没有传递那么vm.$parent就是null而且vm.$vm就是vm本身。接着又设置vm.$refs ={},vm._watch = null,vm._inactive = null,vm._directInactive = false,vm._isMounted = false,vm._isDestroyed = false,vm._isBeingDestroyed = false。
3、 initEvents(vm)、initRender(vm)、initInjections(vm)我就不一一写了,都是初始化,我们主要说initState(vm)
4、 initState(vm)这里面就是定义了Observe、Watcher、Dep的关系,进入看到initState(vm)函数内容

image

我主要讲解initData(vm)这个方法,其他initiativeProps、initMethods、iniComputed、initWatch后续再说。进入initData(vm)函数,如下图

image

看到observe(data,true)这一句,这里的data还是之前的的
image

进入observe方法后,如下图

image

这里出现了new observer(value)其实就是要把data传入了new Observer中。进入Observer中的构造函数,如下图

image

把data数据又传入了this.walk方法中,如下图

image

这里data对象{a:1,b:2}被取出a、b属性和各自值分别执行了

image

进入defineDeactive方法后,如下图出现最重要部分了。这也是为什么Vue不支持IE8的原因了,因为IE8中不支持Object.defineProperty

image

1、 先实例化 var dep = new Dep();这里使用了闭包,因为同一个data属性会对应到html中该data属性。
2、 取得data[‘a’]的属性描述,判断该属性是否能配置。这里先插入一句对象属性的描述规则,比如person对象的name属性是这样定义的。如下:

image

3、 接着取出原声getter和setter方法,我们先看第二个红框set方法当我们写{a:1}的时候,会自动自定元素的set方法,其中set方法的过程为:
一、 取出现有值,比较现有值和跟新值是否一样,
二、 先调用setter.call(obj.newVal)将obj的属性a修改为值1
三、 如果新值是一个负责类型递归执行observe(newVal)
四、 调用dep.notify方法,订阅-发布模型,通知所有元素更新操作
4、 进入dep.notify内容,看定于-发布模型的具体内容如下:

image

取出属性a对应的所有Watcher对象数组,循环所有对象挨个出发update函数,其实subs是一个Watcher实例数组。我们可以看Dep类的内容。

image

现在我们讲述了set方法的内容以及触发订阅者更新。那么subs的watcher监听又是哪里来的呢,这就要回到上上上图的get方法了,我们新截一张get方法图片如下,方便说明

image

说明get前,我们先要简单说明一下template编译为javascript函数的结果,我们回到举例
image

这里的template是一个字符串,会编译为javascript函数,过程我们后面会用一节来讲解,我们就只说这个例子中template编译后的结果。如下:

image

如果这就是将template字符串编译为javascript函数的过程,如果你对_c、_v、_s函数有疑问,你可以回忆第一节扩展实例方法的最后一个图renderMixin中添加的实例方法。
好了,现在tempate已经转换为javascript函数了,转换过程我以后章节再详细说明,第一句with(this)就是with(vm),那么其中的a、b、a出现就是wm.a和wm.b和wm.a了,这样就会触发data函数中的a、b属性的get方法了,当然这里是做了代理的,wm.a等同于wm.data.a,这样我们编译模版后,就会自动触发属性的get方法,我们再回到get方法中如下:
image

比如第一个wm.a进入get方法后,dep.depend方法执行,如下

image

这个this就是dep实例,而Dep.target是watcher,所以每次执行vm.a都会将一个dep实例压入watcher中,即订阅目的,而在修改wm.a值时,又会循环所有watcher通知所有元素更新,即发布目的。这就是Watcher、Dep的关系。

总结:

1、 每个data中属性元素会有一个闭包Dep的实例,里面存放Watcher实例
2、 当template编译为render函数时,会调用属性的get方法,将每一次调用都生成一个watcher实例,并压入dep中,产生订阅效果。
3、 当设置data中属性值的时候,调用属性的set方法通知所有订阅该属性的元素更新
4、 而且props,computed,watch内同理是增加watcher产生订阅和发布的效果。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant