cp from : https://blog.csdn.net/smk108/article/details/84960159

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

定义状态并使其可观察(state)
对状态改变的响应(computed、autorun、reaction、observer、when)
改变状态(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的使用会在后面介绍。
---------------------
作者:smk108
来源:CSDN
原文:https://blog.csdn.net/smk108/article/details/84960159
版权声明:本文为博主原创文章,转载请附上博文链接!

[Web 前端] mobx教程(二)-mobx主要概念的更多相关文章

  1. 推荐20个很有帮助的 Web 前端开发教程

    在平常的搜索中,我碰到过很多有趣的信息,应用程序和文档,我把它们整理在下面这个列表.这是收藏的遇到的有用内容的一个伟大的方式,可以在你需要的时候方便查阅.相信你会在这个列表中发现对你很有用的资料. 您 ...

  2. Web前端入门教程之浏览器兼容问题及解决方法

    JavaScript 被称为JS,是作为浏览器的内置脚本语言,为我们提供操控浏览器的能力,可以让网页呈现出各种特殊效果,为用户提供友好的互动体验.JS是Web前端入门教程中的重点和难点,而浏览器兼容性 ...

  3. 推荐20个很有帮助的web前端开发教程

    1. CSS Vocabulary 一个伟大的指向和点击的小应用程序,让你加快速度掌握 CSS 语法的各个不同部分,学习各个属性的正确的名称. 2. Liquidapsive 一个简单的信息化布局,通 ...

  4. 推荐20个非常有帮助的web前端开发教程

    1. CSS Vocabulary 一个伟大的指向和点击的小应用程序,让你加高速度掌握 CSS 语法的各个不同部分,学习各个属性的正确的名称. 2. Liquidapsive 一个简单的信息化布局.通 ...

  5. web前端该怎么入门?web前端入门教程(非常详细)

    初学编程的小伙伴经常会遇到的问题,1.没资源 2.没人带 3.不知道从何开始 ,小编也是从新手期过来的,所以很能理解萌新的难处,现在整理一些以前自己学习的一些资料送给大家,希望对广大初学小伙伴有帮助! ...

  6. [web 前端] mobx教程(一)-mobx简介

    opy from : https://blog.csdn.net/smk108/article/details/84777649 Mobx是通过函数响应式编程使状态管理变得简单和可扩展的状态管理库.M ...

  7. web前端开发教程系列-2 - 前端开发书籍分享(转)

    目录: 前言 一. CSS 二. JavaScript 三. jQuery 四. 后记   前言 前端书籍在每个商城或书架上面都是琳琅满目,很多初学者又不能很好的判断书的质量或层次.因为今天给同学们分 ...

  8. web前端开发教程系列-2 - 前端开发书籍分享

    目录: 前言 一. CSS 二. JavaScript 三. jQuery 四. 后记   前言 前端书籍在每个商城或书架上面都是琳琅满目,很多初学者又不能很好的判断书的质量或层次.因为今天给同学们分 ...

  9. web前端开发教程系列-4 - 前端开发职业规划

    前言 关于我:小天 1). 架构师,项目经理,产品经理 2). 中间件研发 3). VPCC 云计算基础平台管理 4). 智慧旅游 5). 智慧教育 6). 一次失败的创业体验(爱邂逅网) 一. 在开 ...

随机推荐

  1. python函数式编程——返回函数

    1.函数作为返回值 高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回. 2.闭包 注意到返回的函数在其定义内部引用了局部变量args,所以,当一个函数返回了一个函数后,其内部的局部变量还 ...

  2. Coolpy网络部署说明(局域网)

    本文将介绍Coolpy第一种方案的网络部署方法.以方便大家学习如何让coolpy设备部署到相应的应用场景中.本例将以水星MW310R无线路由器作为演示路由器. 1.局域网部署 即coolpy设备=&g ...

  3. ELK日志分析平台环境部署 (yum安装)

    前言:通常体质被分散存储在不同的设备上面,在庞大的服务器集群中,我们需要集中化的管理,日志的统计和检索,一般我们使用grep和awk,wc等linux命令虽然能够实现检索和统计,但是呢,对于要求更高的 ...

  4. Java中常见的排序方式-选择排序(升序)

    [基本思想] 假设数组为int[] a = { 49, 38, 65, 97, 76, 13, 27 },数组元素个数为7个. 第1轮比较:先是a[0]与a[1]比较,大于则先交换,再比较a[0]和a ...

  5. Idea问题:“marketplace plugins are not loaded”解决方案

    博主本人遇见该问题时是想要通过Idea的plugins工具下载阿里巴巴的代码规约工具 但是在我点开settings,然后打开plugins工具时竟然给我提示“marketplace plugins a ...

  6. HDU 3416 Marriage Match IV 【最短路】(记录路径)+【最大流】

    <题目链接> 题目大意: 给你一张图,问你其中没有边重合的最短路径有多少条. 解题分析: 建图的时候记得存一下链式后向边,方便寻找最短路径,然后用Dijkstra或者SPFA跑一遍最短路, ...

  7. ZOJ 1109 Language of FatMouse 【Trie树】

    <题目链接> 题目大意: 刚开始每行输入两个单词,第二个单词存入单词库,并且每行第二个单词映射着对应的第一个单词.然后每行输入一个单词,如果单词库中有相同的单词,则输出它对应的那个单词,否 ...

  8. hdu 1237 简单计算器 (表达式求值)【stack】

    <题目链接> 题目大意: 读入一个只包含 +, -, *, / 的非负整数计算表达式,计算该表达式的值.  Input测试输入包含若干测试用例,每个测试用例占一行,每行不超过200个字符, ...

  9. datatables跳转自定义页面(后端分页)

    在后端分页的情况下,怎么做到跳转自定义页面? 0x01 难点: 一. 怎么添加自定义代码? 前提:datatables在整个html加载完毕后,进行datatables数据的渲染,并且把右下角的 “上 ...

  10. 002.Zabbix简介

    一 Zabbix简介 1.1 概述 Zabbix是一个企业级的高度集成开源监控软件,提供分布式监控解决方案.可以用来监控设备.服务等可用性和性能. 1.2 所支持监控方式 目前由zabbix提供包括但 ...