vue 怎么实现的双向绑定?刚入坑的恐怕都能立马说出 defineProperty 这个东西,这是好事情,但如果对 Object.defineProperty 不熟的希望点右边链接再来看这篇文章。记得回来哦
话不多说直接上图 调用栈 stateMixin(vue)将_initState 挂到 vue 的原型链上,当 new Vue 的_init 会进行状态初始化。
- _proxy<fn> 给 vm 实例添加 get set 方法可直接修改查找 vm 实例上 example: vm.name 直接操作的是 vm._data.name
上面的判断是告诉你只有当前这个组件没有 props 的时候 说白了他不是一个 template 模版组件就会干这件事
2. 重头戏来了 observe 这才是老大哥。就在上图的最底下 虽然作者轻描淡写了 observe data ==
3.observe 中一进来就判断了当前需要监听的是否是对象,不是就无需监听了,如果是那么判断对象上是否有__ob__属性, 如果有直接返回__ob__的值(监听的对象上 observe 对象,可见 4),防止一个对象多次重复监听。
4. 如果这个对象没有被创建监听那么就走 Observe 这个方法,哎这个方法应该很眼熟,是不是在第 2 步提过他,其实那是假大哥, 但是那位假大哥的地位也是举足轻重的,每次创建对象的拦截监听都必须通过他的审核(见 3)。
6. 那么 Observe 做了啥,首先他接收整个 data,创建两个构造函数属性 value 和 dep,然后有个 def<fn> 在需要的监听对象上(value)添加了值为当前 this 到了__ob__不可枚举属性上,就是说这个对象从今天开始就是我的人了,我还给他盖了个__ob__章印。这一步其实也就改变了了构造属性上的 value,并且也同时修改了 vm 上的_data, 改变前后见图 2 和图三
图 1
图 2
图 3
7. 然后判断了这个对象是不是数组,如果是数组的话让每一项在去让 observe 假大哥去审核一遍,然后最终都会回到 this.walk<fn> 和 convert<fn> 这两位小弟上,这两位小哥就相当轻松了, 把需要拦截的对象,key value 拆分开分批次让他们的顶头上司 defineReactive<fn> 一对一处理
8. 先看下这位顶头上司尊容
一看才发现原来所有活都在这,领导也不是白当的。这位领导人也非常聪明进来就判断了对象 configurable 这个属性(图中红色字 2) 如果 false(不可修改不可删除),那么这位领导人觉得没必要监听对象上的这个属性,没人能动得了他,那还监听他的改变干哈,直接终止。反之,他就会帮这个属性创建拦截器啦。见下图最终的 Observe 的实例(图 4)以及 vm 上的_data(图 5)
图 4
图 5
9 接下来我在我的 new Vue 中写更加复杂的 data(图 6)最终的 observe(图 7)
图 6
图 7
发生了什么?多层嵌套的对象也有__ob__也有 get、set 拦截器,这时我们不妨仔细想想如果多层嵌套的对象只监听最外层的似乎是不合理的,内部改变外部监听不到,那么是怎么实现的呢,我们可以看下第 8 那个步骤,红色字 3 那段代码 `var childOb = observe(val)` 也就是说这位顶头上司把 this.walk<fn> 批次给到他的 val 在当作一个完整的需要监听的对象回到第三步骤中 进行新一轮的过滤,添加__ob__, 添加拦截器, 一直到这个 val 是一个不存在的东西或者不是一个对象,才会被终止。至此 data 是怎么被内部初始化的,怎么添加额外属性以及拦截器的。那么时候会触发拦截器的 get 必然是解析虚拟 dom 时发现有 data 的数据,set 怎么触发相对应 dom 的更新呢,必然是解析虚拟 dom 时将 dom 绑定跟数据一一绑定,形成对应关系。