入口文件
当执行new Vue时会进入instance\init.js的_init()初始化方法,执行到initState,再到initData
initData会调用proxy(vm, _data, key)把data的key挂载到Vue实例上面
再调用observe(data, true /* asRootData */)做响应式处理
响应式的处理在 src\core\observer\index.js
开始流程 (只列举部分代码)
响应式
observe(value, asRootData)
- 判断传入的value是不是对象
- 是否已经做过响应式处理
- 没有就为这个对象创建一个响应式属性
function observe(value, asRootData) {
// 接收一个value对象 asRootData是表示为根数据
// 判断value是否是对象或者是Vnode实例 是就return
if (!isObject(value) || value instanceof VNode) {
// 判断value有没有__ob__ 属性
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer)
// 否则是不是对象或者数组
else if (Array.isArray(value) || isPlainObject(value)) {
// 创建一个Observer对象
ob = new Observer(value)
}
}
// 返回observer对象
return ob
}
Observer constructor
为当前对象value创建一个依赖收集 Dep
把当前Observer实例挂载到value的__ob__
判断value是数组还是对象 分别处理
数组 就把数组的原生方法重新实现,增加数据改变后,发布通知响应式处理
对象 就进行遍历为每个属性进行响应式处理
constructor(value) {
// 收集依赖
this.dep = new Dep()
// 挂载实例
def(value, '__ob__', this)
// 判断是不是数组
if (Array.isArray(value))
// 数组响应式处理 ...
// 对象就调用walk遍历每个属性 转换成getter和setter
this.walk(value)
}
walk中Object.keys()每个属性调用defineReactive(obj, keys[i])
defineReactive(obj, keys[i])
- 为属性创建Dep收集依赖
- 判断是否需要递归属性的子对象
- 定义属性的getter和setter
- getter
- 判断有没有target,有就收集依赖
- 再判断有没有子对象,有也为子对象收集依赖
- 如果子对象是数组就遍历数组元素收集依赖
- setter
- 判断值是否发生改变
- 有改变再判断新值是不是对象
- 是对象就为这个对象做响应式处理
- 为当前属性发布通知更新
defineReactive(obj, key) {
// 创建属性dep
const dep = new Dep()
val = obj[key]
// 判断需不需递归子对象 observe只会处理对象
// 即使shallow为true 如果val不是对象 会在observe中返回false
let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
get: function() {
// 如果有target (Watcher)
if (Dep.target) {
dep.depend()
// 有子对象有__ob__ 也收集依赖
if (childOb) {
// 当子对象notify时会通知当这个父属性的Watcher
childOb.dep.depend()
if (Array.isArray(value)) {
// 遍历收集
dependArray(value)
}
}
}
},
set: function(newVal) {
// 如果新值是对象也会进行响应式处理
childOb = !shallow && observe(newVal)
// 发布通知
dep.notify()
}
})
}
响应式-收集依赖
src\core\observer\watcher.js是new Watcher创建的文件,$mount执行中会创建一个渲染Watcher,并且在_render生成Vnode时会完成依赖收集
这个过程中Watcher和Dep都会把对方记录下来
在Watcher的记录时,会先判断当前dep是否已经收集过了,避免重复收集
Watcher constructor中会调用get(),再调用pushTarget(this)
function pushTarget (target) {
// 把当前Watcher实例放到Dep.target
Dep.target = target
}
生成节点中访问到模板中的属性,触发属性对应的getter
// 因为pushTarget已经把Watcher放到Dep.target
dep.depend()
depend() {
// 调用Watcher的addDep()
Dep.target.addDep(this)
}
addDep (dep) {
const id = dep.id
// this.newDepIds会记录当前dep是否已经收集了
// 避免重复收集依赖
if (!this.newDepIds.has(id)) {
// Watcher记录了Dep
this.newDepIds.add(id)
this.newDeps.push(dep)
if (!this.depIds.has(id)) {
// 再调用回dep收集
dep.addSub(this)
}
}
}
// 添加新的订阅者 watcher 对象
addSub (sub) {
this.subs.push(sub)
}