通过《Mobx系列(一)-Mobx简介》我们简单理解了Mobx的基本原理及流程,使用Mobx实现一个响应式的应用,主要分为三个步骤:

  1. 定义状态并使其可观察(state)
  2. 对状态改变的响应(computed、autorun、reaction、observer、when)
  3. 改变状态(action)

下面从这三个步骤的顺序介绍Mobx的主要概念。(版本为5.X、使用es7装饰器)

1、可观察状态

状态是驱动应用的数据,是应用的核心。状态可以是与应用相关的各种数据,通常有像待办事项列表这样的领域特定状态,还有像当前已选元素的视图状态,当然,你也可以向Redux类似,将state分为三类:与领域直接相关的领域状态数据,反应用户行为的应用状态数据、视图相关的UI状态数据。使用Mobx一般还会创建一个或多个store用来管理state。

Mobx提供了observable和@observable两个API用来定义可观察的state。

  • observable(value)
  • @observable classProperty = value

可以使用这两个API定义可观察state的数据类型可以是JS基本数据类型、引用类型、普通对象、类实例、数组和映射。observable对不同类型有不同的转换规则,详细解释如下:

注:下文提到的会触发更新都是指受其影响的computed value(被依赖)和reaction都会自动更新。

(1)JS基本数据类型

JavaScript 中的所有原始类型值都是不可变的,因此值并不会是可观察的,Mobx会将包含值的属性转换成可观察的。可以如下定义:

@observable count = 1;
@observable city = 'shanghai';

当再改变count和city的值时,会触发更新。

(2)Array

如果 value 是数组,会返回一个 Observable Array。

@observable list = ['a', 'b', 'c', 'd'];

对于对象数组,observable也会递归地作用于数组的每个元素对象,其元素对象的属性也会是可观察的。

@observable todos = [{'id': 1, 'taskName': 'task1', 'finished': true},{'id': 2, 'taskName': 'task2', 'finished': false},{'id': 3, 'taskName': 'task3', 'finished': true},{'id': 4, 'taskName': 'task4', 'finished': false},{'id': 5, 'taskName': 'task5', 'finished': true},{'id': 6, 'taskName': 'task6', 'finished': false},
];

当数组元素的增加、减少或者元素对象的属性改变时,会触发更新。

(3)普通对象

对于没有原型或原型是Object.prototype的普通对象,那么对象会被克隆并且所有的属性都会被转换成可观察的。

{'id': 1, 'taskName': 'task1', 'finished': true}

Id、taskName、finished都会是可观察的,这些属性值的改变都会触发更新。

如果属性的值是一个普通对象,会继续被observable处理,将其属性转换为可观察的,一直如此递归执行。

'id': 1, 'taskName': 'task1', 'finished': {'part1': true, 'part2':false}}

part1和part2也会是可观察的,其值的修改依然会触发更新。

如果以后给可观察属性再赋值是一个普通对象时,新的普通对象值的属性也将被转换为可观察的。

若一个可观察对象的值为:

{'id': 1, 'taskName': 'task1', 'finished': true}

之后被修改为

{'id': 1, 'taskName': 'task1', 'finished': {'part1': true, 'part2':false}}

part1和part2依然是可观察的,其值的修改依然会触发更新。

对于5.x版本,以后新增加的属性也会是可观察的,4.x及以下版本是不可观察的。

若一个可对象的值为:

