咱們大部分對內產品,都普遍使用了 dob 管理前端數據流,下面隆重介紹一下。javascript
dob 是利用 proxy 實現的數據依賴追蹤工具,利用 dob-react 與 react 結合。html
dob 的核心思想大量借鑑了 mobx,可是從實現原理、使用便捷性,以及調試工具都作了大量優化。前端
功能 | redux | mobx | dob |
---|---|---|---|
異步 | 📦redux-thunk 等 | ✅ | ✅ |
可回溯 | ✅ | 📦 mst | ✅ |
分形 | 🤷 replaceReducer | ✅ | ✅ |
代碼精簡 | 📦 dva 等 | ✅ | ✅ |
函數式 | ✅ | 🤷 | 🤷 |
面向對象 | 🤷 | ✅ | ✅ |
Typescript 支持 | 🤷 | ✅ | ✅ |
調試工具 | ✅ | ✅ | ✅ |
調試工具 action 與 UI 雙向綁定 | ❌ | 🤷 | ✅ |
嚴格模式 | ✅ | ✅ | |
支持原生 Map 等類型 | ❌ | ✅ | |
observable 語法天然度 | ❌ | ✅ | |
store 規範化 | ✅ | 🤷 | ✅ |
dob 本身只實現了依賴追蹤功能,其特性很是簡單,以下示意圖+代碼所示:java
import { observable, observe } from "dob"
const obj = observable({ a: 1, b: 1 })
observe(() => {
console.log(obj.a)
})複製代碼
一句話描述就是:由
observable
產生的對象,在observe
回調函數中使用,當這個對象被修改時,會從新執行這個回調函數。react
那麼利用這個特性,將 observe 換成 react 框架的 render 函數,就變成了下圖:git
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 作的工做。github
上面這種結合隨意性太強,不利於項目維護,真正的 dob-react 對 dob 的使用方式作了限制。redux
爲了更好管理全局數據流,咱們引入 action、store 的概念,組件只能觸發 action,只有 action 內部才能修改 store:api
因爲聚合 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) {
// 靜默失敗
}
}複製代碼
藉助 dob-react-devtools 開啓調試模式,能夠實現相似 redux-devtools 的效果,但,該調試工具具有 action 與 UI 雙向可視化綁定 的功能等:
假設如今有一個文章列表需求,咱們建立了 ArticleStore
與 ArticleAction
,ArticleAction
提供了 addArticle, removeArticle, changeArticleTitle 等基礎方法。
如今咱們開啓了調試功能,得到以下 gif 圖的效果:
dob-react-devtools 主要提供了可視化界面展現每一個 Action 觸發列表,鼠標移動到每一個 Action 會高亮對應 rerender 的 UI 元素,UI 元素 render 的時候,左上角工具條也列出了與這個 UI 元素相關的 Action 列表。