標籤:

面向未來的前端數據流框架 - 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 模式。

假設現在有一個文章列表需求,我們創建了 ArticleStoreArticleActionArticleAction 提供了 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:前端框架 |