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. struts2远程命令执行漏洞S2-045

    Apache Struts2最新漏洞(CVE-2017-5638,S02-45) struts2远程命令执行漏洞S2-045 Apache Struts 2被曝存在远程命令执行漏洞,漏洞编号S2-04 ...

  2. P1378 油滴扩展 dfs回溯法

    题目描述 在一个长方形框子里,最多有N(0≤N≤6)个相异的点,在其中任何一个点上放一个很小的油滴,那么这个油滴会一直扩展,直到接触到其他油滴或者框子的边界.必须等一个油滴扩展完毕才能放置下一个油滴. ...

  3. mac 命令操作

    内核空间和用户空间 x86架构中将内核地址空间划分三部分:ZONE_DMA.ZONE_NORMAL和 ZONE_HIGHMEM. 自动加载(开机自动启动服务) ln -sfv /usr/local/o ...

  4. centos7如何添加开机启动服务/脚本

    一.添加开机自启服务 在centos7中添加开机自启服务非常方便,只需要两条命令(以Jenkins为例): systemctl enable jenkins.service #设置jenkins服务为 ...

  5. Kmeans:利用Kmeans实现对多个点进行自动分类—Jason niu

    import numpy as np def kmeans(X, k, maxIt): numPoints, numDim = X.shape dataSet = np.zeros((numPoint ...

  6. hdu 2181 哈密顿绕行世界问题【DFS】

    题目链接 题目大意: Problem Description 一个规则的实心十二面体,它的 20个顶点标出世界著名的20个城市,你从一个城市出发经过每个城市刚好一次后回到出发的城市.    Input ...

  7. jupyter notebook不能选择虚拟环境的解决方法

    今天使用dlib没有py37版本,因此创建了Version_36虚拟环境.但jupyter notebook默认的内核找不到新建的虚拟环境,解决方法是需要安装两个包: × ipykernel × nb ...

  8. python查看对象用法

    python查看类用法: dir(object_name)

  9. 基于netty的websocket例子

    nettyServer package com.atguigu.netty.websocket; import javax.annotation.PostConstruct; import org.s ...

  10. JVM之基本结构

    1. Java虚拟机的架构 1.1 Java的NIO库允许Java程序使用直接内存,访问直接内存的速度优于Java堆.出于性能的考虑,读写频繁的场合会考虑使用直接内存. 1.2 本地方法栈和Java栈 ...