{'id': 1, 'taskName': 'task1', 'finished': true

后面被追加一个属性

{'id': 1, 'taskName': 'task1', 'finished': true, owner: admin}

后添加的属性owner在5.x版本依然是可观察的。

我测试中发现删除对象的属性是不会触发更新的。

(4)Map

对于ES6的Map : 会返回一个新的 Observable Map。Map对象的每一个元素都是可观察的,并且向Map对象中添加和删除新的元素也是可观察的。

@observable task = new Map([['taskName', 'tank1'],['finished', true]]);

再执行

this.task.set('owner', 'admin');

owner也是可观察的,修改会触发更新。

(5)非普通对象

非普通对象是指构造函数为自定义函数的对象,非普通对象的属性不会受observable的影响而转换为可观察的,如果需要将非普通对象的属性转换为可观察的,需要在其自定义的构造函数中进行处理。例如:

class User{@observable name;@observable role;constructor(name, role){this.name = name;this.role = role;}
}
user = new User('admin', '管理员');

之后在对user实例的name或role进行修改,是会触发更新的。

注:对js基本数据类型和非普通对象,observable返回的其实是一个特殊的boxed values类型的可观察对象,保存的是一个指向原基本数据类型或对象的引用,这个引用是可观察的。

2、对状态改变的响应

本段介绍的是Mobx中的衍生(derivation),derivation是指可以从state中衍生出来的任何东西,可以是值(computed value)或者动作(autorun、reaction等)。Derivation可以自动响应state的变化而进行更新或执行有副作用的函数。

(1)Computed

计算值(computed values)是可以根据现有的状态或其它计算值衍生出的值,应该由纯函数产生,计算值可以被其它 observer 使用,例如被ui使用。

Mobx提供了computed和@computed用于定义计算值:

attr = computed(() => value);
@computed get attr(){return value;
}

用法如下:

@observable list = ['a', 'b', 'c', 'd'];
@computed get listLength(){return this.list.length;
}
  • 计算值依赖的observable没有改变,计算值不会更新
  • 计算值采用延迟更新策略,当有其它计算值或reaction在使用时,才会计算
  • 计算值不在被使用时,默认会被回收,也不再更新
  • 计算值可以帮助实现实际可修改的状态尽可能的小,又有上述优化,可以尽量多的使用

(2)autorun

当你想创建一个永远不会被观察的响应式函数时,可以使用autorun。

disposer = autorun(() => {sideEffect});

当autorun依赖的状态变化时会自动执行参数的function,返回值disposer是autorun的清除函数,当不再需要autorun时,可以调用disposer清除autorun。

autorun参数中的函数在使用autorun时会立即执行一次,然后在每次它依赖的state发生变化时自动执行一次。

用法举例:

disposer = autorun(() => {console.log(`autorun : Now the active ID is ${this.activeItem }`);
});
  • autorun不会产生新值,是基于执行函数产生效果
  • autorun不会被观察,是反应式代码桥接到命令式代码的方式
  • 可用于打印日志、更新UI的代码、发起请求
    • autorun第一个参数是函数,可接受第二个参数,处理delay、name、error等

(3)reaction

reaction是autorun的变种,第一个参数为数据函数,第二个参数为效果函数,数据函数返回一个值,作为效果函数的参数,效果函数不会对依赖的状态作出反应,只有数据函数返回的值变化时才会执行。

reaction = reaction(() => data, (data, reaction) => { sideEffect })

用法举例:

@observable todos = [{'id': 1, 'taskName': 'task1', 'finished': true},{'id': 2, 'taskName': 'task2', 'finished': false},{'id': 3, 'taskName': 'task3', 'finished': true},{'id': 4, 'taskName': 'task4', 'finished': false},{'id': 5, 'taskName': 'task5', 'finished': true},{'id': 6, 'taskName': 'task6', 'finished': false},
];
reaction = reaction(() => this.todos.filter(item => item.finished === false), notFinished => {if(notFinished.length === 0){console.log('All tasks have been completed, and autorun and reaction are no longer executed.');this.disposer();this.reaction();}else {console.log(`reaction : Now the active ID is ${this.activeItem}`);}
});

在上面的例子中,reaction的第一个参数会为第二个参数提供notFinished,然后第二个参数是效果函数,会执行打印日志的操作,当todo都已经完成时,会调用disposer和reaction清除auto和reaction。

  • reaction 返回一个清理函数,不需要再执行时应该调用清理函数
  • 数据函数对state的改变作出反应
  • 效果函数仅对数据函数中访问的数据作出反应,不对state的改变作出反应
  • autorun第一个参数是函数,可接受第二个参数,处理delay、name、error等

(4)observer(其实也是autorun)

observer 函数/装饰器可以用来将 React 组件转变成响应式组件,observer 是由单独的 mobx-react 包提供。

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

@observer class MyComponent extends React.Component{...}

用法举例:

import React, {Component} from 'react';
import {inject, observer} from 'mobx-react';
import {Button} from 'antd';
import Timer from '../Timer';
import './style.css';@inject( 'userStore')
@observer
export default class User extends Component{constructor(props){super(props);this.state = {};}render(){const {user} = this.props.userStore;return(<div className='user'><div className='user_list'>name:{user.name}</div><div className='user_list'>role:{user.name}</div><div className='user_list'>{user.isGuest ? `isGuest:${user.isGuest}` : ''}</div><Button type='primary' onClick={() => this.props.userStore.changeUser()}>Change User</Button><Timer /></div>);}
}
  • observer 需要组合其它装饰器或高阶组件时, observer需要是是最深处(第一个应用)的装饰器,否则可能不能正常更新。
  • 响应式组件没有或很少有状态,因为在与其他组件共享的对象中封装(视图)状态通常更方便。但你仍然可以自由地使用状态。
  • @observer 以和 PureComponent 同样的方式实现了 shouldComponentUpdate,因此子组件可以避免不必要的重新渲染。
  • 响应式组件单方面加载数据,即使子组件要重新渲染,父组件也不会进行不必要地重新渲染。

(5)when

when(() => condition, () => {sideEffect})

when会自动响应它使用state的变化,也就是会观察并运行第一个参数,直到condition返回true。 一旦返回 true,给定的sideEffect就会被执行,且只会执行一次,然后 autorunner(自动运行程序) 会被清理。 该函数返回一个清理器以提前取消自动运行程序。

用法举例:

constructor(){when(() => this.hasNotFinished.length === 0, () => {this.disposer();this.reaction();});
}
@computed get hasNotFinished(){return this.todos.filter(item => item.finished === false);
}
  • when非常适合用在以响应式的方式执行取消或清除逻辑的场景
  • when最多执行一次
    • when返回一个清理器,可以在需要时提前清除

(6)mobx对什么作出反应

MobX 会对在追踪函数执行过程读取现存的可观察属性做出反应。

  • “读取” 是对象属性的间接引用,可以用过 . (例如 user.name) 或者 [] (例如 user['name']) 的形式完成。
  • “追踪函数” 是 computed 表达式、observer 组件的 render() 方法和 when、reaction 和 autorun 的第一个入参函数。
  • “过程(during)” 意味着只追踪那些在函数执行时被读取的 observable 。这些值是否由追踪函数直接或间接使用并不重要。

MobX 追踪属性访问,而不是值。

Mobx不会对可观察状态的改变作出反应举例:

@observable message = {title: "Foo",author: {name: "Michel"},likes: ["John", "Sara"]
}

如上message对应的可观察图示:

  • 在追踪函数外进行间接引用
ar title = message.title;
autorun(() => {console.log(title)
})
message.title = "Bar"
  • MobX 只追踪同步地访问数据
autorun(() => {setTimeout(() => console.log(message.likes.join(", ")),10)
})
message.likes.push("Jennifer");

在 autorun 执行期间没有访问到任何 observable,而只在 setTimeout 执行期间访问了。

  • MobX 只会为数据是直接通过 render 存取的 observer 组件进行数据追踪

React中使用mobx,只会对render中直接存取的可观察状态进行跟踪

像上述将值传递给子组件和将可观察状态值缓存到组件内是不会对状态的变化有反应的。

3、改变状态

动作是任何用来修改状态的函数,且应该永远只对修改状态的函数使用动作。

在mobx中可以在任意位置修改状态,但是推荐使严格模式,在严格模式下,只能在action中修改状态。

Mobx提供action和@action包装action函数

Mobx提供action.bound和@action.bound帮助bind this

@action classMethod() {}
@action(name) classMethod () {}
@action(name) boundClassMethod = (args) => { body }
@action.bound classMethod() {}

用法举例:

class commonStoreClass {@observable time = '';@action updateTime(time){this.time = time;}@action.bound computedTime(){const nowTime=new Date();const year=nowTime.getFullYear();const month=nowTime.getMonth()+1;const date=nowTime.getDate();const time = year+"年"+month+"月"+date+" "+nowTime.toLocaleTimeString();this.updateTime(time);}@action startTime(){this.timer = setInterval(this.computedTime,1000);}@action stopTime(){clearInterval(this.timer);}
}
  • 函数内多个修改状态的操作会被合并,全部修改完后会通知computed和reaction
  • 应该永远只对修改状态的函数使用动作。 只执行查找,过滤器等函数应该被标记为动作
  • 推荐使严格模式,在严格模式下,只能在action中修改状态。
  • 注意在需要时bind this

4、各api的import

上面提到的api中与React相关的由mobx-react提供,其余的由mobx包提供。

import {observable, action, computed, autorun, reaction, when} from 'mobx';
import {Provider, inject, observer} from 'mobx-react';

Provider与inject的使用会在后面介绍。

mobx系列(二)-mobx主要概念相关推荐

  1. [Web 前端] mobx教程(二)-mobx主要概念

    cp from : https://blog.csdn.net/smk108/article/details/84960159 通过<Mobx教程(一)-Mobx简介>我们简单理解了Mob ...

  2. mobx系列(一)-mobx简介

    Mobx是通过函数响应式编程使状态管理变得简单和可扩展的状态管理库.Mobx和Redux一样,采用单向数据流管理状态:通过action改变应用的state,state的改变触发相应ui的更新,如下图所 ...

  3. PowerDesigner教程系列(四)概念数据模型

    PowerDesigner教程系列(四)概念数据模型 原文http://www.cnblogs.com/yxonline/archive/2007/04/11/708271.html 目标: 本文主要 ...

  4. 图层几何学 -- iOS Core Animation 系列二

    <图层树和寄宿图 -- iOS Core Animation 系列一>介绍了图层的基础知识和一些属性方法.这篇主要内容是学习下图层在父图层上怎么控制位置和尺寸的. 1.布局 首先看一张例图 ...

  5. 【JAVA编码】 JAVA字符编码系列二:Unicode,ISO-8859,GBK,UTF-8编码及相互转换

    http://blog.csdn.net/qinysong/article/details/1179489 这两天抽时间又总结/整理了一下各种编码的实际编码方式,和在Java应用中的使用情况,在这里记 ...

  6. Tomcat源码解析系列二:Tomcat总体架构

    Tomcat即是一个HTTP服务器,也是一个servlet容器,主要目的就是包装servlet,并对请求响应相应的servlet,纯servlet的web应用似乎很好理解Tomcat是如何装载serv ...

  7. 区块链教程(二):基础概念介绍

    注:本教程为技术教程,不谈论且不涉及炒作任何数字货币 本系列重点在于以太坊基础知识.以太坊客户端以及以太坊solidity编程,因此博客重点在于以太坊核心知识点的掌握,区块链部分的基础知识可以作为补充 ...

  8. Linux设备模型剖析系列一(基本概念、kobject、kset、kobj_type)

    CSDN链接: Linux设备模型剖析系列一(基本概念.kobject.kset.kobj_type) Linux设备模型剖析系列之二(uevent.sysfs) Linux设备模型剖析系列之三(de ...

  9. 进入BeOS的花花世界 系列二

    进入BeOS的花花世界 系列二      BeOS 完全瞄准多媒体            ⊙记者张子强/专题报导             时间回到西元一九九O,苹果电脑Macintosh 麦金塔刚满六 ...

最新文章

  1. jquery 前台分页插件总结(1 前台假分页 2 后台分页)
  2. LeetCode:Minimum Depth of Binary Tree,Maximum Depth of Binary Tree
  3. Hadoop 同步集群时间ntp
  4. 微软Visual Studio 2019版本16.3 正式发布,支持 .NET Core 3.0
  5. reactrouter监听路由变化_一篇文章搞懂前端路由原理解析和实现方式
  6. Mcad学习笔记之异步编程(AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用小总结)...
  7. JAVA中的静态成员
  8. google浏览器不能登录
  9. bzoj 1930: [Shoi2003]pacman 吃豆豆 [费用流]
  10. Delphi 2009 的反射单元(ObjAuto):
  11. 你能给我的手机推荐一款安全软件吗?
  12. linux下打印pdf文件很慢,使用adobe Reader PDF 双面打印/ 福晰pdf阅读器打印速度慢
  13. Abaqus有限元分析软件介绍
  14. 联创机房管理系统服务器密码,高校机房管理系统解决方案.doc
  15. 用excel将有规律的数据随机打乱
  16. 福布斯中国奢侈品市场调查
  17. iOS判断当前是否为模拟器
  18. 网站网页快速变灰方法
  19. Linux替换Docker镜像源
  20. 看这你已经赢麻了,勿以赢小而不麻,2022软件测试八股文最全音

热门文章

  1. 支付宝沙箱支付demo详细教程(idea版)
  2. 测试进阶篇之测试方法的分类(二)
  3. Acrel-7000企业能源管控平台在浙江春风动力股份有限公司的应用
  4. 如何基于任务的价值确定其优先级
  5. TestDataAnalysis软件StdfToCSV转换使用说明
  6. 抖音昵称html,抖音最近流行这种符号,放昵称超好看!
  7. 拱拱Lite开发(3):三翼页及湘大文库下载实现(解析网页获取信息及模拟登陆)
  8. 使用所学Spring知识,实现简易的图书查询系统功能。实现查询全部图书。 根据书籍编号查询信息。 根据书名查询书籍信息。 根据状态查询书籍信息。
  9. CSS3新增的相关属性
  10. 数列的组合及排列方式java_java数组排列组合