② 响应式系统的依赖收集追踪原理
目录
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
这个数据知道需要通知 o1
和 o2
两个 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)
}
}
总结
- 在实例化的过程中会注册
get()
用来进行依赖收集 - 该闭包中会有一个
Dep
对象用来存放watcher
实例对象
3 Object.defineProperty 的set/get方法所处理的事情:
依赖收集的过程就是把 watcher
实例存放到对应的 dep
对象中:
get()
可以让当前的watcher
实例存放到dep对象的subs
中- 数据变化时,
set()
通过调用dep
对象的notify()
通知内部所有watcher
对象进行视图更新
依赖收集的前提条件
- 触发
get
方法 - 新建一个
watcher
对象
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)