面向未來的前端數據流框架 - dob
為獲得更好的閱讀體驗,建議移步 github 閱讀全文。
我們大部分對內產品,都廣泛使用了 dob 管理前端數據流,下面隆重介紹一下。
dob 是利用 proxy 實現的數據依賴追蹤工具,利用 dob-react 與 react 結合。
dob 的核心思想大量借鑒了 mobx,但是從實現原理、使用便捷性,以及調試工具都做了大量優化。
同時 dob 借鑒了 nx-js/observer-util 思想,但這個庫僅僅停留在理論層面,實際使用中細節問題百出,dob 對其實現原理進行大幅修復,通過了多次實戰考驗。
特徵
- ? 支持
- ? 不支持
- ?? 生態支持
- ?? 不完全支持
從依賴追蹤開始
dob 自己只實現了依賴追蹤功能,其特性非常簡單,如下示意圖+代碼所示:
import { observable, observe } from "dob"nnconst obj = observable({ a: 1, b: 1 })nnobserve(() => {n console.log(obj.a)n})n
一句話描述就是:由
observable
產生的對象,在observe
回調函數中使用,當這個對象被修改時,會重新執行這個回調函數。
與 react 優雅結合
那麼利用這個特性,將 observe 換成 react 框架的 render 函數,就變成了下圖:
import { observable, observe } from "dob"nimport { Provider, Connect } from dob-reactnnconst obj = observable({ a: 1 })nn@Connectnclass App extends React.Component {n render() {n return (n <span onClick={() => { this.props.store.a = 2 }}>n {this.props.store.a}n </span>n )n }n}nnReactDOM.render(nt<Provider store={obj}> <App/> </Provider>n, dom)n
這正是 dob-react 做的工作。
上面這種結合隨意性太強,不利於項目維護,真正的 dob-react 對 dob 的使用方式做了限制。
全局數據流
為了更好管理全局數據流,我們引入 action、store 的概念,組件只能觸發 action,只有 action 內部才能修改 store:
由於聚合 store 注入到 react 非常簡單,只需要 Provider
@Connect
即可,所以組織好 store 與 action 的關係,也就組織好了整個應用結構。
那麼如何組織 action、store、react 之間的關係呢?對全局數據流,dob 提供了一種成熟的模式:依賴注入。以下是可維護性良好模式:
import { Action, observable, combineStores, inject } from dobnimport { Provider, Connect } from dob-reactnn@observablenexport class UserStore {n name = bobn}nnexport class UserAction {n @inject(UserStore) private UserStore: UserStore;nn @Action setName () {n this.store.name = lucyn }n}nn@Connectnclass App extends React.Component {n render() {n return (n <span onClick={this.props.UserAction.setName}>n {this.props.UserStore.name}n </span>n )n }n}nnReactDOM.render(n <Provider {n ...combineStores({n UserStore,n UserActionn })n }>n <App />n </Provider>n, dom)n
一句話描述就是:通過
combineStores
聚合 store 與 action,store 通過inject
注入到 action 中被修改,react 組件通過@Connect
自動注入聚合 store。
局部數據流
對於對全局狀態不敏感的數據,可以作為局部數據流處理。
@Connect
裝飾器如果不帶參數,會給組件注入 Provider
所有參數,如果參數是一個對象,除了注入全局數據流,還會把這個對象注入到當前組件,由此實現了局部數據流。
PS: Connect 函數更多用法可以參考文檔: dob-react #Connect
結構如下圖所示:
import { Action, observable, combineStores, inject } from dobnimport { Provider, Connect } from dob-reactnn@observablenexport class UserStore {n name = bobn}nnexport class UserAction {n @inject(UserStore) private UserStore: UserStore;nn @Action setName () {n this.store.name = lucyn }n}nn@Connect(combineStores(UserStore, UserAction))nclass App extends React.Component {n render() {n return (n <span onClick={this.props.UserAction.setName}>n {this.props.UserStore.name}n </span>n )n }n}n
PS: 局部數據流可以替代 setState
管理組件自身狀態,每當組件被實例化一次,就會創建一個與之綁定的局部數據流。如果不想使用 react 提供的 setState,可以使用局部數據流替代。
非同步 & 副作用
redux 中需要將副作用代碼從 reducer 抽離,而 dob 不需要,我們可以如下書寫 action:
@Action async getUserInfo() {ntthis.UserStore.loading = truentthis.UserStore.currentUser = await fetchUser()ntthis.UserStore.loading = falsentnttry {nttthis.UserStore.articles = await fetchArticle()nt} catch(error) {ntt// 靜默失敗nt}n}n
Devtools
藉助 dob-react-devtools 開啟調試模式,可以實現類似 redux-devtools 的效果,但,該調試工具具備 action 與 UI 雙向可視化綁定 的功能等:
- UI 與 action 綁定:ui 元素觸發 rerender 時,自身會高亮,並在左上角顯示渲染次數,以及導致其 render 的 action。
- action 與 UI 綁定:展開右側 action 列表後,通過 hover 可展示因此 action 觸發而 rerender 的 UI 元素,高亮出來。
- 搜索、清空等方式管理 action。
- 點擊燈泡 開啟/關閉 debug 模式。
假設現在有一個文章列表需求,我們創建了 ArticleStore
與 ArticleAction
,ArticleAction
提供了 addArticle, removeArticle, changeArticleTitle 等基礎方法。
現在我們開啟了調試功能,獲得如下 gif 圖的效果:
dob-react-devtools 主要提供了可視化界面展示每個 Action 觸發列表,滑鼠移動到每個 Action 會高亮對應 rerender 的 UI 元素,UI 元素 render 的時候,左上角工具條也列出了與這個 UI 元素相關的 Action 列表。
開啟調試模式的方法:
import "dob-react-devtools"nimport { startDebug } from "dob-react"nnstartDebug()n
調試 UI 元素將自動附著在 Provider
元素上。
Devtools 雙向綁定原理解讀
一旦開啟調試模式,在 dob 依賴追蹤的 getter
setter
處就會增加調用信息的存儲(因此開啟調試模式時性能會一定程度下降,且更加吃內存)。
由於 react render 函數是同步的(16 支持的非同步渲染模式,在執行到 render 時也是同步的),只要包裹在 Action 中的變數存取,都可以在結束時打上唯一 id,當 react render 執行時,將當前 id 推送給調試工具,即可將 UI 與 Action 一一綁定。
action 與 debug 調用順序:startBatch -> debugInAction -> ...multiple nested startBatch and endBatch -> debugOutAction -> reaction -> observe。
生態
- dob-react 讓您在 react 中使用 dob!
- dob-react-devtools - dob-react 的調試工具,具有 UI 元素與 Action 雙向綁定特性。
- dob-redux - 可以在 dob 中使用 redux 與 react-redux,同時具有 mutable 與 immutable 的優點!
- dob-refect - 自動發請求,擺脫
componentDidUpdate
的困擾。
快速上手 Demo
git clone https://github.com/dobjs/dob-example.gitn
通過 npm i; npm start
快速運行起來,本項目包含了 dob 推薦的目錄結構與 store 組織方式。本 demo 使用了 typescript、react@16 與 react-router@4。
總結
mobx 即將到來的 4.0 版本也要支持 proxy 了:Road to 4.0 · Issue #1076 · mobxjs/mobx
屆時其性能與實用度將與 dob 越來越接近,但現在 dob 已憑藉大量使用經驗進行優化,對 devtools 的支持也更為搶眼,UI 與 Action 雙向綁定 debug 模式應該是首創。
所以至於選擇 mobx 還是 dob,全憑個人喜好,也許等到 4.0,mobx 會變得和 dob 一樣好用,現在,你可以通過 dob 預先嘗試拋棄 IE 的爽快開發體驗,以及獨具特色的 devTools。
至於 redux/rxjs 與 mobx/dob 之間的選擇,憑團隊或個人愛好或許會更好抉擇。
推薦閱讀:
※遺世獨立的組件——Angular應用中的單組件構建
※ReactEurope 2016 小記 - 上
※如何通俗易懂地解釋 Redux 和 Flux 的區別和實現意義?
TAG:前端框架 |