面向未來的前端數據流框架- dob
為獲得更好的閱讀體驗,建議移步github閱讀全文。
我們大部分對內產品,都廣泛使用了dob管理前端數據流,下面隆重介紹一下。
dob是利用proxy實現的數據依賴追踪工具,利用dob-react與react結合。
dob的核心思想大量借鑒了mobx ,但是從實現原理、使用便捷性,以及調試工具都做了大量優化。
同時dob借鑒了nx-js/observer-util思想,但這個庫僅僅停留在理論層面,實際使用中細節問題百出,dob對其實現原理進行大幅修復,通過了多次實戰考驗。
特徵
- ✅ 支持
- ❌ 不支持
- ? 生態支持
- ? 不完全支持

從依賴追踪開始
dob 自己只實現了依賴追踪功能,其特性非常簡單,如下示意圖+代碼所示:

import { observable, observe } from "dob" const obj = observable({ a: 1, b: 1 }) observe(() => { console.log(obj.a) })
一句話描述就是:由
observable
產生的對象,在observe
回調函數中使用,當這個對像被修改時,會重新執行這個回調函數。
與react 優雅結合
那麼利用這個特性,將observe 換成react 框架的render 函數,就變成了下圖:

import { observable, observe } from "dob" import { Provider, Connect } from 'dob-react' const obj = observable({ a: 1 }) @Connect class App extends React.Component { render() { return ( <span onClick={() => { this.props.store.a = 2 }}> {this.props.store.a} </span> ) } } ReactDOM.render( <Provider store={obj}> <App/> </Provider> , dom)
這正是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 'dob' import { Provider, Connect } from 'dob-react' @observable export class UserStore { name = 'bob' } export class UserAction { @inject(UserStore) private UserStore: UserStore; @Action setName () { this.store.name = 'lucy' } } @Connect class App extends React.Component { render() { return ( <span onClick={this.props.UserAction.setName}> {this.props.UserStore.name} </span> ) } } ReactDOM.render( <Provider { ...combineStores({ UserStore, UserAction }) }> <App /> </Provider> , dom)
一句話描述就是:通過
combineStores
聚合store與action,store通過inject
注入到action中被修改,react組件通過@Connect
自動注入聚合store。
局部數據流
對於對全局狀態不敏感的數據,可以作為局部數據流處理。
@Connect
裝飾器如果不帶參數,會給組件注入Provider
所有參數,如果參數是一個對象,除了注入全局數據流,還會把這個對象注入到當前組件,由此實現了局部數據流。
PS: Connect函數更多用法可以參考文檔: dob-react #Connect
結構如下圖所示:

import { Action, observable, combineStores, inject } from 'dob' import { Provider, Connect } from 'dob-react' @observable export class UserStore { name = 'bob' } export class UserAction { @inject(UserStore) private UserStore: UserStore; @Action setName () { this.store.name = 'lucy' } } @Connect(combineStores(UserStore, UserAction)) class App extends React.Component { render() { return ( <span onClick={this.props.UserAction.setName}> {this.props.UserStore.name} </span> ) } }
PS:局部數據流可以替代setState
管理組件自身狀態,每當組件被實例化一次,就會創建一個與之綁定的局部數據流。如果不想使用react 提供的setState,可以使用局部數據流替代。
異步& 副作用
redux 中需要將副作用代碼從reducer 抽離,而dob 不需要,我們可以如下書寫action:
@Action async getUserInfo() { this.UserStore.loading = true this.UserStore.currentUser = await fetchUser() this.UserStore.loading = false try { this.UserStore.articles = await fetchArticle() } catch(error) { // 静默失败 } }
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" import { startDebug } from "dob-react" startDebug()
調試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.git
通過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 之間的選擇,憑團隊或個人愛好或許會更好抉擇。