Vue实例
Vue会把data中的数据成员注入到Vue的实例中去,并转成getter/setter访问器属性。 这样的目的是: 1.我们能够通过 this.xxx(this.属性名)的方式进行调用。 2.通过getter/setter进行数据劫持,实现数据监听。
data中的成员都被存放到了$data中,并且全部转换成访问器属性,即getter/setter,$data中的setter是真正监视数据变化的地方。
Vue的实例中还有一个_data,它与$data指向的是同一个对象(地址相同),只不过_data表示私有成员,而$data是公有成员。
Vue实例中的$options用来存放创建Vue实例时传递进来的参数(选项)。
Vue响应式原理
这里说一说最小模式的响应式原理。
- Vue类,负责接收初始化的参数(选项)。
- 然后会把data中的数据成员注入Vue的实例,并转成getter/setter。
- Vue类的内部会调用Observer、Compiler
- Observer对数据进行劫持,能够对data中的属性进行监听,如果数据发生变化,会获取到最新的值,并通知Dep。
- Compiler会对每个元素进行解析,即解析每个元素中的指令(v-xxx)以及差值表达式({ xxx }),并替换成响应的数据。
- Watcher就是观察者模式中的观察者又叫订阅者。它的内部有一个update方法,负责更新视图。
- Dep就是观察者模式中的目标又叫发布者。负责添加观察者,当数据发生变化,通知所有的观察者调用update方法更新视图。
_proxyData
在Vue类的内部,有个一个_proxyData的私有方法,负责把data中的数据成员,转成getter/setter并注入到Vue的实例中去,实现数据监听也方便后续使用(可以通过this.属性名的方式调用data中的数据)。
戳 → _proxyData
Observer
Observer类中有两个核心方法walk、definReactive。 walk 遍历data中的属性,并调用definReactive把数据转为getter/setter – 响应式。
当访问data中的数据时,即通过this.属性名获取/使用数据时,首先访问Vue实例(后统一叫做vm)中的get,因为Vue首先将data中的数据都转成了getter/setter并注入vm中。vm的get中通过data[属性名]来返回数据,而在Observer中将data中的数据转成了getter/setter存放到vm的$data中,所以会调用$data中的get。
即, vm.属性名 → vm中的get → $data中的get vm.属性名 = xxx → vm中的set → $data中的set → Observer中,在set发送数据变化通知
戳 → Observer
Compiler
Compiler类的作用用一句话总结就是在操作DOM。
核心功能:
- 编译模板,处理差值表达式、指令。递归遍历
vm.el 中的所有子节点,判断:
- 如果是文本节点,则处理差值表达式。
- 如果是元素节点,则处理指令。
戳 → Compiler
Dep
Dep主要负责监听数据变化。Dep会在Observer的$data的get方法中收集依赖,在set方法中触发依赖(发送通知)。
Vue会为每一个响应式数据($data中的每个属性)创建一个Dep对象,在使用数据时收集依赖(即创建观察者对象 Watcher),在更新数据时通知所有(依赖数据的)观察者更新视图。
戳 → Dep
Watcher
Watcher主要负责更新视图,其次在自身实例化的时候会把自身实例添加到Dep类的target静态属性中。
Watcher中可以拿到oldVaule、newValue,以及callback。更新过程中去比较新、旧的值是否发生变化,如果发生变化,则调用callback并将newValue作为参数传递过去。这里的callback(回调)才是真正执行更新视图的方法。
所以,需要在所有依赖数据的地方创建Watcher对象,并传递一个更新视图数据的回调函数。当数据发生改变的时候,Dep会调用notify方法通知所有Watcher对象更新视图,其实就是通知Watcher执行回调。
戳 → Watcher
双向绑定
前面一系列操作~~都是数据驱动视图更新,那么,对于表单元素,当视图发生变化也应该更新数据。
拿input 输入框举例。
当输入框中的值被修改,此时可以拿到被修改后的值,也就是input 的value 。然后再将value 重新赋值给vm中的数据,即vm.xxx = input.value 。
戳 → 双向绑定
|