网络上的相关文章又干又长,大多数时候只想知道vue2响应式的运转框架,不关注太多细节的东西,所以自己总结了这篇。
- 三个步骤
- 数据劫持
- 依赖收集
- 派发更新
- 四个方法
- observer
- Compiler
- watcher
- Dep
具体执行:
- 在Observer中,对vue的data中的所有数据遍历递归,进行数据劫持:getter,setter
- 在Compiler中,找到html中用到了变量的地方
eg:<p>{{ name }}</p>
, 对这个变量进行new Watcher(p标签, name, vm)
(就是一指定元素的副作用函数),该Watcher()
就是属性name
依赖之一,页面有很多用到name属性的地方,也就是说name
会有很有watcher
,标记依赖之后 - Dep进行依赖收集,如何收集?data中的属性哪些在页面中使用了,哪些就收集起来,所以在Observer的
getter
能知道哪里使用了,getter
它会通知Dep
进行watcher
的收集,并且Dep它有一个notify()
函数,该函数会在Observer的setter中调用,当某个data的某属性改变了,调用setter,触发notify(),notify()会拿着该属性,通知该属性的所有watcher(),watcher() 中的update()方法会告诉下游方法更新页面
init
function defineReactive(obj: Object, key: string, ...) {
const dep = new Dep()
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
....
dep.depend()
return value
....
},
set: function reactiveSetter (newVal) {
...
val = newVal
dep.notify()
...
}
})
}
class Dep {
static target: ?Watcher;
subs: Array<Watcher>;
depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
}
notify () {
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
mount
mountComponent(vm: Component, el: ?Element, ...) {
new Watcher(vm, updateComponent, ...)
}
class Watcher {
getter: Function;
// 代码经过简化
constructor(vm: Component, expOrFn: string | Function, ...) {
...
this.getter = expOrFn
Dep.target = this
this.value = this.getter.call(vm, vm) // 调用组件的更新函数, 在这一步收集到了依赖
...
}
}
更新
notify () {
const subs = this.subs.slice()
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}