React 进阶 - React Mobx

2023-05-17 20:54:32 浏览数 (2)

# Mobx 特性

# 观察者模式

Mobx 采用了一种'观察者模式'—— Observer,整个设计架构都是围绕 Observer 展开:

  • 在 mobx 的状态层,每一个需要观察的属性都会添加一个观察者,可以称之为 ObserverValue
  • 有了观察者,那么就需要向观察者中收集 listener ,mobx 中有一个 Reaction 模块,可以对一些行为做依赖收集,在 React 中,是通过劫持 render 函数执行行为,进行的依赖收集
  • 如何监听改变,用自定义存取器属性中的 getset ,来进行的依赖收集和更新派发,当状态改变,观察者会直接精确通知每个 listener

# 状态提升

在正常情况下,在 React 应用中使用 Mobx ,本质上 mobx 里面的状态,并不是存在 React 组件里面的,是在外部由一个个 mobx 的模块 model 构成,每一个 model 可以理解成一个对象,状态实质存在 model 中,model 状态通过 props 添加到组件中,可以用 mobx-react 中的 Provderinject 便捷获取它们,虽然 mobx 中响应式处理这些状态,但是不要试图直接修改 props 来促使更新,这样违背了 React Prop 单向数据流的原则。正确的处理方法,还是通过 model 下面的 action 方法,来改变状态,React 实质上调用的是 action 方法。

# 装饰器模式

为了建立观察者模式,便捷地获取状态 / 监听状态,mobx 很多接口都支持装饰器模式的写法,所以在 mobx 中,装饰器模式是最常用的写法:

代码语言:javascript复制
class Root {
  @observable name = "Cell"
  @action setName(name) {
    this.name = name
  }
}

# 精确颗粒化收集

mobx 有一个重要特点,对于属性的依赖收集是精确的,颗粒化的:

代码语言:javascript复制
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 ,分别对应 objectnamemsg
  • 当上面通过 setName 改变 name 属性的时候,只有组件 A 会更新,
    • 也就是 name ObserverValue 只收集了用到 name 的依赖项 A 组件
  • 调用 setMsg 同理,只有组件 B 更新
    • msg ObserverValue 只收集了 B 组件的依赖
  • 当上面通过 setObject 改变 object 的时候,即使 object 里面 namemsg 的值没有变化,也会让组件 A ,组件 B ,组件 C ,全部渲染
    • objectObserver 同样收集了 nameObserverValuemsgObserverValue

# 引用类型处理

observable 对于引用数据类型,比如 ObjectArraySetMap 等,除了新建一个 observable 之外,还会做如下两点操作:

  • Proxy:会把原始对象用 Proxy 代理,Proxy 会精确响应原始对象的变化,比如增加属性——给属性绑定 ObserverValue ,删除属性——给属性解绑 ObserverValue
  • ObservableAdministration: 对于子代属性,会创建一个 ObservableAdministration,用于管理子代属性的 ObserverValue

对于外层 Root ,在 constructor 使用 makeObservable ,mobx 会默认给最外层的 Root 添加 ObservableAdministration

# Mobx 基本使用

# mobx 常用 API

把上述每一个 class 称之为一个模块,如上述 Root 就是一个模块。mobx 的 api 基本用于构建每一个响应式模块。

makeObservable

  • 在新版本 mobx 中,想要让整个模块变成可响应式的,那么需要在 constructor 调用 makeObservable
代码语言:javascript复制
constructor() {
  makeObservable(this)
}

observable

  • 会给属性值加一个观察者对象,使其能变成可观察的,当属性值改变的时候,观察者会通知每一个依赖项
代码语言:javascript复制
@observable name = "Cell"

action

  • 通过 action 包裹的函数,可以用来修改 mobx 中的状态
代码语言:javascript复制
@action setName(name) {
  this.name = name
}

computed

  • 根据现有的状态或其它计算值衍生出的值
代码语言:javascript复制
@observable price = 666
@observable count = 1
@computed get total() {
  return this.price * this.count
}

# mobx-react 常用 API

mobx-react 中的 api ,用于把 mobx 中的状态,提供给组件,并把组件也变成可观察的 —— mobx 状态改变,组件触发更新。

Provider

  • 用于把 mobx 的各个模块,用 Context 上下文形式,保存起来,供给组件使用
代码语言:javascript复制
<Provider Root={Root}>
  <App />
</Provider>

inject

  • inject 高阶组件可以把 Provider 中的 mobx 模块,混入到组件的 props 中,所以就可以在组件中消费状态,或者调用改变状态的方法
代码语言:javascript复制
@inject("Root")
class Index extends React.Component {}

observer

  • observer 高阶组件包装的组件,如果组件内部引入了 mobx 可观察属性值,当值改变的时候,会追溯到当前组件,促使当前组件更新
代码语言:javascript复制
@observer
class Index extends React.Component {}

# 实践-实现状态共享

创建 Root 模块,用于保存全局的一些数据:

代码语言:javascript复制
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()

根本组件注入状态:

代码语言:javascript复制
import Root from "./mobx"

export default function Index() {
  return (
    <Provider Root={Root}>
      <Child />
    </Provider>
  )
}

使用状态:

代码语言:javascript复制
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>
    )
  }
}

# 实践-实现组件通信

注册模块用于组件通信:

代码语言:javascript复制
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 组件实现通信功能:

代码语言:javascript复制
@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 和 mobx-react 整个流程:

  • 初始化: mobx 在初始化的时候,是如何处理 observable 可观察属性的
  • 依赖收集:通过 mobx-react 中的 observer ,如何收集依赖项,与 observable 建立起关系的
  • 派发更新:当改变可观察属性的值的时候,如何更新对应组件的

# 模块初始化

  1. 绑定状态-observable
  2. 激活状态-makeObservable
  3. 观察者属性管理者-ObservableAdministration

# 依赖收集

  1. 观察者-ObservableValue
  2. 注入模块-Providerinject
  3. 可观察组件-observer
  4. 反应器-Reaction

# 派发更新

# Mobx 与 Redux 区别

  • 在 Mobx 在上手程度上,要优于 Redux ,比如 Redux 想使用异步,需要配合中间价,流程比较复杂
  • Redux 对于数据流向更规范化,Mobx 中数据更加多样化,允许数据冗余
  • Redux 整体数据流向简单,Mobx 依赖于 ProxyObject.defineProperty 等,劫持属性 getset ,数据变化多样性
  • Redux 可拓展性比较强,可以通过中间件自定义增强 dispatch
  • 在 Redux 中,基本有一个 store ,统一管理 store 下的状态,在 mobx 中可以有多个模块,可以理解每一个模块都是一个 store ,相互之间是独立的

0 人点赞