Mobx 采用了一种'观察者模式'—— Observer
,整个设计架构都是围绕 Observer
展开:
ObserverValue
listener
,mobx 中有一个 Reaction
模块,可以对一些行为做依赖收集,在 React 中,是通过劫持 render
函数执行行为,进行的依赖收集get
和 set
,来进行的依赖收集和更新派发,当状态改变,观察者会直接精确通知每个 listener
在正常情况下,在 React 应用中使用 Mobx ,本质上 mobx 里面的状态,并不是存在 React 组件里面的,是在外部由一个个 mobx 的模块 model
构成,每一个 model
可以理解成一个对象,状态实质存在 model
中,model
状态通过 props
添加到组件中,可以用 mobx-react 中的 Provder
和 inject
便捷获取它们,虽然 mobx 中响应式处理这些状态,但是不要试图直接修改 props
来促使更新,这样违背了 React Prop 单向数据流的原则。正确的处理方法,还是通过 model
下面的 action
方法,来改变状态,React 实质上调用的是 action
方法。
为了建立观察者模式,便捷地获取状态 / 监听状态,mobx 很多接口都支持装饰器模式的写法,所以在 mobx 中,装饰器模式是最常用的写法:
class Root {
@observable name = "Cell"
@action setName(name) {
this.name = name
}
}
mobx 有一个重要特点,对于属性的依赖收集是精确的,颗粒化的:
class Root {
// C 组件使用
@observable object = {
name: "Cell", // A 组件使用
msg: "Hello World", // B 组件使用
}
@action setName(name) {
this.object.name = name
}
@action setMsg(msg) {
this.object.msg = msg
}
@action setObject(object) {
this.object = object
}
}
observable
处理过的属性,每一个属性都会有 ObserverValue
,比如上面的结构会产生三个 ObserverValue
,分别对应 object
,name
,msg
setName
改变 name
属性的时候,只有组件 A
会更新, name ObserverValue
只收集了用到 name
的依赖项 A
组件setMsg
同理,只有组件 B
更新 msg ObserverValue
只收集了 B
组件的依赖setObject
改变 object
的时候,即使 object
里面 name
,msg
的值没有变化,也会让组件 A
,组件 B
,组件 C
,全部渲染 object
的 Observer
同样收集了 name
的 ObserverValue
和 msg
的 ObserverValue
observable
对于引用数据类型,比如 Object
,Array
,Set
,Map
等,除了新建一个 observable
之外,还会做如下两点操作:
Proxy
:会把原始对象用 Proxy
代理,Proxy
会精确响应原始对象的变化,比如增加属性——给属性绑定 ObserverValue
,删除属性——给属性解绑 ObserverValue
等ObservableAdministration
: 对于子代属性,会创建一个 ObservableAdministration
,用于管理子代属性的 ObserverValue
对于外层 Root
,在 constructor
使用 makeObservable
,mobx 会默认给最外层的 Root
添加 ObservableAdministration
。
把上述每一个 class
称之为一个模块,如上述 Root
就是一个模块。mobx 的 api 基本用于构建每一个响应式模块。
makeObservable
constructor
调用 makeObservable
constructor() {
makeObservable(this)
}
observable
@observable name = "Cell"
action
action
包裹的函数,可以用来修改 mobx 中的状态@action setName(name) {
this.name = name
}
computed
@observable price = 666
@observable count = 1
@computed get total() {
return this.price * this.count
}
mobx-react 中的 api ,用于把 mobx 中的状态,提供给组件,并把组件也变成可观察的 —— mobx 状态改变,组件触发更新。
Provider
<Provider Root={Root}>
<App />
</Provider>
inject
inject
高阶组件可以把 Provider
中的 mobx 模块,混入到组件的 props
中,所以就可以在组件中消费状态,或者调用改变状态的方法@inject("Root")
class Index extends React.Component {}
observer
observer
高阶组件包装的组件,如果组件内部引入了 mobx 可观察属性值,当值改变的时候,会追溯到当前组件,促使当前组件更新@observer
class Index extends React.Component {}
创建 Root
模块,用于保存全局的一些数据:
import { observable, action, makeObservable } from "mobx"
class Root {
constructor() {
makeObservable(this)
}
@observable info = {
name: "Cell",
age: 18,
}
@action setInfo(info) {
this.info = info
}
}
export default new Root()
根本组件注入状态:
import Root from "./mobx"
export default function Index() {
return (
<Provider Root={Root}>
<Child />
</Provider>
)
}
使用状态:
const getUserInfo = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
name: "Cell",
age: 18,
})
}, 1000)
})
}
@inject("Root")
@observer
class Child extends React.Component {
async componentDidMount() {
const info = await getUserInfo()
this.props.Root.setInfo(info)
}
render() {
const { info } = this.props.Root
return (
<div>
<p>name: {info.name}</p>
<p>age: {info.age}</p>
</div>
)
}
}
注册模块用于组件通信:
class Commui {
constructor() {
makeObservable(this)
}
@observable msgA = ""
@observable msgB = ""
@action setMsgA(msg) {
this.msgA = msg
}
@action setMsgB(msg) {
this.msgB = msg
}
}
export default new Commui()
建立 A 、 B 组件实现通信功能:
@inject("Commui")
@observer
class CommponentA extends React.Component {
state = {
CompAsay: "",
}
render() {
const { CompAsay } = this.state
const { mesB } = this.props.Commui
return (
<div>
CommponentA
<div>from B:{mesB}</div>
<input
type="text"
value={CompAsay}
onChange={(e) => this.setState({ CompAsay: e.target.value })}
/>
<button onClick={() => this.props.Commui.setMsgA(CompAsay)}>send</button>
</div>
)
}
}
@inject("Commui")
@observer
class CommponentB extends React.Component {
state = {
CompBsay: "",
}
render() {
const { CompBsay } = this.state
const { mesA } = this.props.Commui
return (
<div>
CommponentB
<div>from A:{mesA}</div>
<input
type="text"
value={CompBsay}
onChange={(e) => this.setState({ CompBsay: e.target.value })}
/>
<button onClick={() => this.props.Commui.setMsgB(CompBsay)}>send</button>
</div>
)
}
}
可以从三个角度分析 mobx 和 mobx-react 整个流程:
observable
可观察属性的observer
,如何收集依赖项,与 observable
建立起关系的observable
makeObservable
ObservableAdministration
ObservableValue
Provider
和 inject
observer
Reaction
Proxy
, Object.defineProperty
等,劫持属性 get
,set
,数据变化多样性dispatch
store
,统一管理 store
下的状态,在 mobx 中可以有多个模块,可以理解每一个模块都是一个 store
,相互之间是独立的