面向未來的前端數據流框架- dob

blank

面向未來的前端數據流框架- dob

為獲得更好的閱讀體驗,建議移步github閱讀全文

我們大部分對內產品,都廣泛使用了dob管理前端數據流,下面隆重介紹一下。

dob是利用proxy實現的數據依賴追踪工具,利用dob-react與react結合。

dob的核心思想大量借鑒了mobx ,但是從實現原理、使用便捷性,以及調試工具都做了大量優化。

同時dob借鑒了nx-js/observer-util思想,但這個庫僅僅停留在理論層面,實際使用中細節問題百出,dob對其實現原理進行大幅修復,通過了多次實戰考驗。

特徵

  • ✅ 支持
  • ❌ 不支持
  • ? 生態支持
  • ? 不完全支持
blank

從依賴追踪開始

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

blank

import { observable, observe } from "dob" const obj = observable({ a: 1, b: 1 }) observe(() => { console.log(obj.a) })

一句話描述就是:由observable產生的對象,在observe回調函數中使用,當這個對像被修改時,會重新執行這個回調函數。

與react 優雅結合

那麼利用這個特性,將observe 換成react 框架的render 函數,就變成了下圖:

blank

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:

blank

由於聚合store注入到react非常簡單,只需要Provider @Connect即可,所以組織好store與action的關係,也就組織好了整個應用結構。

那麼如何組織action、store、react 之間的關係呢?對全局數據流,dob 提供了一種成熟的模式:依賴注入。以下是可維護性良好模式

blank

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

結構如下圖所示:

blank

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 模式。

假設現在有一個文章列表需求,我們創建了ArticleStoreArticleActionArticleAction提供了addArticle, removeArticle, changeArticleTitle等基礎方法。

現在我們開啟了調試功能,獲得如下gif 圖的效果:

blank

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 之間的選擇,憑團隊或個人愛好或許會更好抉擇。

What do you think?

Written by marketer

blank

WebVR開發教程——交互事件(二)使用Gamepad API

blank

在線追踪壓縮後的JS出錯代碼