零、參考資料
一、雙劍之 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; }