banner
banner
banner
NEWS LETTER

Mobx库

Scroll down
  • 自动获取源自应用state的数据
  • 机制:事件调用actions,使用actions来修改state,然后自动更新,更新的值会触发对应的值变化
  • 安装:mobx的介绍
  • 搭档:React 通过提供机制把应用状态转换为可渲染组件树并对其进行渲染。而MobX提供机制来存储和更新应用状态供 React 使用。

核心概念

优点

  • 简单,可扩展
  • 保证参照完整性
  • 高效

要点

  • 将应用变成响应式的三个步骤
    • 定义状态并使其可观察
      • 使用observable
    • 创建视图以响应状态的变化
      • 使用@observer
    • 更改状态

observable

  • 定义:使用@observable装饰器给现有的数据结构添加可观察功能

用法

  • observable(value)

  • @observable classProperty = value

  • value

    • Map: 会返回一个新的Observable Map
      1
      2
      const map = observable.map({ key: "value"});
      map.set("key", "new value");
    • 数组:会返回一个Observable Array
      1
      const list = observable([1,2,3]) 
    • 没有原型的对象:会被克隆并且所有属性都会被转换成可观察的
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      const person = observable({
      // 观察的属性
      name: "cc",
      age: '23',
      show: false,
      // 计算属性
      get labelText() {
      return this.show ? `${this.name} age: ${this.age}` : this.name
      }
      // 动作
      setAge(age) {
      this.age = age
      }
      }, {
      setAge: action
      })
      autorun(() => console.log(person.labelTExt))
    • 有原型的对象(原值类型值和引用类型值):会返回一个Boxed Observable。在构造函数中使用extendObservable 或在类中使用@observable
      • 获取当前值 : .get()
      • 更新值: .set()
      • 监听值的更改:antorun(()=>{})
      • eg:const temperature = observable.box(20);temperature.set(25)

decorators

  • 不使用observable,然后单独使用decorate来另外定义
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class Person {
    name = "John"
    age = 42
    showAge = false

    get labelText() {
    return this.showAge ? `${this.name} (age: ${this.age})` : this.name;
    }

    setAge(age) {
    this.age = age;
    }
    }
    // 使用 decorate 时,所有字段都应该指定
    decorate(Person, {
    name: observable,
    age: observable,
    showAge: observable,
    labelText: computed,
    setAge: action
    })

(@)computed

  • 定义:使用@computed装饰器或(extend)Observable时调用的getter/setter函数来定义数据发生变化时自动更新的值

@computed 与 autorun 的区别

  • @computed:响应式调用的表达式,响应式的产生一个可以被其它observer使用的值
  • autorun:响应式调用的表达式,与@computed区别,不产生新值,只想要效果

getter 与 setter

  • 正常情况下的示例:@computed get total(){ retuan this.price * this.amount}
  • 正常情况下setter也不需要注解,因为是一个自动的动作
  • setter不能用来直接改变计算属性的值,但是可以用来逆向衍生
  • 永远在getter之后定义setter
  • observable.object 和 extendObservable 都会自动将 getter 属性推导成计算属性,所以这两种情况不用添加装饰器
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const orderLine = observable.object({
    price:0
    amount:1,
    get total() {
    retuan this.price * this.amount
    }
    set total(total) {
    // 从total中推导出price
    this.price = total / this.amount
    }
    })

computed(expression) 函数(不常用)

  • 使用场合:传递一个“在observable.box中”的计算值
    1
    2
    3
    4
    5
    6
    7
    import {observable, computed} from "mobx";
    var name = observable.box("John");
    var upperCaseName = computed(() =>
    name.get().toUpperCase()
    );
    var disposer = upperCaseName.observe(change => console.log(change.newValue));
    name.set("Dave");

第二参数对象的可选参

  • name:字符串, 在 spy 和 MobX 开发者工具中使用的调试名称
  • context:在提供的表达式中使用的 this
  • set:要使用的setter函数。
  • equals:比较前一个值和后一个值的函数,相等不评估
  • requiresReaction:推荐为true
  • keepAlive:慎用,会造成内存泄露

内置比较器

  • comparer.identity: 使用恒等 (===) 运算符来判定两个值是否相同。
  • comparer.default: 等同于 comparer.identity,但还认为 NaN 等于 NaN
  • comparer.structural: 执行深层结构比较以确定两个值是否相同。

Autorun(自定义reactions)

  • 第二参数对象的可选参
    • delay:对reaction函数去抖动的数字,默认为0不抖动,以毫秒为单位
    • name:字符串,用于在例如spy事件中用作reaction的名称
    • onError: 处理reaction的错误
    • scheduler:设置自定义调度器已决定如何调度autorun函数的重新运行

When(自定义reactions)

  • 语法:when(predicate: () => boolean, effect?: () => void, options?)
  • when 观察并运行给定的 predicate,直到返回true。 一旦返回 true,给定的 effect 就会被执行,然后 autorunner(自动运行程序) 会被清理。 该函数返回一个清理器以提前取消自动运行程序。

when-promise

1
2
3
4
// 不提供effect返回一个promise
async function() {
await when(() => that.isVisible)
}

