mobx入门教程

58 篇文章 0 订阅
9 篇文章 0 订阅

背景

React的自身的状态本身由statesetState维护;但是随着应用复杂度的提升(组件间状态共享及状态变更),单纯通过setState进行状态管理的方案不仅变得代码复杂、难以维护,而且影响可扩展性;

针对这种状况,React目前主要有Reduxmobx两种解决方案(均可以让状态逻逻辑从展示组件等解耦出来,提高扩展性与复用性);


mobx与Redux有什么不同?

  1. Redux采用FP原则,核心为actionstorereducer,采用单一数据源,状态不可变更(总是返回一个新状态),使用纯函数进行状态变更;

    Redux更新时总是会返回一个新的state(redux底层的combineReducers会通过比较对象的引用来判断是否同一个对象,如果是的话则继续使用旧的state,即不会有任何UI更新,否则执行更新操作)

    为什么redux要求不可变性 ?

    1. 浅比较:redux的combineReducers及react-redux的connect方法
      生成的组件在比较根组件state与mapStateToProps函数返回值以确定是否更新时,都用到了浅比较;浅比较的前提是数据不可变;
    2. 不可变数据的管理极大地提升了数据处理的安全性。

    3. 进行时间旅行调试要求 reducer 是一个没有副作用的纯函数,以此在不同 state 之间正确的移动。

    什么是时间旅行?

    在 2015 年的 React Europe 会议上,Dan Abramov 展示了通过 Redux DevTools 让开发者在历史状态中自由穿梭, 提升调试体验

    时间旅行主要用于事件驱动的应用,比如富文本编辑器的状态回撤、游戏进度的读取和保存等等;具体参考从时间旅行的乌托邦,看状态管理的设计误区

    (state, action) => newState
    
  2. mobx采用OOP原则,也是响应式编程;采用observableobserver的概念,observable中将对象变为被观察对象,可以通过action等变更状态;observer定义观察对象,可以观察被观察对象的变更并应用;mobx状态是可变的,更加轻量级,入门比Redux简单;

    需要注意的是,MobX 在重绘时的性能优势是以访问劫持后更大的内存占用为代价的;(开箱即用的mobx与优化后的redux性能相当)

  3. 要点比较
    在这里插入图片描述
    在这里插入图片描述

Mobx概述

mobx支持单向数据流,由原始状态衍生的值都会自动变更,无法观察到中间值;

  1. 创建应用模型,定义状态使其可观察(建立store,即被观察对象)
  2. 创建视图以响应状态的变化(创建观察者,检测并响应状态变化)
  3. 更改状态

环境配置

mobx使用装饰器特性的话需要开启ES7的decorator属性;可以安装插件@babel/plugin-proposal-decorators"package.json中添加以下配置:

  "babel": {
    "plugins": [
      [
        "@babel/plugin-proposal-decorators",
        {
          "legacy": true
        }
      ]
    ],
    "presets": [
      "react-app"
    ]
  }

否则,可以借助mobx API —— decorate(object, decorators)实现部分功能


Mobx API —— observable相关

  1. 描述:observable用于创建被观察对象(定义可观察状态),需要注意的是被观察对象总是类的属性而不是值

  2. 用法:

    // 用法一, 会自动递归到整个对象
    observable(value)
    
    // 用法二 是extendObservable(this, { property: value})的语法糖
    @observable classProperty = value
    
    // demo
    import { observable, computed } from "mobx";
    
    class Order {
        @observable price = 0;
        @observable count = 1;
    
        @computed get total() {
            return this.price * this.count;
        }
    	
    	@action.bound
    	increasePriice() {
    		this.price++;
    	}
    }
    
    // demo2 —— 使用decorate重写demo
    import { observable, computed, decorate } from 'mobx';
    
    class Order {
    	price = 0;
    	count = 1;
    
    	get total() {
    		return this.price * this.count;
    	}
    	
    	increasePriice() {
    		this.price++;
    	}
    }
    
    decorate(Order, {
    	price: observable,
    	count: observable,
    	total: computed,
    	increasePrice: action.bound
    })
    

Mobx API —— 对observable做出响应

1. computed——计算值

  • 含义:computed可以根据现有的状态或其他计算值衍生出新的值,并且这些值可以被observer使用;autorun主要适用于产生一个副作用而不是一个新值,如日志打印和网络请求等;

  • 要点:
    computed是响应式的,如果前一个依赖计算的值未发生变化,计算属性不会重新运行,这种情况下它会被暂停(自动暂停特性)。同时,如果一个计算属性不再被观察,其将会自动进行垃圾回收。

    不要把 computed 和 autorun 搞混。它们都是响应式调用的表达式,但是,如果你想响应式的产生一个可以被其它 observer 使用的值,请使用 @computed,如果你不想产生一个新值,而想要达到一个效果,请使用 autorun。 举例来说,效果是像打印日志、发起网络请求等这样命令式的副作用。

  • 用法:

    // 用法一:函数形式
    computed(expression)
    // demo
    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"); // 输出: 'DAVE'
    
    // 用法二:装饰器。在任意属性的getter上使用
    @computed get propertyName() {
    	return
    }
    
    

