Vue响应式原理三:响应式依赖收集-类
前提:实际开发中,响应式对象肯定是不止一个对象的, 使用数组reactiveFns, 所有依赖的函数都放到数组里面,是满足不了多个响应式对象,使用类单独来管理一个对象所有的依赖
1. 响应式依赖收集-类
- 1.1. 类结构:创建Depend类来管理响应式依赖,包含reactiveFns数组存储依赖函数
- 1.2. 核心方法:
- addDepend(fn):添加依赖函数到数组,会先判断fn是否存在
- notify():遍历执行所有收集的依赖函数
- 代码如下:
// 使用类单独来管理一个对象所有的依赖class Depend {constructor() {this.reactiveFns = [];}addDepend (fn) {if(fn) {// 将依赖函数添加到数组,this.reactiveFns.push(fn);}}notify () {this.reactiveFns.forEach(fn => {// 遍历执行所有收集的依赖函数fn()})}}
- 1.3. 优势: 相比单一数组管理,可以针对不同对象创建独立的Depend实例,避免所有依赖混在一起
- 1.4. 执行过程
- 创建Depend实例:const dep = new Depend()
- 收集依赖:通过watchFn函数调用dep.addDepend(fn)
- 触发更新:修改属性后手动调用dep.notify()
- 示例代码如下:
// 创建Depend实例const dep = new Depend()function watchFn (fn) {dep.addDepend(fn)// 传入函数后立即执行一次,类似watchEffect()fn()}// 响应式函数watchFn(function foo () {console.log('foo: ', obj.name);console.log('foo: ', obj.age);console.log('foo function');})watchFn(function bar () {console.log('bar: ', obj.name + ' hello');console.log('bar: ', obj.age + 10);console.log('bar function');})// 修改obj的属性obj.name = 'kobe'// 当依赖发生变化时,会执行对应的响应式函数dep.notify()```
- 1.5. 当前问题:
- 每次属性修改后需要手动调用
dep.notify()
- 容易遗漏导致依赖不更新
- 多个属性变更时需要多次调用
// 参考上面的代码...// 修改obj的属性console.log('name发生变化时----------------------------------------');obj.name = 'kobe'// 当依赖发生变化时,会执行对应的响应式函数dep.notify()// 再次修改obj的属性obj.name = 'james'// 每次手动通知,一旦忘记就不是响应式了// dep.notify()
-
- 完整代码如下:
// 使用类单独来管理一个对象所有的依赖class Depend {constructor() {this.reactiveFns = [];}addDepend (fn) {if(fn) {// 将依赖函数添加到数组,this.reactiveFns.push(fn);}}notify () {this.reactiveFns.forEach(fn => {// 遍历执行所有收集的依赖函数fn()})}}// 实际开发中,响应式对象肯定是不止一个对象的// 例如: // const user = { nickName: 'kobe', level: 100 }// const product = { name: '电脑', price: 1000 }// 有一个弊端,只有一个数组reactiveFns, 所有依赖的函数都放到数组里面,是满足不了多个响应式对象// 例如:一旦修改了product.name所有的响应式函数都会执行,目标是只想要执行product.name的响应式函数const obj = {name: 'why',age: 18}// 当上面有很多依赖函数时,很难收集// 设置一个专门执行响应式函数的一个函数const dep = new Depend()// obj => new Depend()// user => new Depend()// product => new Depend()function watchFn (fn) {dep.addDepend(fn)// 传入函数后立即执行一次,类似watchEffect()fn()}watchFn(function foo () {console.log('foo: ', obj.name);console.log('foo: ', obj.age);console.log('foo function');})watchFn(function bar () {console.log('bar: ', obj.name + ' hello');console.log('bar: ', obj.age + 10);console.log('bar function');})// 修改obj的属性console.log('name发生变化时----------------------------------------');obj.name = 'kobe'// 当依赖发生变化时,会执行对应的响应式函数dep.notify()// 修改obj的属性console.log('age发生变化时----------------------------------------');obj.age = 20dep.notify()console.log('name发生变化时----------------------------------------');obj.name = 'james'// 每次手动通知,一旦忘记就不是响应式了// dep.notify()```
- 1.7. 后续改进:
- 将展示如何自动创建依赖收集器
- 实现属性变更自动通知机制