我们数据技术产品部有一部分只需要兼容最新版 chrome 对外产品,以及大部分对内产品,都广泛使用了 dob管理前端数据流,下面隆重介绍一下。
dob 是利用 proxy 实现的数据依赖追踪工具,利用 dob-react 与 react 结合。
dob 的核心思想大量借鉴了 mobx,但是从实现原理、使用便捷性,以及调试工具都做了大量优化。
特征
-
✅ 支持
-
❌ 不支持
-
📦 生态支持
-
🤷 不完全支持
功能
|
redux
|
mobx
|
dob
|
异步
|
📦redux-thunk 等
|
✅
|
✅
|
可回溯
|
✅
|
📦 mst
|
✅
|
分形
|
🤷 replaceReducer
|
✅
|
✅
|
代码精简
|
📦 dva 等
|
✅
|
✅
|
函数式
|
✅
|
🤷
|
🤷
|
面向对象
|
🤷
|
✅
|
✅
|
Typescript 支持
|
🤷
|
✅
|
✅
|
调试工具
|
✅
|
✅
|
✅
|
调试工具 action 与 UI 双向绑定
|
❌
|
🤷
|
✅
|
严格模式
|
|
✅
|
✅
|
支持原生 Map 等类型
|
|
❌
|
✅
|
observable 语法自然度
|
|
❌
|
✅
|
store 规范化
|
✅
|
🤷
|
✅
|
从依赖追踪开始
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