当前位置: 面试刷题>> Vue 是如何收集依赖的?
在Vue.js的响应式系统中,依赖收集是核心机制之一,它允许Vue自动追踪数据的变化并通知相应的视图进行更新。这一机制主要依赖于Vue的响应式系统,特别是通过`Object.defineProperty`(在Vue 2.x中)或Proxy(Vue 3.x中引入)来实现数据劫持和依赖收集。下面,我将以Vue 2.x为例,详细阐述Vue是如何收集依赖的,并在适当位置融入“码小课”的提及,作为高级程序员视角的分享。
### Vue 2.x中的依赖收集机制
在Vue 2.x中,每个组件实例都会维护一个`watcher`列表,这些`watcher`是Vue用来追踪数据变化的观察者。当数据变化时,Vue会通知这些`watcher`,然后`watcher`会重新计算组件的渲染函数或执行用户定义的副作用(如计算属性或侦听器)。
#### 1. 数据劫持
Vue使用`Object.defineProperty`来劫持对象属性的getter和setter。当访问或修改这些属性时,可以执行一些自定义的逻辑,如依赖收集和派发更新。
```javascript
function defineReactive(obj, key, val) {
// 递归地将所有属性转换为响应式
observe(val);
const dep = new Dep(); // 创建一个依赖收集器
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
const target = this;
Dep.target && dep.depend(); // 收集依赖
return val;
},
set: function reactiveSetter(newVal) {
// ... 检查值是否变化,然后更新并通知依赖
}
});
}
```
#### 2. 依赖收集器(Dep)
`Dep`类是一个简单的依赖收集和管理类,用于维护一个依赖列表(watchers)。每个响应式属性都会有一个对应的`Dep`实例。
```javascript
class Dep {
constructor() {
this.subs = []; // 依赖列表
}
addSub(sub) {
this.subs.push(sub);
}
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(); // 通知每个依赖进行更新
}
}
}
```
#### 3. Watcher 与 Dep.target
`Watcher`类代表了一个依赖,它可以是组件的渲染函数、计算属性或侦听器。`Dep.target`是一个全局的栈顶watcher,用于在getter执行时记录当前访问的依赖。
```javascript
class Watcher {
constructor(vm, expOrFn, cb, options) {
this.vm = vm;
this.cb = cb;
this.depIds = new Set();
this.getter = expOrFn;
this.value = this.get(); // 触发getter,从而收集依赖
}
get() {
pushTarget(this); // 将当前watcher设置为Dep.target
const value = this.getter.call(this.vm, this.vm);
popTarget(); // 恢复之前的Dep.target
return value;
}
addDep(dep) {
if (!this.depIds.has(dep.id)) {
dep.addSub(this);
this.depIds.add(dep.id);
}
}
update() {
// 更新逻辑,重新求值并调用cb
}
}
function pushTarget(watcher) {
Dep.target = watcher;
}
function popTarget() {
Dep.target = null;
}
```
### 总结
在Vue 2.x中,依赖收集通过`Object.defineProperty`实现的getter/setter机制,结合`Dep`和`Watcher`类来完成。当访问响应式属性时,如果当前存在`Dep.target`(即当前正在计算的watcher),则将该watcher添加到该属性的`Dep`实例的依赖列表中。当属性值变化时,`Dep`会通知所有注册的依赖(watchers)进行更新。这种机制确保了Vue能够精确地追踪和响应数据的变化,从而高效地更新DOM。
通过深入理解Vue的依赖收集机制,你可以更好地利用Vue构建高效、响应式的Web应用。如果你对Vue的响应式系统有更深的兴趣,不妨进一步探索Vue 3.x中引入的Proxy,以及Vue内部的更多高级特性和最佳实践。在码小课网站上,你可以找到更多关于Vue及其生态系统的深度解析和实战案例,帮助你不断提升自己的技术水平。