随笔 - 175  文章 - 6  评论 - 0  阅读 - 36429

② 响应式系统的依赖收集追踪原理

1 为什么要依赖收集

1.1 栗子1

1. new 一个 Vue 实例
new Vue({
  template: `
    <div>
      <span>{{ text1 }}</span>
      <span>{{ text2 }}</span>
     </div>
  `,
  data: {
    text1: 'text1',
    text2: 'text2',
    text3: 'text3'
  }
})
2. 执行操作

this.text3 = "modify text3"

因为视图并不需要用到 text3,所以我们并不需要触发 cb 函数来更新视图

1.2 栗子2

1. 在多个 Vue 实例中用到同一个对象(全局对象)
let globalObj = {
  text1: 'text1'
};
let o1 = new Vue({
  template: `
    <div>
      <span>{{ text1 }}</span>
    </div>
  `,
  data: globalObj
});
let o2 = new Vue({
  template: `
    <div>
      <span>{{ text1 }}</span>
    </div>
  `,
  data: globalObj
});
2. 执行操作

globalObj.text1 = 'hello text1'

依赖收集会让 text1 这个数据知道需要通知 o1o2 两个 Vue 实例进行视图的更新

  • 形成数据与视图的一种对应关系

依赖收集是如何实现的?

2 订阅者 Dep

2.1 实现发布者 Dep

作用:用来存放 Watcher 观察者对象

  • addSub() 在目前的 Dep 对象中新增一个 watcher 的订阅操作
  • notify() 通知目前 Dep 对象的 subs 中的所有 watcher 对象触发更新操作

发布察订阅者模式

class Dep {
  constructor() {
    // 缓存列表--用来存放Watcher对象的数组
    this.subs = [];
  }
  // 在subs中添加一个watcher对象
  addSub(sub) {
    this.subs.push(sub);
  }
  // 通知所有watcher对象更新视图
  notify() {
    this.subs.forEach(sub => {
      sub.update();
    })
  }
}

2.2 订阅者 Watcher

class Watcher {
  constructor () {
    // 在new一个Watcher对象时将该对象赋值给Dep.target,在get中会用到
    Dep.targer = this;
  }
  // 更新视图的方法
  update () {
    console.log('视图更新啦~');
  }
}
Dep.target = null;

2.3 依赖收集

修改 defineReactive 以及 Vue 的构造函数

增加 Dep

用来收集 watcher 对象

  • 在对象被 时,会触发 getter() 把当前的 watcher 对象(存放在 Dep.target 中)收集到 Dep 类中去

  • 当该对象被 时,会触发 setter() ,通知 Dep 类调用 notify() 来触发所有 watcher 对象的 update 方法更新对应视图

function defineReactive (obj, key, val) {
  // Dep类对象
  const dep = new Dep();
  Object.defineProperty(obj, key, {
    enumberable: true,
    configurable: true,
    get() {
      // 将Dep.target(订阅者--当前watcher对象)存入发布者--dep的subs(缓存列表)中
      dep.addSubs(Dep.target);
      return val;
    },
    set(newVal) {
      if (newVal === val) return;
      // 在set时触发dep的notify来通知所有的watcher对象更新视图
      dep.notify();
    } 
  })
}

class Vue {
  constructor(options) {
    this._data = options.data;
    // 新建一个watcher观察者对象,此时Dep.target会指向这个watcher对象
    new Watcher();
    // 模仿render的过程,为了触发test属性的get函数
    console.log('render~', this._data.test)
  }
}

总结

  1. 在实例化的过程中会注册 get() 用来进行依赖收集
  2. 该闭包中会有一个 Dep 对象用来存放 watcher 实例对象

3 Object.defineProperty 的set/get方法所处理的事情:

依赖收集的过程就是把 watcher 实例存放到对应的 dep 对象中:

  1. get() 可以让当前的 watcher 实例存放到dep对象的 subs
  2. 数据变化时,set() 通过调用 dep 对象的 notify() 通知内部所有 watcher 对象进行视图更新

依赖收集的前提条件

  1. 触发 get 方法
  2. 新建一个 watcher 对象
posted on   pleaseAnswer  阅读(76)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示