🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## 变化侦测 在 vue2 中,状态绑定的依赖不再是一个具体的dom节点,而是一个组件,当状态发生改变时,通知到组件,组件内部再去用虚拟 DOM 进行比对,找出需要更新的地方。 ![](https://img.kancloud.cn/87/08/8708bec088170a04a4807a4c0d1f432a_1287x876.png) ## 如何收集依赖 && 依赖收集在哪里 ``` function defineReactive (data, key, val) { let dep = [] Object.defineProperties(data, key, { enumerable: true, configurable: true, get () { dep.push(window.target) return val }, set (newVal) { if (val === newVal) { return } dep.forEach((d, i) => d(val, newVal)) val = newVal } }) } ``` ``` class Dep { constructor () { this.subs = [] } addSub (sub: Watcher) { this.subs.push(sub) } removeSub (sub: Watcher) { remove(this.subs, sub) } } ``` 总结: * 通过 Object.defineProperty 中的 get 收集依赖,set 触发依赖 * 依赖收集在 Dep 类中 ## Watcher Watcher 是一个中间商,当数据变化时,通知Watcher,由 Watcher 再通知其他地方 ![](https://img.kancloud.cn/b5/7e/b57e0e767b4878235e65dd5208218c03_1197x393.png) ## Data、Observer、Dep 和 Watcher的关系 ![](https://img.kancloud.cn/dd/d2/ddd2ae6a70060f5a55a0eb4ccf6b0b9a_1298x666.png) 总结: * object 通过 object.defineProperty 将属性转换成 getter 和 setter 的形式来追踪变化 * 依赖收集在getter (只有第一次触发getter时,才会收集依赖),依赖触发在setter ``` addDep (dep: Dep) { const id = dep.id if (!this.newDepIds.has(id)) { this.newDepIds.add(id) // this.newDeps.push(dep) if (!this.depIds.has(id)) { dep.addSub(this) } } } ``` * 对依赖的删除,收集都在 Dep中 * 依赖其实就是 Watcher ,只有 Watcher 触发的 getter,才会收集依赖, Watcher是被收集到 Dep 中的