1. 介绍

1.1. 原理

React的render是 状态 转化为树状结构的渲染组件的方法
而MobX提供了一种存储,更新 状态 的方法
React 和 MobX都在优化着软件开发中相同的问题。
React 使用的方法是让虚拟DOM来减少繁琐而沉重的DOM变化。
而MobX则通过一个虚拟的状态依赖图表来让react组件和应用状态同步化来减少不必要的状态导致组件更新

1.2. 安装

MobX:

npm install mobx --save

React bindings:

npm install mobx-react --save

1.3. 要点

MobX看起来很复杂的样子,其实是用它只需要三步

  1. 定义你的状态,让它们成为观察者(observable)
    存储状态(Store state)可以是任何的数据结构,随你定义为:对象,数组,类,循环结构,引用都没所谓。但需要记住一点,就是:随着时间的变化,用MobX 去把它们定义成观察者(observable)
import {observable} from 'mobx'
let appState = observable({timer: 0
})

  1. 我们不需要让appState去观察什么。你现在就能创建视图(view),每当appState的相关数据发生变化的时候,就会自动更新。MobX会采用最优的方式去更新你的视图。以下有一个例子来说明如何使用,其中使用了ES6/ES7的语法(当然MobX也是支持ES5),代码中@的意义
import {observer} from 'mobx-react';
@observer
class TimerView extends React.Component {render() {return (<button onClick={this.onReset.bind(this)}>Seconds passed: {this.props.appState.timer}</button>);}onReset () {//appState.resetTimer会在下一节完成this.props.appState.resetTimer();}
};
React.render(<TimerView appState={appState} />, document.body);

  1. 修改状态
    第三节要说的是修改状态。MobX和其他框架不同,它不会要求你去做什么事情,它只是帮助你去做简单的事情
appState.resetTimer = action(function reset() {appState.timer = 0;
});
setInterval(action(function tick() {appState.timer += 1;
}), 1000);

其中action包装用法只能在strict模式下使用,请记得在你的javascript文件头写上:'use strict'。

2. API

从上面的例子可以看到,MobX的API其实不多:observable, computed, reactions, actions

2.1. observable(value)

其中的value可以是JS原定的数据结构,引用,对象,数组,ES6的map

