vue如何实现observer和watcher源码解析

网络安全 2025-04-06 03:50www.168986.cn网络安全知识

这篇文章主要了Vue中的观察者(Observer)和监视器(Watcher)的源码实现细节。我们将深入了解如何实现Vue的observe功能以及$watch方法,帮助读者更好地理解Vue的双向数据绑定机制。

让我们回顾一下背景知识。Vue的核心功能之一是响应式系统,它依赖于Object.defineProperty来实现数据的响应式更新。通过定义属性的getter和setter,Vue能够追踪依赖关系并在数据变化时触发相应的更新。

接下来,我们将实现一个简单的$watch功能。为了实现这个功能,我们需要首先创建一个Observer类来观察对象的变化。我们利用递归遍历对象的所有属性,并为每个属性添加getter和setter。这样,当属性的值发生变化时,会触发setter函数。

为了更有效地处理这些变化,我们需要一个消息订阅器。这个订阅器将维护一个订阅者数组。一旦观察到数据变化,我们就会触发通知,并调用订阅者的更新方法。这个订阅器可以看作是一个Dep类,它包含添加订阅者和通知所有订阅者的功能。

下面是一个简单的代码示例:

```javascript

// Observer类用于观察对象的变化

export default class Observer {

constructor(value) {

this.value = value;

this.walk(value);

}

walk(value) {

Object.keys(value).forEach(key => this.convert(key, value[key]));

}

convert(key, val) {

defineReactive(this.value, key, val);

}

}

// 定义响应式属性

export function defineReactive(obj, key, val) {

const childOb = observe(val);

Object.defineProperty(obj, key, {

enumerable: true,

configurable: true,

get() { return val; },

set: newVal => {

childOb = observe(newVal); // 如果新赋值的值是个复杂类型,再次观察并递归添加getter和setter

// 在这里添加订阅者通知逻辑(触发Dep的notify方法)

}

});

}

// 观察值并返回Observer实例或null(如果值不是对象)

export function observe(value) {

if (!value || typeof value !== 'object') return;

return new Observer(value);

}

// Dep类用于实现消息订阅器功能

export default class Dep {

constructor() {

this.subs = []; // 存储订阅者数组

}

addSub(sub) { // 添加订阅者到数组中

this.subs.push(sub);

}

notify() { // 触发通知并调用所有订阅者的update方法

this.subs.forEach(sub => sub.update()); // 这里假设每个订阅者都有一个update方法用于处理更新逻辑

}

}

未知的编程世界,我们一步步完善代码。今天我们来一个关于响应式系统的关键部分——如何定义反应性对象。

设想我们有一个函数 `defineReactive`,它的任务是接受一个对象、一个键和一个值,然后在对象上定义一个新的响应式属性。我们来详细看看这个函数是如何工作的。

创建一个新的依赖对象 `dep` 和观察当前传入的值 `val` 的子对象 `childOb`。然后,使用 `Object.defineProperty` 在目标对象上定义新的属性。这个属性具有可枚举性和可配置性,并定义了 `get` 和 `set` 方法。

当我们访问这个属性时,会触发 `get` 方法,返回当前的值 `val`。而当我们尝试设置这个属性的新值时,会触发 `set` 方法。在 `set` 方法中,我们先判断新值与旧值是否相同,如果相同则直接返回,否则更新 `val` 的值,重新观察新的值,并通知所有订阅者。

那么谁是这些订阅者呢?没错,就是我们的观察者 `Watcher`。每当有依赖通知时,我们就会遍历所有的 `Watcher`,并调用它们的 `update` 方法。

接下来,我们来一下 `Watcher` 的实现。这个类接收一个表达式和一个回调函数作为参数。在构造函数中,我们初始化一些基本属性并调用 `get` 方法来获取初始值。而 `update` 和 `run` 方法则用于更新和触发回调函数。

现在问题是,我们如何将 `Watcher` 加入到依赖系统中呢?我们知道 `dep` 存在于闭包中,并且在 `Watcher` 的构造函数中会调用 `this.get`。我们可以在 `Object.defineProperty` 的 `get` 函数中做判断,如果是 `Watcher` 的 `this.get` 调用的,就将它添加到订阅者列表中。那么如何判断呢?我们可以在 `Dep` 中定义一个全局唯一的变量来实现这个判断。

区分fuction还是expression

在Vue中,我们需要区分是函数表达式还是普通的表达式。我们可以使用Dep类来追踪依赖项的变化。以下是简化版的代码,重点在于如何区分function和expression:

```javascript

// 定义依赖追踪类

class Dep {

constructor() {

this.subscribers = []; // 存储Watcher实例的数组

}

// 添加订阅者(Watcher)的方法

addSub(watcher) {

this.subscribers.push(watcher);

}

// 当数据变化时通知所有订阅者重新计算依赖项的值

notify() {

this.subscribers.forEach(subscriber => subscriber()); // 执行所有订阅者的回调函数

}

Copyright © 2016-2025 www.168986.cn 狼蚁网络 版权所有 Power by