欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 名人名企 > vue2.x 的依赖收集通知更新

vue2.x 的依赖收集通知更新

2024/10/24 15:37:54 来源:https://blog.csdn.net/weixin_46074818/article/details/143176443  浏览:    关键词:vue2.x 的依赖收集通知更新

Vue 的响应式系统是基于 观察者模式 设计的。当某些数据(状态)发生变化时,依赖这些数据的视图或计算属性会自动更新。为了实现这一点,Vue 会对数据进行 “劫持”(通过 Object.defineProperty 或 Proxy),并在读取数据时进行 依赖收集,在数据变化时通知所有依赖进行更新。

  1. 依赖收集

    • 当你使用 data 中的属性时,Vue 会自动创建一个 依赖关系,将当前组件的 Watcher (观察者) 与这个属性关联起来。
    • Watcher 是一个特殊的对象,它负责在数据发生变化时执行特定的函数。
    • 依赖收集的过程主要通过 getter 和 setter 函数实现。
  2. 通知更新

    • 当 data 中的属性发生变化时,Vue 会触发 setter 函数。
    • setter 函数会通知所有与该属性相关的 Watcher,并执行它们的更新逻辑。
    • 更新逻辑通常是重新渲染组件的模板,从而反映数据变化。
  3. 具体流程

    • 数据访问: 当你访问 data 中的属性时,Vue 会创建一个依赖关系,将当前组件的 Watcher 与该属性关联起来。
    • 依赖记录: Vue 会将 Watcher 记录到属性对应的 Dep 对象中。Dep 对象是一个专门用于管理依赖关系的对象。
    • 数据变化: 当 data 中的属性发生变化时,Vue 会触发 setter 函数。
    • 通知 Watcher: setter 函数会通知所有与该属性相关的 Watcher,并执行它们的更新逻辑。
    • 组件更新: Watcher 的更新逻辑通常是重新渲染组件的模板,从而反映数据变化。

示例:

<template><div>{{ message }}</div>
</template><script>
export default {data() {return {message: 'Hello Vue!'}},mounted() {// 访问 data 中的属性,建立依赖关系console.log(this.message);}
}
</script>

在这个例子中,当组件被挂载时,访问 this.message 会建立一个依赖关系,将组件的 Watcher 与 message 属性关联起来。

如果 message 属性的值发生变化,Vue 会通知相关的 Watcher,并重新渲染组件的模板,从而显示新的消息。

Vue 使用 Observer、Watcher 和 Dep 类来实现依赖收集。

依赖收集
  1. Observer:
    • Observer 类负责将数据转换为响应式对象。它会遍历对象的所有属性,并使用defineReactive 方法为每个属性设置 getter 和 setter。
    • defineReactive 方法会为每个属性创建一个 Dep 实例,用于收集依赖。
// core/observer/index.js
function observe(value, asRootData) {if (!isObject(value)) {return}let ob;if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {ob = value.__ob__;} else if (observerState.shouldConvert &&!isServerRendering() &&(Array.isArray(value) || isPlainObject(value)) &&Object.isExtensible(value) &&!value._isVue) {ob = new Observer(value);}return ob
}class Observer {constructor(value) {this.value = value; // 保存被观察的对象this.dep = new Dep(); // 创建一个 Dep 实例,用于收集依赖this.vmCount = 0; // 记录有多少个 Vue 实例依赖于这个观察者def(value, '__ob__', this); // 将观察者实例附加到对象上if (Array.isArray(value)) {// 处理数组的特殊情况} else {this.walk(value); // 遍历对象的所有属性}}walk(obj) {const keys = Object.keys(obj);for (let i = 0; i < keys.length; i++) {defineReactive(obj, keys[i]); // 为每个属性设置 getter 和 setter}}
}function defineReactive(obj, key, val, customSetter, shallow) {const dep = new Dep(); // 创建一个 Dep 实例,用于收集依赖const property = Object.getOwnPropertyDescriptor(obj, key); // 获取属性描述符if (property && property.configurable === false) {return}const getter = property && property.get; // 获取原始的 getterconst setter = property && property.set; // 获取原始的 setterlet childOb = !shallow && observe(val); // 递归观察子属性Object.defineProperty(obj, key, {enumerable: true,configurable: true,get: function reactiveGetter() {const value = getter ? getter.call(obj) : val; // 获取属性值if (Dep.target) {dep.depend(); // 收集依赖if (childOb) {childOb.dep.depend(); // 收集子属性的依赖}if (Array.isArray(value)) {dependArray(value); // 处理数组的依赖}}return value},set: function reactiveSetter(newVal) {const value = getter ? getter.call(obj) : val; // 获取属性值if (newVal === value || (newVal !== newVal && value !== value)) {return}if (getter && !setter) returnif (setter) {setter.call(obj, newVal); // 调用原始的 setter} else {val = newVal; // 更新属性值}childOb = !shallow && observe(newVal); // 递归观察新的子属性dep.notify(); // 通知所有依赖更新}});
}
  1. Dep:
    • Dep 类维护一个订阅者列表,记录所有依赖于该属性的 Watcher 实例。
    • 每当属性被访问时,Dep 会将当前的 Watcher 添加到订阅者列表中。
// core/observer/dep.js
class Dep {constructor() {this.id = uid++; // 唯一标识符this.subs = []; // 订阅者列表}addSub(sub) {this.subs.push(sub); // 添加订阅者}removeSub(sub) {remove(this.subs, 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(); // 通知所有订阅者更新}}
}Dep.target = null; // 当前正在收集依赖的 Watcher
  1. Watcher:
    • Watcher 类负责执行依赖收集和更新。它会在组件渲染时或计算属性计算时被创建。
    • Watcher 实例会在访问响应式数据时被添加到相应 Dep 的订阅者列表中。
// core/observer/watcher.js
class Watcher {constructor(vm, expOrFn, cb, options, isRenderWatcher) {this.vm = vm; // 保存 Vue 实例if (isRenderWatcher) {vm._watcher = this; // 将当前 Watcher 设置为渲染 Watcher}vm._watchers.push(this); // 添加到 Vue 实例的 Watcher 列表中this.cb = cb; // 回调函数this.id = ++uid; // 唯一标识符this.deps = []; // 依赖列表this.newDeps = []; // 新依赖列表this.depIds = new Set(); // 依赖的唯一标识符集合this.newDepIds = new Set(); // 新依赖的唯一标识符集合this.expression = expOrFn.toString(); // 表达式this.getter = expOrFn; // 获取器函数this.value = this.get(); // 初始值}get() {pushTarget(this); // 将当前 Watcher 设置为 Dep.targetlet value = this.getter.call(this.vm); // 执行获取器函数popTarget(); // 恢复 Dep.targetthis.cleanupDeps(); // 清理旧的依赖return value}addDep(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); // 添加订阅者}}}update() {this.run(); // 执行更新}run() {const value = this.get(); // 获取新值if (value !== this.value || isObject(value) || this.deep) {const oldValue = this.value; // 保存旧值this.value = value; // 更新值this.cb.call(this.vm, value, oldValue); // 调用回调函数}}
}
通知更新

通知更新是指在数据发生变化时,通知所有依赖于该数据的 Watcher,从而触发视图的更新。

  1. 数据变化:
    • 当响应式数据发生变化时,会触发 setter 方法。
    • setter 方法会调用 Dep 实例的 notify 方法,通知所有订阅者(即 Watcher 实例)。
  2. Watcher 更新:
    • 每个 Watcher 实例会在数据变化时被通知,并调用其 update 方法。
    • update 方法会调用 run 方法,重新计算依赖的数据,并触发组件的重新渲染。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com