  1. 如果value是一个map的话,则需要使用一个调节器(modifier)asMap来使用。这时候会返回一个Observable Map
  2. 如果是一个数组,返回Observable Array
  3. 如果是一个没有属性的对象,则返回一个Observable Object
  4. 如果是一个有属性的对象,JS原有的数据结构,函数等,返回一个** Boxed Observable**。MobX不会自动让一个有属性的对象成为观察者。这是这个有属性的对象的构造函数应该做的事情,你可以使用extendObservable在它的构造函数里面,或者在它的类使用@observable去定义。

以下是一些例子:

const map = observable(asMap({ key: "value"}));
map.set("key", "new value");const list = observable([1, 2, 4]);
list[2] = 3;const person = observable({firstName: "Clive Staples",lastName: "Lewis"
});
person.firstName = "C.S.";const temperature = observable(20);
temperature.set(25);

2.2. @observable

import {observable} from "mobx";
class OrderLine {@observable price:number = 0;@observable amount:number = 1;constructor(price) {this.price = price;}//这里在下一节会说到@computed get total() {return this.price * this.amount;}
}
const line = new OrderLine();
console.log("price" in line); // true
//hasOwnProperty:判断一个对象是否有你给出名称的属性或对象。需要注意,此方法无法检查该对象的原型链中是否具有该属性
console.log(line.hasOwnProperty("price")); //false

如果你的环境不支持ES6/7的语法的话,其实@observable key = value; 只是extendObservable(this, { key: value })的语法糖。因此在ES5环境下你也能使用

2.3. (@)computed

Computed values 就像一个算术公式一样去从现有的状态或其他值去计算出需要的值。计算的耗费是不可低估的。Computed尽可能帮你减少其中的耗费。它们是高度优化的,请把它用在可能用到的地方。

不要混淆下一节说到的autorun。虽然他们都是被动调用的表达式。但是……
Computed使用情况:如果你需要产生一个有观察者(observers)参数计算的新的值的时候
autorun使用情况:你不想产生一个新的值就想达到一个新的效果/功能。就像是打log或者进行网络请求
Computed values是自动帮你从你的状态(state)值和其他计算辅助值来计算的。MobX做了很多的优化。当参与计算的值没有发生改变,Computed是不会重新运行。如果参与计算的值没有被使用,Computed values是暂停的。

如果Computed values不再是观察者(observed),那么在UI上也会把它除掉,MobX能自动做垃圾回收。autorun则需要你自己手动去处理。如果参与计算的值不再被使用,是不会缓存Computed的,所以重新计算是需要的。这个是最理想的默认情况。如果你想保留,可以了解一下keepalive和observe。

例子1: 在2.2的例子。@computed get

例子2: @computed set

class Foo {@observable length: 2,@computed get squared() {return this.length * this.length;}set squared(value) { //this is automatically an action, no annotation necessarythis.length = Math.sqrt(value);}
}

需要注意的是:setter并非用于直接改变参数计算的值,如例子中的length。而是作为一个逆推导。

2.4. Autorun

Autorun是用在一些你想要产生一个不用观察者参与的被动调用函数里面。当autorun被使用的时候,一旦依赖项发生变化,autorun提供的函数就会被执行。与之相反的是,computed提供的函数只会在他有自己的观察员(observers)的时候才会评估是否重新执行,否则它的值被认为是无用的。

根据这些经验:如果你需要一个自动运行但却不会产生任何新的值的结果的函数,那么请使用Autorun。其他情况请使用computed。Autorun只是作用于如果达到某个效果或者功能,而不是计算某些值。如果有一个字符串作为第一个参数存入Autorun,那么它将成为一个调试名称。

var numbers = observable([1,2,3]);
var sum = computed(() => numbers.reduce((a, b) => a + b, 0));var disposer = autorun(() => console.log(sum.get()));
// prints '6'
numbers.push(4);
// prints '10'

2.5. @observer

  1. observer 函数/修饰器用于react组件。通过mobx-react依赖包来提供。它通过mobx.autorun来包装了组件的render函数,以确保组件的render函数在任何数据的更改是强制重新渲染
import {observer} from "mobx-react";
var timerData = observable({secondsPassed: 0
});
setInterval(() => {timerData.secondsPassed++;
}, 1000);
@observer class Timer extends React.Component {render() {return (<span>Seconds passed: { this.props.timerData.secondsPassed } </span> )}
});
React.render(<Timer timerData={timerData} />, document.body);

tips: 如果还有其他的decorators一起或者高阶组件的存在,请确保observer为最内层(优先应用)的修饰器。否则它可能无法工作。如果你只在ES5的环境下工作:其实observer不过是observer(class Timer ... { }) 的语法糖。

  1. 难点—组件中相关值的引用:
    MobX能做的事情很多,但是它却不能把原始的值变成观察者(尽管可以通过包裹这个值来返回一个boxed observables的对象)。所以观察者不是这个原始的值,而是返回后的对象的属性值。修改一个刚才的例子:
React.render(<Timer timerData={timerData.secondsPassed} />, document.body)

这时候程序并不会工作了。传入组件的只是timerData里面secondsPassed的当前值。在组件里面,它是不可变的。

  1. 把你的组件内部状态变成可观察的
    和普通的类一样,你可以在你的组件使用@observable修饰器。这意味着你的组件拥有了一个内部state,而且它不需要使用react内部提供的繁琐的setState机制。这个内部state能调起render函数,但是却不能准确调起React的生命周期函数,例如:componentShouldUpdate / componentWillUpdate。如果你想要这些,最好使用react提供的API来创建state。当然也可以这样写
import {observer} from "mobx-react"
import {observable} from "mobx"
@observer class Timer extends React.Component {@observable secondsPassed = 0componentWillMount() {setInterval(() => {this.secondsPassed++}, 1000)}render() {return (<span>Seconds passed: { this.secondsPassed } </span> )}
})
React.render(<Timer />, document.body)

  1. 连接observer和stores
    mobx-react提供了Provider组件让你可以把传递下来的stores作用在react提供的上下文机制。通过连接这些stores和observer,这些observer会成为组件的属性来使用。
