一、什么是响应式数据?
举个🌰子
const data = { text: 'Hello, 掘金!' } function effect() { document.title = data.text; } effect() 复制代码
在上述代码中,我们的effect
函数中的操作依赖于data.text
,运行之后,标签页标题会变成”Hello, 掘金“,我们知道当修改data数据之后,标签页标题是不会变的,如果会变,那么上述代码中的data
就是响应式数据。
二、Vue3响应式系统的基本原理
响应式系统基本原理就是依赖收集和派发更新。
我们首先需要对数据做代理。使用ProxyAPI,然后定义get
和set
操作,在get
中做依赖收集
,在set
中做派发更新
依赖收集
收集那些我们视图
中用到的对象属性值
。我们将其相关副作用函数和对应关系封装起来,并称之为依赖
,并将其存起来。
派发更新
当响应式数据发生改变时,我们需要将依赖
取出来,并执行相关函数来触发更新。
三、reactive简易实现
// 我们先写一个简单的单测 describe('effect', () => { it('happy path', () => { const user = reactive({ age: 10, }); let nextAge; effect(() => { nextAge = user.age + 1; }); expect(nextAge).toBe(11); // update user.age++; expect(nextAge).toBe(12); }); }); 复制代码
export function reactive(data) { return new Proxy(data, { get(target, key) { const res = Reflect.get(target, key); // 等价于 const res = target[key] // 依赖收集 track(target, key); return res; }, set(target, key, value) { const res = Reflect.set(target, key, value); // 返回值类型为Boolean,表示是否set成功 // 触发更新 trigger(target, key); return res; }, }); 复制代码
track
函数:
我们决定将依赖存放在dep变量中,由于对象的属性是唯一的,因此我们将dep的数据结构设置为Set,同时这里有两层关系: target => key => dep
const targetMap = new Map(); export function track(target, key) { let depsMap = targetMap.get(target); // 初始化会出现不存在depsMap的情况 if (!depsMap) { depsMap = new Map(); targetMap.set(target, depsMap); } let dep = depsMap.get(key); // 初始化也会出现不存在dep的情况 if (!dep) { dep = new Set(); depsMap.set(key, dep); } // 添加依赖, 这里的activeEffect为全局变量,后续我们在做处理。 dep.add(activeEffect); } let activeEffect 复制代码
trigger
函数:
我们只需要将dep中取出来,并执行相关方法,我们暂且将其定义为
run
方法,后续实现。
export function trigger(target, key) { let depsMap = targetMap.get(target); let dep = depsMap.get(key); for (const effect of dep) { effect.run(); } } 复制代码
activeEffect
和run
方法的处理:
class ReactiveEffect { private _fn: any; constructor(fn) { this._fn = fn; } run() { activeEffect = this; this._fn(); } } export function effect(fn) { const _effect = new ReactiveEffect(fn); _effect.run(); } 复制代码
四、流程梳理
const user = reactive({ age: 10, }); let nextAge; effect(() => { nextAge = user.age + 1; }); expect(nextAge).toBe(11); // update user.age++; expect(nextAge).toBe(12); 复制代码
- 传入
fn
到effect
函数。 - 生成一个
ReactiveEffect
的实例,并执行run
方法。 run
方法所做的事情就是将activeEffect
绑定到当前实例,然后调用我们传入的fn
。fn
函数首先会读取响应式数据的值,从而触发get
操作,进行依赖收集触发track
- 将依赖
activeEffect
存放在dep
中。 - 修改值
user.age++
,触发set
操作。 - 进行派发更新触发
trigger
- 将依赖依次取出,即
activeEffect
实例,执行该实例上的run
方法,从而数据nextAge
得到更新。
最后
以上就是reactive的简易实现,基于上面的实现,相信你可以更好地理解Vue3的响应式系统~
⚾如果你对这篇文章感兴趣欢迎点赞关注+收藏,更多精彩知识正在等你!😘
🏉此外笔者还有其他专栏,欢迎阅读~