Mobx-React:当前最适合React的状态管理工具

Kevin501
• 阅读 1331

MobX

简单、可扩展的状态管理

MobX 是由 Mendix、Coinbase、Facebook 开源和众多个人赞助商所赞助的。

安装

浏览器支持

  • MobX >=5 版本运行在任何支持 ES6 proxy 的浏览器。如果运行在像 IE11、Node.js 6 以下版本或依靠与较旧的 JavaScripCore 的安卓端的 React Native (点击查看如何升级])。
  • MobX 4 可以运行在任何支持 ES5 的浏览器上,而且也将进行持续地维护。MobX 4 和 5 的 API 是相同的,并且语义上也能达到相同的效果,只是 MobX 4 存在一些 局限性

小贴士: MobX 5 包的主入口点附带 ES5 代码,以便向后兼容所有构建工具。但因为 MobX 5 只能运行在现代浏览器上,所以可以考虑使用速度最快、体积最小的 ES6 构建: lib/mobx.es6.js 。例如,通过设置 webpack 的别名: resolve: { alias: { mobx: __dirname + "/node_modules/mobx/lib/mobx.es6.js" }}

入门指南

入门

MobX 是一个经过战火洗礼的库,它通过透明的函数响应式编程(transparently applying functional reactive programming - TFRP)使得状态管理变得简单和可扩展。MobX背后的哲学很简单:

任何源自应用状态的东西都应该自动地获得。

其中包括UI、数据序列化、服务器通讯,等等。

Mobx-React:当前最适合React的状态管理工具

React 和 MobX 是一对强力组合。React 通过提供机制把应用状态转换为可渲染组件树并对其进行渲染。而MobX提供机制来存储和更新应用状态供 React 使用。

对于应用开发中的常见问题,React 和 MobX 都提供了最优和独特的解决方案。React 提供了优化UI渲染的机制, 这种机制就是通过使用虚拟DOM来减少昂贵的DOM变化的数量。MobX 提供了优化应用状态与 React 组件同步的机制,这种机制就是使用响应式虚拟依赖状态图表,它只有在真正需要的时候才更新并且永远保持是最新的。

核心概念

MobX 的核心概念不多。 下面的代码片段可以在 codesandbox 示例中在线试用。

Observable state(可观察的状态)

Egghead.io 第1课: observable & observer

MobX 为现有的数据结构(如对象,数组和类实例)添加了可观察的功能。 通过使用 @observable 装饰器(ES.Next)来给你的类属性添加注解就可以简单地完成这一切。

1 import { observable } from "mobx"; 2 
3 class Todo { 4     id = Math.random(); 5     @observable title = ""; 6     @observable finished = false; 7 }

使用 observable 很像把对象的属性变成excel的单元格。 但和单元格不同的是,这些值不只是原始值,还可以是引用值,比如对象和数组。

如果你的环境不支持装饰器语法,也不必担心。 你可以点击这里查看如何进行设置。 或者你可以直接跳过设置,因为 MobX 可以通过 decorate 工具在不支持装饰器语法的情况加使用。 尽管如此,多数 MobX 用户更喜欢装饰器语法,因为它更简洁。

例如,上面一段代码的ES5版本应该是这样:

 1 import { decorate, observable } from "mobx";
 2 
 3 class Todo { 4     id = Math.random(); 5     title = "";
 6     finished = false;
 7 }
 8 decorate(Todo, {
 9 title: observable, 10 finished: observable 11 })

Computed values(计算值)

Egghead.io 第3课: 计算值

使用 MobX, 你可以定义在相关数据发生变化时自动更新的值。 通过@computed 装饰器或者利用 (extend)Observable 时调用 的getter / setter 函数来进行使用。(当然,这里也可以再次使用 decorate来替代 @ 语法)。

1 class TodoList { 2     @observable todos = \[\]; 3     @computed get unfinishedTodoCount() { 4         return this.todos.filter(todo => !todo.finished).length; 5 } 6 }

当添加了一个新的todo或者某个todo的 finished 属性发生变化时,MobX 会确保 unfinishedTodoCount 自动更新。 像这样的计算可以类似于 MS Excel 这样电子表格程序中的公式。每当只有在需要它们的时候,它们才会自动更新。

Reactions(反应)

Egghead.io 第9课: 自定义反应

Reactions 和计算值很像,但它不是产生一个新的值,而是会产生一些副作用,比如打印到控制台、网络请求、递增地更新 React 组件树以修补DOM、等等。 简而言之,reactions 在 响应式编程命令式编程之间建立沟通的桥梁。

React 组件

Egghead.io 第1课: observable & observer

如果你用 React 的话,可以把你的(无状态函数)组件变成响应式组件,方法是在组件上添加 observer 函数/ 装饰器. observer由 mobx-react 包提供的。

 1 import React, {Component} from 'react';
 2 import ReactDOM from 'react-dom';
 3 import {observer} from 'mobx-react';
 4 
 5 @observer
 6 class TodoListView extends Component { 7     render() {
 8         return <div>
 9             <ul>
10                 {this.props.todoList.todos.map(todo =>
11                     <TodoView todo={todo} key={todo.id} />
12 )} 13             </ul>
14             Tasks left: {this.props.todoList.unfinishedTodoCount} 15         </div>
16 } 17 } 18 
19 const TodoView = observer(({todo}) =>
20     <li>
21         <input 22             type="checkbox"
23             checked\={todo.finished} 24             onClick={() => todo.finished = !todo.finished} 25         />{todo.title} 26     </li>
27 ) 28 
29 const store = new TodoList(); 30 ReactDOM.render(<TodoListView todoList={store} />, document.getElementById('mount'));

