數據流 2022

這段時間把主流數據流代表的源碼翻了一遍,同時看到內網同學聞冰有寫一篇 6000+ 字的「談談複雜應用的狀態管理(上):爲什麼是 Zustand」,手癢也想寫寫我對於現階段數據流方案的理解。

1、說到數據流,大家可能會想到老牌的 Redux、Dva、Mobx、RxJS、XState,也可能會想到新一代的 Recoil、Jotai、Zustand、Valtio、React Tracked、Redux Toolkit,以及還有很多使用度沒那麼廣的方案 use-context-selector、react-easy-state、hox、useModel in umi、icestore、kylva、overmind 等,同時很多簡單場景可能會直接裸用 hooks 組織。這些方案大家應該多少都有用過一些,那他們之間有啥區別?又應該如何選擇?選擇時應該如何考慮呢?

2、數據流方案應該關注啥? 整理之後發現還是有不少的。比如心智模型、讀取數據、寫入數據、數據推導、異步 Action、渲染性能優化、Suspense 併發模式支持、SSR 支持、React 之外訪問、組件封裝、瞬時更新、插件中間件擴展、Redux DevTools 支持、兼容性、多實例和單實例、數據序列化能力、同步 / 異步更新、內存管理、測試、包尺寸等。(還有啥?)大部分數據流方案都考慮了這些點,區別是實現方式和使用體驗上的差異。

3、心智模型指的是「外部 store」和「內部 store」的區別。外部 store 以數據爲中心,設計應用時設計的是數據,從數據角度出發,無需考慮組件,從上到下地組織,比如 Redux、Dva、Zustand 等都屬於這類;內部 store 以 React 組件爲中心,設計應用時以組件爲主,從下到上原子化的組織(原子是一個最小但完整的狀態單位,它們是小塊的狀態,可以連接在一起形成新的派生狀態,最終形成了一個圖),比如新出的 Recoil、Jotai、React Tracked 都是此類。

4、寫入數據直接影響研發體驗,分「immutable」和「mutable」。比如 state => return {...state, count: state.count + 1} 是 immutable,而 state => state.count += 1 是 mutable,後者明顯更符合編碼直覺,尤其是涉及嵌套結構時,immutable 寫起來會比較痛苦。從實現上看,基於 proxy 的通常是 mutable,比如 Valtio、React Tracked、MobX;其他都是 immutable。但由於 immer 的存在,很多 immutable 的方案都可藉此實現 mutable 寫入數據。那基於 proxy 是不是沒優勢了?不是的。

5、proxy 影響的除了寫入數據,還有渲染優化。渲染優化分手動、半自動和全自動。手動是通過 selector 實現,比如 Redux 系、Zustand 和 use-context-selector 都屬於此類,selector 非常依賴開發者素質,select 多了就可能會導致不必要的渲染;全自動的有 Valtio、react-tracked、react-easy-state,實現方案是基於 Proxy 做的使用追蹤,開發者無需關心默認最優渲染性能;半自動比如 Jotai、Recoil 和 Mobx[? 待確認],需要用戶手動聲明依賴,實現方式是基於依賴追蹤(a 依賴 b,b 更新了會導致 a 更新),有些基於 proxy 有些不是。

6、其他功能點簡單說下我的理解。異步 Action 已是新一代數據流方案的基礎能力,大多是 async action(){} 直接聲明直接用,除了 Redux Toolkit 的用法還有點搓,但估計也是基於 Redux 方案優化的極限了?Suspense 大家也已逐步支持,主要是內部實現挑戰,遇到 promise 也要 throw 出來,開發者使用上應該是透明的。SSR 支持 對於外部 Store 的數據流來說會稍顯複雜,尤其是有多個 store 同時中間還存在共享依賴時會更麻煩,所以 Zustand 和 Valtio 都有 Context 模式以提供初始值。React 之外訪問可能在 1% 的場景會用到,如果沒有可能會形成卡點,據我瞭解應該就 jotai 不支持。組件封裝是強需求,這也是單一 store 的 Redux 沒落的原因之一,隨着業務發展項目變多,肯定會有提取公共組件的需求,這方面 Redux 和 Dva 非常弱,而新出的外部 store 方案都是多 store,無此問題。瞬時更新 指拿最新數據但不訂閱更新觸發 rerender,通常基於 ref 都可實現。兼容性 的分水嶺是 proxy,基於 proxy 唯一缺點就是兼容性。

7、數據流除了本地狀態,還有來自遠程服務器的狀態需要同步。而很多 CURD 項目的 80% 需求只需要做後者,所以社區湧現出不少專門解遠程狀態的庫,比如 React Query(已更名爲 TanStack Query)、SWR、Apollo、Relay、RTK Query、use-request 等。廣義地看,這些庫也是數據流方案。同時不得不提的是,Remix 通過在服務端做數據加載的方式,把下圖的數據流環 ui=fn(state) 擴展到了全棧。

8、**所以怎麼選?**沒有銀彈!如果站在社區開發者的角度來看。先看遠程狀態庫是否滿足需求,滿足的話就無需傳統數據流方案了;然後如果是非常非常簡單的場景用 useState + Context,複雜點的就不建議了,因爲需要自行處理渲染優化,手動把 Context 拆很細或者嘗試用 use-context-selector;再看心智模型,按內部 store 或外部 store 區分選擇,個人已經習慣後者,前者還在觀望;如果選外部 store,無兼容性要求的場景優先用類 Valtio 基於 proxy 的方案,寫入數據時更符合直覺,同時擁有全自動的渲染優化,有兼容性要求時則選 Zustand。

你怎麼看呢?

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/26_yYH5fbDyMTEKOMcNxtA