2. autorun——自定义反应

  • 含义:如果创建一个函数,本身不会有观察者,并且其不产生新值而是用于产生一个副作用(网络请求/日志打印/更新UI)等时,需要使用autorun

  • 要点:
    computed(function) 创建的函数只有当它有自己的观察者时才会重新计算,否则它的值会被认为是不相关的。而autorun创建后会立即运行一次;

  • 用法
    第一个参数一般为函数(如果是字符串,将被用作调试名),第二个参数为可选的对象参数,包括属性:delay(去抖时间)/name(reaction名称)/onError(处理reaction错误)/scheduler(设置自定义调度器决定autorun函数如何重新运行)

    autorun(() => {
    	xxx
    }, { delay: 200 })
    

3. reaction——自定义反应

  • 含义:可以将reaction看作是autorun的变种,为`observable的追踪提供了更加细粒度的控制;

  • 用法:
    第一个为数据追踪函数,第二个为副作用函数(其中的任何observable都不会被追踪);第三个为可选的对象参数;
    只用在数据追踪函数返回新值时副作用函数才会运行;

    reaction(() => data, (data, reaction) => { sideEffect }, options?)
    
  • demo:

    // 只调用一次并清理掉 reaction : 对 observable 值作出反应。
    const reaction3 = reaction(
        () => counter.count,
        (count, reaction) => {
            console.log("reaction 3: invoked. counter.count = " + count);
            reaction.dispose();
        }
    );
    
    counter.count = 1;
    // 输出:
    // reaction 3: invoked. counter.count = 1
    
    counter.count = 2;
    // 输出:
    // (There are no logging, because of reaction disposed. But, counter continue reaction)
    

4. @observer——函数装饰器

  • 含义:可以将React组件转为响应式组件,响应被观察对象的改变;

  • 要点:它用 mobx.autorun 包装了组件的 render 函数以确保任何组件渲染中使用的数据变化时都可以强制刷新组件

  • 用法:

    当 observer 需要组合其它装饰器或高阶组件时,请确保 observer 是最深处(第一个应用)的装饰器,否则它可能什么都不做。

    observer(class Demo ... { })
    
    @observer class Demo extends React.Component {
    	...
    }
    
    const Demo = observer(({ data }) =>
        <span>type: { data.type } </span>
    );
    

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

import { observer, inject} from 'mobx-react';
import React from 'react';

@inject('store')
@observer
class Casual extends React.Component {

    render() {
        return (
            <div>
                <h2>num: {this.props.store.num}</h2>
                <button onClick={this.handleClick}>increment</button>
            </div>
        )
    }

    handleClick = () => {
        this.props.store.increment();
    }
}

export default Casual;


// store.js
import { action, observable, computed, decorate } from 'mobx';


class Store {
    
    @observable num = 0;

    @computed get retNum() {
        return `the value of num is ${this.num}`
    } 

    @action.bound
    increment() {
        this.num++;
    }
}

export default Store;

6. componentWillReact

  1. 含义:当组件因为它观察的数据发生重新渲染时,会触发该生命周期函数;可以追溯渲染并找到导致渲染的操作

  2. 要点:componentWillReact不接受任何参数;初始化渲染时不会被触发;

    @observer 以和 PureComponent 同样的方式实现了 shouldComponentUpdate,因此子组件可以避免不必要的重新渲染

  3. demo:

    import {observer} from "mobx-react";
    
    @observer class TodoView extends React.Component {
        componentWillReact() {
            console.log("I will re-render, since the todo has changed!");
        }
    
        render() {
            return <div>this.props.todo.title</div>;
        }
    }
    

Mobx API —— 改变observables

1. action

  • 含义:用于修改状态;

  • 用法

    action(fn)
    action(name, fn)
    @action classMethod() {}
    @action(name) classMethod() {}
    // @action.bound可以自动绑定到目标对象(即指定了this作用域)
    @action.bound classMethod() {} 
    ...
    
  • action.bound不要和箭头函数一起使用;


参考文献

  1. mobx中文文档
  2. Ten minute introduction to MobX and React
  3. Redux vs MobX without Confusion
  4. Becoming fully reactive: an in-depth explanation of MobX
  5. 为什么 Redux 需要不变性?
  6. Redux 是如何使用浅比较的?
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Neil-

你们的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值