observer 会将 React (函数)组件转换为它们需要渲染的数据的衍生。 使用 MobX 时没有所谓的智能和无脑组件。 所有的组件都会以巧妙的方式进行渲染,而只需要一种简单无脑的方式来定义它们。MobX 会确保组件总是在需要的时重新渲染,但仅此而已。所以上面例子中的 onClick 处理方法会强制对应的 TodoView 进行渲染,如果未完成任务的数量(unfinishedTodoCount)已经改变,它将导致 TodoListView进行渲染。 可是,如果移除 Tasks left 这行代码(或者将它放到另一个组件中),当点击 checkbox 的时候 TodoListView 就不再重新渲染。你可以在 JSFiddle 中自己动手来验证这点。

自定义 reactions

使用autorunreaction 和 when 函数即可简单的创建自定义 reactions,以满足你的具体场景。

例如,每当 unfinishedTodoCount 的数量发生变化时,下面的 autorun 会打印日志消息:

autorun(() => {
    console.log("Tasks left: " + todos.unfinishedTodoCount)
}) 

MobX 会对什么作出响应?

为什么每次 unfinishedTodoCount 变化时都会打印一条新消息?答案就是下面这条经验法则:

_MobX 会对在执行跟踪函数期间读取的任何现有的可观察属性做出反应_。

想深入了解 MobX 是如何知道需要对哪个可观察属性进行响应,请查阅 理解 MobX 对什么有反应

Actions(动作)

Egghead.io 第5课: actions

不同于 flux 系的一些框架,MobX 对于如何处理用户事件是完全开明的。

  • 可以用类似 Flux 的方式完成
  • 或者使用 RxJS 来处理事件
  • 或者用最直观、最简单的方式来处理事件,正如上面演示所用的 onClick

最后全部归纳为: 状态应该以某种方式来更新。

当状态更新后,MobX 会以一种高效且无障碍的方式处理好剩下的事情。像下面如此简单的语句,已经足够用来自动更新用户界面了。

从技术上层面来讲,并不需要触发事件、调用分派程序或者类似的工作。归根究底 React 组件只是状态的华丽展示,而状态的衍生由 MobX 来管理。

1 store.todos.push( 2     new Todo("Get Coffee"), 3     new Todo("Write simpler code") 4 ); 5 store.todos\[0\].finished = true;

尽管如此,MobX 还是提供了 actions 这个可选的内置概念。 如果你现在就想要了解如何编写 actions,请阅读 Actions 章节。很简单! 使用 actions 是有优势的: 它们可以帮助你把代码组织的更好,还能在状态何时何地应该被修改这个问题上帮助你做出明智的决定。

MobX: 简单且可扩展

MobX 是状态管理库中侵入性最小的之一。这使得 MobX的方法不但简单,而且可扩展性也非常好:

使用类和真正的引用

使用 MobX 不需要使数据标准化。这使得库十分适合那些异常复杂的领域模型(以 Mendix 为例: 一个应用中有大约500个领域类)。

保证参照完整性

因为数据不需要标准化,所以 MobX 会自动跟踪状态和衍生之间的关系,你可以免费获得参照完整性。渲染通过三级间接寻址访问的数据?

没有问题,MobX 会跟踪它们,一旦其中一个引用发生了变化,就会重新渲染。作为回报,陈年的老bug已不复存在。作为一个程序员,你可能记不住修改的一些数据可能会影响到的某个角落里看起来毫不相关的组件,但 MobX 不会。

更简单的 actions 更便于维护

正如上面所演示的,使用 MobX 修改状态是非常简单的。你只需简单的写出你的目的。MobX 会替你处理好剩下的事情。

细粒度的可观测性是高效的

MobX 构建应用中所有衍生的图形,以找到保持最新状态所需的重新计算的最少次数。“衍生一切”或许听上去开销很昂贵,但 MobX 构建虚拟衍生图以保持衍生与状态同步所需的重计算的数量最小化。

事实上,在 Mendix 测试 MobX 时我们发现使用这个库跟踪代码中的关系通常会更有效,而不是通过使用手写事件或基于容器组件的“智能”选择器来推送更改。

简单来说,是因为 MobX 会在数据上建立更细粒度的“监听器”,而不是通过程序来控制。

其次, MobX 看到衍生之间的因果关系,因此它可以为衍生排序,使得衍生不会运行多次或引入缺陷。

想了解这是如何工作的? 请参见 深入剖析 MobX

易操作性

MobX 使用原生 javascript 。由于它的侵入性不强,它可以和绝大部分 javascript 库共同使用,而不需要特定的 MobX 风格库。

所以你可以继续使用你的路由,数据获取和工具库,比如react-router、 director、 superagent、 lodash,等等。

出于同样的原因,你可以在服务器端和客户端使用它,也可以在 react-native 这样的同构应用中使用。

结论就是: 相比其它状态管理解决方案,当使用 MobX 时通常只需学习更少的新概念。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
2年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Android So动态加载 优雅实现与原理分析
背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Kevin501
Kevin501
Lv1
黑发不知勤学早,白首方悔读书迟。
文章
4
粉丝
0
获赞
0