JS - 实现一个简单的响应式系统

在各大前端框架中,响应式系统无疑是核心。今天我们就来实现一个简易版的 Angular Signals。

先看结果:

 

ts

复制代码

// 创建 Signal const s1 = signal(2), s2 = signal('s2') // 副作用 const clear = effect(() => { console.log('signal is:', s1(), s2()) }) // 第一次运行,输出 signal is: 2 s2 // 更新值 s1.set(0) // signal is: 0 s2 s1.update(p => p + 1) // signal is: 1 s2 s2.set('new s2') // signal is: 1 new s2 clear() s1.set(1024) s2.set('s2 after clear') console.log(s1(), s2()) // 1024 s2 after clear

下面我们就来实现 signaleffect 这两个函数。

signal 函数应该接受一个初始值,返回一个 Signal 对象,像这样:

 

ts

复制代码

function signal<T>(initial: T): Signal<T>

Signal 对象应该是一个函数,调用它将返回内部值。并且有两个方法 setupdate。除此之外,它的内部还应该保存一个值和副作用函数列表:

 

ts

复制代码

interface Signal<T> { (): T set(value: T): T update(updater: (prev: T) => T): T _value: T _effect: Set<() => void> }

现在让我们来实现 signal 函数:

 

ts

复制代码

function signal<T>(initial: T): Signal<T> { const s = (() => s._value) as Signal<T> s._value = initial s._effect = new Set() s.set = v => (s._value = v) s.update = updater => s.set(updater(s())) return s }

看上去很简单,这是因为我们还没有追踪响应式依赖。为了追踪依赖,我们需要一个全局变量保存副作用函数和所用到的信号:

 

ts

复制代码

let globalState: | { effect: () => void // 当前的副作用函数 signals: Set<Signal<unknown>> // 当前副作用函数所依赖的信号 } | undefined

我们可以先来实现 effect 函数:

 

ts

复制代码

function effect(func: () => void) { globalState = { effect: func, signals: new Set() } const signals = globalState.signals func() globalState = undefined return () => signals.forEach(s => s._effect.delete(func)) }

现在你知道我们应该怎样做来实现状态跟踪吗?答案是:在读取值的时候将当前副作用函数添加到信号的副作用函数列表中,在设置值的时候调用信号的副作用函数:

 

ts

复制代码

const s = (() => { if (globalState) { globalState.signals.add(s) s._effect.add(globalState.effect) } return s._value }) as Signal<T> s.set = v => { s._value = v s._effect.forEach(cb => cb()) return v }

如此,我们就可以自动追踪副作用的依赖,并在信号更新时自动调用副作用函数了。

全部代码如下:

 

ts

复制代码

interface Signal<T> { (): T set(value: T): T update(updater: (prev: T) => T): T _value: T _effect: Set<() => void> } let globalState: | { effect: () => void signals: Set<Signal<unknown>> } | undefined function signal<T>(initial: T): Signal<T> { const s = (() => { if (globalState) { globalState.signals.add(s) s._effect.add(globalState.effect) } return s._value }) as Signal<T> s._value = initial s._effect = new Set() s.set = v => { s._value = v s._effect.forEach(cb => cb()) return v } s.update = updater => s.set(updater(s())) return s } function effect(func: () => void) { globalState = { effect: func, signals: new Set() } const signals = globalState.signals func() globalState = undefined return () => signals.forEach(s => s._effect.delete(func)) } // 测试 const s1 = signal(2), s2 = signal('s2'), clear = effect(() => { console.log('signal is:', s1(), s2()) }) s1.set(0) s1.update(p => p + 1) s2.set('new s2') clear() s1.set(1024) s2.set('s2 after clear') console.log(s1(), s2())

运行结果:

 

text

复制代码

signal is: 2 s2 signal is: 0 s2 signal is: 1 s2 signal is: 1 new s2 1024 s2 after clear

标签:

JavaScript


原文链接:https://juejin.cn/post/7377247135006130210
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值