const colors = observable({foreground: '#000',background: '#fff'
});
const App = () =><Provider colors={colors}><app stuff... /></Provider>;
const Button = observer(["colors"], ({ colors, label, onClick }) =><button style={{color: colors.foreground,backgroundColor: colors.background}}onClick={onClick}>{label}<button>
);
// later..
colors.foreground = 'blue';
// all buttons updated

  1. componentWillReact
    React 的组件总是从新的堆栈去渲染。因此让它它很难判断一个组件是否需要重新渲染。在mobx-react里面,你可以使用重新定义的生命周期componentWillReact。它只会在观察者发生变化的时候才重新渲染。
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>;}
}

componentWillReact没有任何参数,而且不会在render初始化之前执行(componentWillMount的区别)。而当接收新的属性或者setState之后,它会被调用。

2.6. action

  1. 任何应用程序都有操作(action)。action是任何改变状态的事物。使用MobX,您可以通过标记它们在您的代码中显式地显示您的操作(action)。它会更好的帮助你组织你的代码。建议将它们用于修改可观察量或具有副作用的任何函数中。
    需要注意的是:action是用在strict mode 中的
action(fn)
action(name, fn)
@action classMethod() {}
@action(name) classMethod () {}
@action boundClassMethod = (args) => { body }
@action(name) boundClassMethod = (args) => { body }
@action.bound classMethod() {}
@action.bound(function() {})@action createRandomContact() {this.pendingRequestCount++;superagent.get('https://randomuser.me/api/').set('Accept', 'application/json').end(action("createRandomContact-callback", (error, results) => {if (error) console.error(error)else {const data = JSON.parse(results.text).results[0];const contact = new Contact(this, data.dob, data.name, data.login.username, data.picture)contact.addTag('random-user');this.contacts.push(contact);this.pendingRequestCount--;}}
))}

  1. action 仅仅作用于当前运行的函数,而不能作用于当前函数调用的函数。这意味着在一些定时器或者网络请求,异步处理的情况下,它们的回调函数无法对状态做成改变。这些回调函数都应该有action包裹,如果例子里面的 createRandomContact-callback 一样。但是,如果你使用了async / await的话,最好的方式应该是使用 runInAction 来让它变得更加简单
@action /*optional*/ updateDocument = async () => {const data = await fetchDataFromUrl();/* required in strict mode to be allowed to update state: */runInAction("update state after fetching data", () => {this.data.replace(data);this.isSaving = true;})
}

  1. Bound actions
    目前看到的actions都是遵循在javascript中绑定的正常规则,但是在MobX 3引入了action.bound来自动绑定actions到目标对象上。和action的使用不一样,不需要一个名字参数。它的名称始终基于绑定到属性的操作上。需要注意的是,在箭头函数上不要这样使用,因为箭头函数已经绑定了上下文,不能在重新更改上下文
class Ticker {@observable this.tick = 0@action.boundincrement() {this.tick++ // 'this' will always be correct}
}
const ticker = new Ticker()
setInterval(ticker.increment, 1000)

后记

启动例子项目:进入

  1. 前端项目:npm install 或者 yarn install 打开本地的8080端口
  2. 后端部分:进入 back文件夹,执行npm start 打开本地的3000端口
  3. 作为对比:使用redux的例子:进入

作者:三月懒驴
链接:https://www.jianshu.com/p/2fb42dee32dd
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

转载于:https://www.cnblogs.com/0616--ataozhijia/p/9147088.html

[Web 前端] MobX相关推荐

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

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

  2. python web前端开发面试_面试前端,听听别人怎么说!

    分享一个人的面试经验: 一年半经验,百度.有赞.阿里面试总结 前言 人家都说,前端需要每年定期出来面面试,衡量一下自己当前的技术水平以及价值,本人17年7月份,毕业到现在都没出来试过,也没很想换工作, ...

  3. 一位老程序员的心得分享,WEB前端菜鸟,感觉很迷茫,该怎么做?