Reaction(自定义reactions)

  • antorun的变种,与anturun的区别,创建时reaction函数不会直接运行,只有在数据表达式首次返回一个新值后才会运行。在执行时访问任何observable都不会被追踪。
  • 用法:reaction(() => data, (data, reaction) => { sideEffect }, options?)
  • 简单理解:reaction是computed(expression).observe(action(sideEffect)) 或 autorun(() => action(sideEffect)(expression)) 的语法糖。

用法解析

  • 参数1为data函数,用来追踪并返回数据作为第二个函数的输入。
  • 参数2为effect函数,接收的两个参数分别为data函数返回的值当前的reaction,当前的reaction用来在执行期间清理reaction。effect函数仅对data函数中访问的数据作出反应
  • 返回一个清理函数

options可选参

  • fireImmediately: 布尔值,用来标识效果函数是否在数据函数第一次运行后立即触发。默认值是 false
  • 其他的同autorun一样

@observer

  • 用来将 React 组件转变成响应式组件,observer由mobx-react包提供
    1
    2
    3
    4
    5
    @observer class Timer extends React.component {
    render (
    return <span>seconds:{this.props.timerData.second}</span>
    )
    }
  • 当observer需要组合其它装饰器或高阶组件时,请确保observer是最深处(第一个应用)的装饰器,否则不起效
  • 使用@observer装饰器observer(class Timer … {})效果一样
  • 陷阱:使用以下方式初始化,组件不会有反应。值需要通过引用来传递而不是通过字面量值来传递
    • React.render(<Timer timerData={timerData.secondsPassed} />, document.body)
  • 无状态函数组件:const Timer = observer(({timerData}) => seconds:{timerData.second})
  • 可观察的局部组件状态:在使用@observer的类中使用@observable来观察属性。响应式状态会被render调用,也会调用componentWillUpdate 和 componentDidUpdate

使用 inject 将组件连接到提供的 stores

  • 用法:inject(stores)(component) 或 @inject(stores) class Component…
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    const colors = observable({
    foreground: '#000',
    background: '#fff'
    });
    const App = () =>
    <Provider colors={colors}>
    <app stuff... />
    </Provider>;
    const Button = inject("colors")(observer(({ colors, label, onClick }) =>
    <button style={{
    color: colors.foreground,
    backgroundColor: colors.background
    }}
    onClick={onClick}
    >{label}</button>
    ));
    // 稍后..
    colors.foreground = 'blue';
    // 所有button都会更新

componentWillReact(生命周期钩子)

  • 当组件因为它观察的数据发生了改变,它会重新渲染,这时componentWillReact会被触发,方便组件追溯并找到导致渲染的操作

MobX 会对什么作出反应?

  • MobX 会对在执行跟踪函数期间读取的任何现有的可观察属性做出反应。
    • 追踪函数:是computed表达式、observer组件的render()和when、reaction 和anturun的第一个入参函数
    • 过程:只追踪哪些在函数执行时被读取的observable
    • 读取:对象属性的间接引用,可用. 或 [] 形式完成
  • 只追踪同步地访问数据
  • MobX 只会为数据是直接通过 render 存取的 observer 组件进行数据追踪
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 不接受参数,避免创建额外组件
    const MyComponent = ({ message }) =>
    <SomeContainer
    title = {() =>
    <Observer>
    {() => <div>{message.title}</div>}
    </Observer>
    }
    />
    message.title = "Bar"
  • 避免在本地字段中缓存 observable
  • 不会有反应的
    • 从observable获取的值,但是在追踪函数之外
    • 在异步调用的代码块中读取的observable

Action

用法

- action(fn)
- action(name, fn)
- @action classMethod() {}
- @action(name) classMethod () {}
- @action boundClassMethod = (args) => { body }
- @action(name) boundClassMethod = (args) => { body }
- @action.bound classMethod() {}
    - action.bound 不要和箭头函数一起使用;箭头函数已经是绑定过的并且不能重新绑定。
  • 使用
    • 应该永远只对修改状态的函数使用动作。 只执行查找,过滤器等函数>应该被标记为动作

编写异步 Actions

  • @action 只对当前运行的函数作出反应,而不会对当前运行函数所调用的函数(不包含在当前函数之内)作出反应

  • runInAction(name?, thunk)

    • 接收代码块并在异步动作中执行。对于即时创建和执行动作非常有用
  • promise

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    mobx.configure({ enforceActions: true }) // 不允许在动作之外进行状态修改
    class Store {
    @observable githubProjects = []
    @observable state = "pending"
    @action
    fetchProjects() {
    this.githubProjects = []
    this.state = "pending"
    fetchGithubProjectsSomehow().then(this.fetchSuccess, this.fetchError)
    }
    // 内联方法,缺点不能进行类型推导
    action("fetchSuccess", projects => {
    const filteredProjects = somePreprocessing(projects)
    this.githubProjects = filteredProjects
    this.state = "done"
    })
    action("fetchError",(error) => {
    this.state = "error"
    })
    }
  • async/await

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    mobx.configure({ enforceActions: true }) // 不允许在动作之外进行状态修改
    class Store {
    @observable githubProjects = []
    @observable state = "pending"
    @action
    async fetchProjects() {
    this.githubProjects = []
    this.state = "pending"
    try {
    const projects = await fetchGithubProjectsSomehow()
    const filteredProjects = somePreprocessing(projects)
    // await 之后,再次修改状态需要动作:
    runInAction(() => {
    this.state = "done"
    this.githubProjects = filteredProjects
    })
    } catch (error) {
    runInAction(() => {
    this.state = "error"
    })
    }
    }
    }
  • flows

  • 使用 function * 来代替 async,使用 yield 代替 await

  • 其它与async/await差不多,只能作为函数使用,不能作为装饰器

