零、参考资料
一、双剑之 createSignal
用法:
// js const [value, setValue] = createSignal(1); // html-template <div>值:{ value() }</div>
简单实现:
const createSignal = (value) => { const getter = () => { return value; }; const setter = (newValue) => { value = newValue; }; return [getter, setter]; };
二、双剑之 createEffect
用法:
// js const [getValue, setValue] = createSignal(1); createEffect(() => { console.log('effect 执行了', getValue()); })
大致实现,包括对 createSignal 的改造:
// 缓存桥变量 const observers = []; const getCurrentObserver = () => observers[observers.length - 1]; const createSignal = (value) => { const subscribers = []; // 注意,这里目前用的是 数组 const getter = () => { const currentObserver = getCurrentObserver(); currentObserver && subscribers.push(currentObserver); return value; }; const setter = (newValue) => { value = newValue; subscribers.forEach(subscriber => subscriber()); }; return [getter, setter]; }; const createEffect = (effect) => { const execute = () => { observers.push(effect); try { effect(); } finally { observers.pop(); } } execute(); }
三、派生之 createMemo
用法:
const [count, setCount] = createSignal(10); const double = () => count() * 2;
从代码上看, double 是一个 signal 的派生值,页面上只要有一个地方用到了 double ,那么就要执行一遍过程计算函数 ` () => count() * 2 `,如果页面上有 n 个地方都用到了 double ,那么是不是要执行 n 遍呢?回答:是的。所以就引入了 createMemo
实现:
const createMemo = (memo) => { const [_value, _setValue] = createSignal(); createEffect(() => { _setValue(memo()); }); return _value; }
const setter = (newValue) => { if (value === newValue) return; // 就加了这一句 value = newValue; subscribers.forEach(subscriber => subscriber()); };
四、总结
核心的实现和 Vue 3 的实现基本没什么差别,只不过在创建响应式数据的时候,V3 选择的是使用 Proxy 对象,而 solid 选择的是闭包缓存数据
全部代码:
const observers = []; const getCurrentObserver = () => observers[observers.length - 1]; const createSignal = (value) => { const subscribers = new Set(); // 注意,这里要用 Set // 获取 Signal value const getter = () => { const currentObserver = getCurrentObserver(); currentObserver && subscribers.add(currentObserver); return value; }; // 修改 Signal value const setter = (newValue) => { if (value === newValue) return; value = newValue; subscribers.forEach(subscriber => subscriber()); }; return [getter, setter]; }; const createEffect = (effect) => { const execute = () => { observers.push(effect); try { effect(); } finally { observers.pop(); } } execute(); } const createMemo = (memo) => { const [_value, _setValue] = createSignal(); createEffect(() => { _setValue(memo()); }); return _value; }