    我们可以把学习路线比作游戏中的段位上分,在不同的分段都有自己的定位和要锻炼的事情: 1.青铜 - 从零开始小学生:怀着满腔的热血,看到了这一个行业的希望和未来,准备开始学习 Web 开发知识. a. ...

  4. Web前端的状态管理

    背景 我相信很多朋友跟我一样,初次听到什么 Flux ,  Redux ,  Vuex , 状态管理 的时候是一脸懵逼的.因为在外面之前前端大部分开发的时候,根本没有那么多的概念.自从ReactJS火 ...

  5. web前端-微信小程序开发学习

    web前端-微信小程序开发学习 1. 小程序的概述 2. 小程序的项目结构 2.1 小程序项目结构分析 2.2 WXML模版 2.3 小程序的宿主环境 3. 组件 3.1 视图容器类组件 3.2 常用 ...

  6. 了解WEB前端开发吗,风风雨雨30年,前端开发变迁史

    按1990年,第一个Web浏览器诞生,而WWW的诞生直接拉开前端史的序幕. 从静态页面到JavaScript,从依赖后端到自主开发,前端开发者从不被重视的"页面仔"逆袭为如今很多前 ...

  7. 如何才能成为一名合格的web前端开发工程师

    如何才能成为一名合格的web前端开发工程师 开篇 前端开发是一个非常特殊的行业,它的历史实际上不是很长,但是知识之繁杂,技术迭代速度之快是其他技术所不能比拟的. winter在他的<重学前端&g ...

  8. 读书笔记:编写高质量代码--web前端开发修炼之道(二:5章)

    读书笔记:编写高质量代码--web前端开发修炼之道 这本书看得断断续续,不连贯,笔记也是有些马虎了,想了解这本书内容的童鞋可以借鉴我的这篇笔记,希望对大家有帮助. 笔记有点长,所以分为一,二两个部分: ...

  9. 对WEB前端的几段思考(一)——界面设计和性能优化(整理中)

    尽管我并非艺术出生,既没有任何设计基础,又没有较高艺术涵养,也深谙在短时间内创造一定艺术造诣并非易事,但是既然当初选择从事网站前端开发,我的目光不能仅停留在前端代码上.作为一名志向在前端领域发展的人员 ...

最新文章

  1. MyEclipse设置默认的文档注释和背景色设置
  2. 页面导航的基础与深入
  3. C#操作符??和?:
  4. java高并发(十六)J.U.C之ForkJoin
  5. 【Programming Clip】06、07年清华计算机考研上机试题解答(个别测试用例无法通过)...
  6. matlab仿真对电脑要求,计算机仿真技术matlab基本语法.ppt
  7. Maven简明教程(3)---在eclipse中创建工程与基本命令介绍
  8. 朴素贝叶斯算法优缺点
  9. java 大字符集_JAVA语言之java 乱码 字符集编码
  10. mysql数据库输入窗体vbs代码_VBS教程:VBScript 与窗体
  11. R 回归 虚拟变量na_R语言 | 回归分析(一)
  12. Java实例项目之投票统计(可拓展衍生)
  13. django--安装
  14. 腾讯区块链发行多少腾讯区块链如何投资?
  15. 微信二维码扫描下载APK
  16. 两组数据的偏差率_GWT测试报告 篇七十五:隐患难忽视,RIVAL 3 WIRELESS精准度LOD测试...
  17. mqtt haproxy 代理及负载搭建
  18. 微信小程序的网络请求 —— 微信小程序教程系列(14)
  19. linux查看网口流量ifconfig,ifconfig与网络流量监控
  20. 7-data android recovery,7-Data Android Recovery

热门文章

  1. java中堆栈内存_Java堆空间与堆栈– Java中的内存分配
  2. 如何在Windows 10上安装Python
  3. Java基础篇:什么是递归?如何用递归?
  4. JS正则表达式(RegExp)
  5. scala学习手记2 - scala中的循环
  6. connect()在UDP连接中的应用一例
  7. intellji External Libraries下依赖包找不到解决方法
  8. 【链表递归构造二叉树】LeetCode 109. Convert Sorted List to Binary Search Tree
  9. LeetCode 36. Valid Sudoku
  10. Python高性能编程指南大纲