前端开发最重要的 2 个⼯作:
- ⼀个是把数据渲染到⻚⾯
- 另⼀个是处理⽤户交互
1、在 Vue 的初始化阶段, _init ⽅法执⾏的时候, 会执⾏ initState(vm) ⽅法
1、初始化props
调⽤ defineReactive ⽅法把每个 prop 对应的值变成响应式。
⼀旦对象拥有了 getter 和 setter, 我们可以简单地把这个对象称为响应式对象。
defineReactive(props, key, value)
通过 proxy把 vm._props.xxx 的访问代理到 vm.xxx 上
proxy(vm, `_props`, key)
2、初始化data
1、对定义 data 函数返回对象的遍历, 通过 proxy把每⼀个值 vm._data.xxx 都代理到 vm.xxx 上。
while (i--) {
const key = keys[i]
。。。
proxy(vm, `_data`, key)
。。。
}
proxy的作⽤是把 props 和 data 上的属性代理到 vm 实例上
function proxy (target: Object, sourceKey: string, key: string) {
sharedPropertyDefinition.get = function proxyGetter () {
return this[sourceKey][key]
}
sharedPropertyDefinition.set = function proxySetter (val) {
this[sourceKey][key] = val
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}
- 第一个参数target是vm实例
- 第二个参数sourceKey是字符串
_data
(在执行proxy之前已经让vm._data 等于data函数返回的对象了) - 第三个参数key就是data中的key
2、调⽤ observe ⽅法观测整个 data的变化, 把 data 也变成响应式
observe(data, true /* asRootData */)
observe 的功能就是⽤来监测数据的变化
function observe (value: any, asRootData: ?boolean): Observer | void {
let ob: Observer | void
。。。
ob = new Observer(value)
。。。
return ob
}
先对传入的value做一些判断逻辑(为简化我把这些都删了)
最后传入data对象给new Observer,生成observer实例
1、Observer类
在Observer类的构造函数中:
1、生成了一个dep实例
this.dep = new Dep()
2、对 value 做判断(value 是vue实例中的data数据对象)
1、如果是数组
调⽤ observeArray ⽅法,observeArray 是遍历数组再次调⽤ observe ⽅法
2、如果是对象
对纯对象调⽤ walk ⽅法,
this.walk(value)
walk ⽅法是遍历对象的 key 调⽤ defineReactive ⽅法
walk ⽅法是遍历对象的 key 调⽤ defineReactive ⽅法
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
defineReactive 的功能就是定义⼀个响应式对象, 给对象动态添加 getter 和 setter,
在defineReactive 方法中:
- 1、为每个数据创建一个dep实例,它是用于watcher收集的,它有收集watcher、通知watcher的方法。
const dep = new Dep()
- 2、然后对⼦对象递归调⽤ observe ⽅法, 这样就保证了⽆论 obj 的结构多复杂, 它的所有⼦属性也能变成响应式的对象, 这样我们访问或修改 obj 中⼀个嵌套较深的属性, 也能触发 getter 和 setter。
- 3、Object.defineProperty 去给 每个属性值 添加 getter 和 setter
在get中依赖(watcher)收集
在set中派发更新
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get:
set:
}
3、依赖收集
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
if (Array.isArray(value)) {
dependArray(value)
}
}
}
return value
},
1、什么时候触发getter
当对数据对象的访问会触发他们的 getter ⽅法, 那么这些对象什么时候被访问呢?
执⾏ vm._render() ⽅法,这个⽅法会⽣成 渲染 VNode, 并且在这个过程中会对 vm 上的数据访问, 这个时候就触发了数据对象的 getter。
2、触发getter后执行dep.depend()
每个数据值的 getter 都持有⼀个 dep , 在触发 getter 的时候会调⽤ dep.depend() ⽅法, 也就会执⾏ Dep.target.addDep(this),Dep.target是当前的渲染watcher实例,把这个watcher实例放入了这个被使用的数据的dep数组中
4、派发更新
在触发setter时,会执行dep实例的notify方法;
set: function reactiveSetter (newVal) {
。。。
dep.notify()
}
notify方法遍历所有的 subs , 也就是 Watcher 的实例数组, 然后调⽤每⼀个watcher 实例的 update ⽅法
notify () {
const subs = this.subs.slice()
。。。
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
Vue 在做派发更新的时候, 它并不会每次数据改变都触发 watcher 的回调, ⽽是把这些 watcher 先添加到⼀个队列⾥, 然后在 nextTick 后执⾏。