面向未來的前端資料流框架 - dob

黃子毅發表於2017-11-02

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

dob 是利用 proxy 實現的資料依賴追蹤工具,利用 dob-react 與 react 結合。

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

特徵

  • ✅ 支援
  • ❌ 不支援
  • ? 生態支援
  • ? 不完全支援
功能 redux mobx dob
非同步 ?redux-thunk
可回溯 ? mst
分形 ? replaceReducer
程式碼精簡 ? dva
函式式 ? ?
物件導向 ?
Typescript 支援 ?
除錯工具
除錯工具 action 與 UI 雙向繫結 ?
嚴格模式
支援原生 Map 等型別
observable 語法自然度
store 規範化 ?

從依賴追蹤開始

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

img
img

import { observable, observe } from "dob"

const obj = observable({ a: 1, b: 1 })

observe(() => {
    console.log(obj.a)
})複製程式碼

一句話描述就是:由 observable 產生的物件,在 observe 回撥函式中使用,當這個物件被修改時,會重新執行這個回撥函式。

與 react 優雅結合

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

img
img

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:

img
img

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

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

img
img

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

結構如下圖所示:

img
img

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 圖的效果:

dob-react-devtools
dob-react-devtools

dob-react-devtools 主要提供了視覺化介面展示每個 Action 觸發列表,滑鼠移動到每個 Action 會高亮對應 rerender 的 UI 元素,UI 元素 render 的時候,左上角工具條也列出了與這個 UI 元素相關的 Action 列表。

相關文章