关于响应式里面有几个重要的构造函数,他们各司其职,我会把他们主要的方法罗列出来,咱们慢慢考虑作者设计出他们的目的
1.Dep
我们需要给每个属性都增加一个 dep 收集器,目的就是收集 watcher。当响应式数据发生变化时,更新收集的所有 watcher
let id = 0
Dep.target = null
class Dep {
constructor() {
this.id = id++
// 存放watcher的数组
this.subs = []
}
// dep add watcher
addSub(watcher) {
this.subs.push(watcher)
}
// getter的入口函数,用于依赖收集
// watcher add dep
depand() {
if(!Dep.target) return
Dep.target.addDep(this)
}
}
2.Observer
class Observer {
constructor(value = {}) {
this.value = value
this.dep = new Dep()
this.vmCount = 0
Object.defineProperty(value, '__ob__', {
value: this
})
}
defineReactive$$1() {
let dep = new Dep()
Object.defineProperty(obj, key, {
get() {
if (Dep.target) {
dep.depand() // 收集依赖
if (childOb) {
childOb.dep.depend();
if (Array.isArray(value)) {
dependArray(value);
}
}
}
},
set() {
dep.notify() // 通知watch更新视图
}
})
}
}
3.Watcher
不同组件有不同的 watcher。这里只涉及渲染watcher。
function $mount(vm, el) {
vm.$el = el
const updateComponent = () => {
vm._update(vm._render())
}
// true用于标识是一个渲染watcher
const watcher = new Watcher(vm, updateComponent, ... , true)
}
class Watcher {
constructor() {
this.vm = vm
if (isRenderWatcher) {
vm._watcher = this
}
this.cb = cb
this.deps = [];
this.newDeps = [];
this.getter = expOrFn
// ...
this.get()
}
get() {
Dep.target = this
// 执行vm._update(vm._render(), hydrating) 并重新收集依赖
this.getter.call(vm, vm)
Dep.target = null
}
addDep(dep) {
let id = dep.id
// 去重,一个组件 可对应 多个属性 重复的属性不用再次记录
if (!this.depsId.has(id)) {
this.deps.push(dep)
this.depsId.add(id)
dep.addSub(this) // watcher已经收集了去重后的 dep,同时让 dep也收集 watcher
}
}
}
以上是我摘出来的简要代码,现在来讲解过程
整体上看,get是用来收集依赖,set用于更新视图!!!
1.关于Dep.target:这个是全局的,表示同一时间只有一个watcher在执行。渲染/更新完毕后我们会立即清空 Dep.target,保证了只有在模版渲染/更新阶段的取值操作才会进行依赖收集
2.当数据访问时,执行 dep.depend()
,通知 watcher 订阅 dep,然后在 watcher内部执行dep.addSub()
,通知 dep 收集 watcher
3.当数据变更时,执行dep.notify()
,通知所有的观察者 watcher 进行 update 更新操作
未完更新...