WHAT - Vue3 响应性(一)

今天我们参考 vue3 的 Reactivity in Depth 进行详细阐述 Vue3 的 Reactivity。

一、前言

One of Vue’s most distinctive features is the unobtrusive reactivity system.

Vue 框架最具特色的功能之一,即其响应式系统。

Component state consists of reactive JavaScript objects. When you modify them, the view updates. It makes state management simple and intuitive, but it’s also important to understand how it works to avoid some common gotchas.

在 Vue 中,您可以通过简单地声明数据,而不需要手动追踪数据变化并更新视图,Vue 框架会自动帮助您管理数据的变化并更新相关的视图。

这种响应式系统让开发者能够专注于编写业务逻辑,而不必过多关注数据变化的处理。当数据发生变化时,Vue 框架会自动检测到变化,并且更新相关的视图,从而实现了一种非常流畅和高效的开发体验。

这也是 Vue 框架受欢迎的原因之一,特别是在构建大型、复杂的前端应用程序时。但理解它是如何工作的也是很重要的,这可以帮助我们避免一些常见的陷阱。

二、什么是响应性:Reactivity

Reactivity is a programming paradigm that allows us to adjust to changes in a declarative manner.

响应性是一种可以使我们声明式地处理变化的编程范式。

接下来将通过一个例子来一步步认识它。

let A0 = 1
let A1 = 2
let A2 = A0 + A1
console.log(A2) // 3
A0 = 2
console.log(A2) // 仍然是 3

当我们更改 A0 后,A2 不会自动更新。

我们可以将相关逻辑封装到一个函数:

let A2
function update() {
  A2 = A0 + A1
}

然后需要定义几个术语:

  1. The update() function produces a side effect, or effect(for short), because it modifies the state of the program.
  2. A0 and A1 are considered dependencies of the effect, as their values are used to perform the effect. The effect is said to be a subscriber to its dependencies.

上面提到了 side effectdependenciessubscriber,可以称呼为副作用、依赖、订阅者,而 update() 可以称呼为副作用函数。

然后我们接着实现一个新函数:

whenDepsChange(update)

这个函数能够在 A0 或 A1 (这两个 dependencies ) 变化时调用 update(),产生 side effect。此时可以发现,我们还需要一个 subscriber 机制。

这些都将被实现在 whenDepsChange() 里:

  1. 当一个变量被读取时进行追踪。例如我们的 update() 执行了表达式 A0 + A1 的计算,则 A0 和 A1 都被读取到了。
  2. 如果一个变量在当前运行的副作用函数中被读取了,就将该副作用函数设为此变量的一个订阅者。例如由于 A0 和 A1 在 update() 执行时被访问到了,则 update() 需要在第一次调用之后成为 A0 和 A1 的订阅者。
  3. 探测变量的变化。例如当我们给 A0 赋了一个新的值后,应该通知其所有订阅了的副作用函数重新执行。

三、响应性如何工作的

1. trick + trigger

We can’t really track the reading and writing of local variables like in the example. There’s just no mechanism for doing that in vanilla JavaScript.

原生 JavaScript 没有提供任何机制可以做到直接追踪对上述示例中局部变量的读写。

但关键有一个,我们可以追踪(或者说拦截)对象属性的读写,注意,是对象属性,object properties.

在 JavaScript 中,提供两种方式:

  • getter / setters (vue2)
  • Proxy (vue3’s reactive objects, vue3’s refs)

WHAT - Vue3 响应性连接丢失问题(含解构场景) 中我们分析过 refreactive 的代码实现。

这里再给出一个更简洁的伪代码实现进行后续说明:

function ref(value) {
	const refObject = {
		get value() {
			track(refObject, 'value');
			return value;
		}
		set value(newValue) {
			value = newValue;
			trigger(refOjbect, 'value');
		}
	}
	return refObject;
}

function reactive(obj) {
	return new Proxy(obj, {
		get(target, key) {
			track(target, key);
			return target[key];
		},
		set(target, key, value) {
			target[key] = value;
			trigger(target, key);
		}
	})
}

注意,代码片段旨在以最简单的形式解释核心概念,因此会省略了许多细节和边界情况。

在两个实现里,都出现了 track()trigger()

  • 在 track 内部
let activeEffect;
function track(target, key) {
	if (activeEffect) {
		const effects = getSubscribersForProperty(target, key);
		effects.add(activeEffect);
	}
}

我们会检查当前是否有正在运行的副作用函数,如果有,我们会查找到一个存储了所有追踪了该属性的订阅者的 Set,然后将当前这个副作用函数作为新订阅者添加到该 Set 中。

注意,副作用订阅者一般会存储在一个 WeakMap<target, Map<key, Set<effect>>> 数据结构中。在第一次追踪时如果没有找到对相应属性 key 对应的的副作用函数集合 Set<effect>,它将会在这里新建,这就是 getSubscribersForProperty() 函数所做的事。为了简化描述,我们跳过了它其中的细节。

  • 在 trigger 内部
function trigger(target, key) {
  const effects = getSubscribersForProperty(target, key);
  effects.forEach((effect) => effect());
}

查找到该属性的所有副作用订阅,然后全部执行。

2. whenDepsChange:Reactive Effect

还记得我们前面的 whenDepsChange(update) 例子吗?这里我们可以给出伪代码实现了:

function whenDepsChange(update) {
	const effect = () => {
		activeEffect = effect;
		update();
		activeEffect = null;
	}
	effect();
}

它将原本的 update 函数封装在了一个副作用函数 effect 中,并在运行实际的 update 之前,这个副作用函数会将自己设为当前活跃的副作用 activeEffect(在执行完 update 后取消),这使得在执行 update 期间,触发的 track() 调用都能定位到这个当前活跃的副作用函数。即 if (activeEffect) {} 为真。

至此,我们已经实现了一个能够自动跟踪依赖的能力,它会在任意依赖被改动时,重新运行相关副作用订阅者。这称之为 Reactive Effect.

  • 22
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值