工具函数

toJS(value, options?)

  • 作用:递归地将一个observable对象转换成javascript解构。支持observable数组、对象、映射和原始类型
  • options:
    • exportMapsAsObjects 是否将observable映射序列化为对象(true) 或JavaScript Map对象(false).默认true
    • detectCycles 如果监测到循环,则重现使用已经序列化的对象。可以防止无限递归。默认true

extendObservable(target, properties, decorators?, options?)

  • 作用:用来向已存在的目标对象添加observable属性。属性映射中的所有键对都会导致目标上的新的observable属性被初始化为给定值。属性映射中的任意getters都会转化成计算属性。
  • 使用deep:false使得新的属性变成浅的。即阻止它们的值自动转换成observables。
  • observable.object(object) 实际上是 extendObservable({}, object) 的别名

创建observable数据结构和reactions

  • Atoms可以用来通知MobX某些observable数据源被观察或发生了改变。当数据源被使用或不再使用时,MobX会通知atom
  • 多数场景下,可以通过创建普通的observable并使用工具onBecomeObserved 或 onBecomeUnobserved在Mobx开始追踪observable时接收通知来避免创建atom

Intercept & Observe

  • 作用:用来监测单个observable的变化。 intercept可以在变化作用域observable之前监测和修改变化。observe允许在observable变化之后拦截改变。

intercept(target,propertyName?, interceptor)

  • target: 监测的observable
  • propertyName: 可选参数,用来指定某个属性进来拦截。注意,intercept(user.name, interceptor) 和 intercept(user, “name”, interceptor) 根本是完全不同的。前者尝试给 user.name(或许根本不是一个 observable) 里面的当前值添加一个拦截器,而后者拦截 user 的 name 属性的变化。
  • interceptor: 在每次变化作用于 observable 后调用的回调函数。接收一个用来描述变化的对象。

Observe(target, propertyName?, listener, invokeImmediately?)

  • target: 观察的 observable
  • propertyName: 可选参数,用来指定某个属性进行观察。注意,observe(user.name, listener) 和 observe(user, “name”, listener) 根本是完全不同的。前者观察 user.name(或许根本不是一个 observable) 里面的当前值,而后者观察 user 的 name 属性。
  • listener: 在每次变化作用于 observable 后调用的回调函数。接收一个用来描述变化的对象,除了装箱的 observable,它调用listener 的两个参数: newValue、oldValue
  • invokeImmediately: 默认是 false。如果想直接使用 observable 的状态(而不是等待第一次变化)调用 listener 的话,把它设置为 true。不是所有类型的 observable 都支持。
  • 该函数返回一个disposer函数,当调用时取消观察者。
  • observe 会响应对应的变动,而像是 autorun 或 reaction 则会对新值做出响应
其他文章
cover
Next --- React 开发框架
  • 24/11/01
  • 16:52
  • 前端工程化工具
cover
设计模式
  • 24/11/01
  • 16:52
  • JavaScript
目录导航 置顶
  1. 1. 核心概念
  2. 2. 优点
  3. 3. 要点
  4. 4. observable
    1. 4.1. 用法
    2. 4.2. decorators
  5. 5. (@)computed
    1. 5.1. @computed 与 autorun 的区别
    2. 5.2. getter 与 setter
    3. 5.3. computed(expression) 函数(不常用)
    4. 5.4. 第二参数对象的可选参
    5. 5.5. 内置比较器
  6. 6. Autorun(自定义reactions)
  7. 7. When(自定义reactions)
    1. 7.1. when-promise
  8. 8. Reaction(自定义reactions)
    1. 8.1. 用法解析
    2. 8.2. options可选参
  9. 9. @observer
    1. 9.1. 使用 inject 将组件连接到提供的 stores
    2. 9.2. componentWillReact(生命周期钩子)
  10. 10. MobX 会对什么作出反应?
  11. 11. Action
    1. 11.1. 用法
    2. 11.2. 编写异步 Actions
  12. 12. 工具函数
    1. 12.1. toJS(value, options?)
    2. 12.2. extendObservable(target, properties, decorators?, options?)
    3. 12.3. 创建observable数据结构和reactions
    4. 12.4. Intercept & Observe
    5. 12.5. intercept(target,propertyName?, interceptor)
    6. 12.6. Observe(target, propertyName?, listener, invokeImmediately?)
请输入关键词进行搜索