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

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

 
1.@observable 装饰器
使用observable可以吧对象的属性变成单据格或者引用值
 
import { observable } from "mobx";

class Todo {
id = Math.random();
@observable title = "";
}

2.@computed 装饰器. 计算值

相关数据发生变化时自动更新的值

class TodoList {
@observable todos = [];
@computed get unfinishedTodoCount() {
return this.todos.filter(todo => !todo.finished).length;
}
}

 当添加了一个新的todo或者某个todo的 finished 属性发生变化时,MobX 会确保 unfinishedTodoCount 自动更新。

3.reactions. 反应

reactions是在响应式编程和命令式编程之间建立沟通的桥梁

和@computed相似,但是不是产生新的值,而是一些操作,例如 网络请求,打印控制台,更新react组件树

4.react 响应式组件

在组件上添加observer装饰器/函数 就可以把无状态组件变成响应式组件

MobX 会确保组件总是在需要的时重新渲染

import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import {observer} from 'mobx-react'; @observer
class TodoListView extends Component {
render() {
return <div>
<ul>
{this.props.todoList.todos.map(todo =>
<TodoView todo={todo} key={todo.id} />
)}
</ul>
Tasks left: {this.props.todoList.unfinishedTodoCount}
</div>
}
} const TodoView = observer(({todo}) =>
<li>
<input
type="checkbox"
checked={todo.finished}
onClick={() => todo.finished = !todo.finished}
/>{todo.title}
</li>
) const store = new TodoList();
ReactDOM.render(<TodoListView todoList={store} />, document.getElementById('mount'));

3.1 自定义reactions

使用autorun,reaction,when函数可以创建自定义reactions

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

autorun(() => {
console.log("Tasks left: " + todos.unfinishedTodoCount)
})
mobx的基本实现
1.定义可观察的状态 (observable)
2.创建视图用来响应状态的变化
3.更改状态
 
mobx会对追踪函数执行过程中读取现存的可观察属性作出反应
  • “读取” 是对象属性的间接引用,可以用过 . (例如 user.name) 或者 [] (例如 user['name']) 的形式完成。
  • “追踪函数” 是 computed 表达式、observer 组件的 render() 方法和 whenreaction 和 autorun 的第一个入参函数。
  • “过程(during)” 意味着只追踪那些在函数执行时被读取的 observable 。这些值是否由追踪函数直接或间接使用并不重要。

换句话说,MobX 不会对其作出反应:

  • 从 observable 获取的值,但是在追踪函数之外
  • 在异步调用的代码块中读取的 observable

默认情况下 observable 会递归应用

所有字段都可观察
let message = observable({
title: "Foo",
author: {
name: "Michel"
},
likes: [
"John", "Sara"
]
})

mobx只追踪同步地访问数据

function upperCaseAuthorName(author) {
const baseName = author.name;
return baseName.toUpperCase();
}
autorun(() => {
console.log(upperCaseAuthorName(message.author))
})
message.author.name = "Chesterton"

这将会作出反应。尽管 author.name 不是在 autorun 本身的代码块中进行直接引用的。 MobX 会追踪发生在 upperCaseAuthorName 函数里的间接引用,因为它是在 autorun 执行期间发生的。

 
autorun(() => {
setTimeout(
() => console.log(message.likes.join(", ")),
10
)
})
message.likes.push("Jennifer");

这将不会作出反应。在 autorun 执行期间没有访问到任何 observable,而只在 setTimeout 执行期间访问了。 通常来说,这是相当明显的,很少会导致问题。

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

一个使用 observer 的常见错误是它不会追踪语法上看起来像 observer 父组件的数据,但实际上是由不同的组件渲染的。当组件的 render 回调函数在第一个类中传递给另一个组件时,经常会发生这种情况。

const MyComponent = observer(({ message }) =>
<SomeContainer
title = {() => <div>{message.title}</div>}
/>
) message.title = "Bar"

这种情况下,SomeContainer也应该是一个observer,才可以对message.title作出反应

如果 SomeContainer 来源于外部库的话,这通常不在你的掌控之中。在这种场景下,你可以用自己的无状态 observer 组件来包裹 div 解决此问题,或通过利用 <Observer>组件:

const MyComponent = observer(({ message }) =>
<SomeContainer
title = {() => <TitleRenderer message={message} />}
/>
) const TitleRenderer = observer(({ message }) =>
<div>{message.title}</div>}
) message.title = "Bar"

或者

const MyComponent = ({ message }) =>
<SomeContainer
title = {() =>
<Observer>
{() => <div>{message.title}</div>}
</Observer>
}
/> message.title = "Bar"

避免在本地字段中缓存observer

组件会对 author.name 的变化作出反应,但不会对 message 本身的 .author 的变化作出反应!因为这个间接引用发生在 render() 之外,而render() 是 observer 组件的唯一追踪函数。 注意,即便把组件的 author 字段标记为 @observable 字段也不能解决这个问题,author 仍然是只分配一次。

@observer class MyComponent extends React.component {
author;
constructor(props) {
super(props)
this.author = props.message.author;
} render() {
return <div>{this.author.name}</div>
}
}

这个问题可以简单地解决,方法是在 render() 中进行间接引用或者在组件实例上引入一个计算属性:

@observer class MyComponent extends React.component {
@computed get author() {
return this.props.message.author
}
// ...

 

多个组件将如何渲染

const Message = observer(({ message }) =>
<div>
{message.title}
<Author author={ message.author } />
<Likes likes={ message.likes } />
</div>
) const Author = observer(({ author }) =>
<span>{author.name}</span>
) const Likes = observer(({ likes }) =>
<ul>
{likes.map(like =>
<li>{like}</li>
)}
</ul>
)
变化 重新渲染组件
message.title = "Bar" Message
message.author.name = "Susan" Author (.author 在 Message 中进行间接引用, 但没有改变)*
message.author = { name: "Susan"} MessageAuthor
message.likes[0] = "Michel" Likes

注意:

  1. * 如果 Author 组件是像这样调用的: <Author author={ message.author.name} /> 。Message 会是进行间接引用的组件并对 message.author.name 的改变作出反应。尽管如此,<Author> 同样会重新渲染,因为它接收到了一个新的值。所以从性能上考虑,越晚进行间接引用越好。
  2. ** 如果 likes 数组里面的是对象而不是字符串,并且它们在它们自己的 Like 组件中渲染,那么对于发生在某个具体的 like 中发生的变化,Likes 组件将不会重新渲染。
 

mobx的优点

1,使用@observer的组件真正实现按需更新,只有监听的数据发生变化,它才会re-render,尽管父组件发生更新,但是子组件只要有@observer,则不会触发更新,类似于实现了shouldComponentUpdate的效果,而同样的场景,如果是redux,子组件通过connect绑定了store里部分数据,但是如果父组件发生更新,子组件绑定的数据源并未发生变化,因此子组件不应该更新,然而却强制更新。

mobx耦合性更低。

mobx的缺点

1,store过多导致无法统一数据源,在多人开发的情况下会比较混乱,如果需要公用某个store,唯有在特定组件注入该store,那如果有多个store里有多个数据源需要公用呢,那就要同时注入多个store,项目复杂了,store的管理又是一个问题。

2,mobx对于数组的处理,会将它转换成observableArray,它不是一个数组类型,需要进行数组转换(如slice)。

3,mobx在forceupdate时,并没有像setState那样处理多个更新合并的操作,这就带来个问题就是,在同一个event loop中,如果多次通过action去引起同一个观察者的更新,那么它就会连续更新多次,但是如果是setState,它会进行一次合并,不会有这种负担。(这里的解决方案就是人为的去控制,避免这种多次同时更新,可以将多个action手动合并为1个action);

4,对于某个公共组件,可能多个页面需要用到,但是组件内状态较多,并且不同页面,状态差异较多,此时如果用mobx的store来管理,会存在一个问题,组件虽然unmount了,但是store并未消失,导致需要手动reset store里所有的状态,否则会混乱

mobx的思想

将响应式编程和部分函数式编程相结合,取缔传统React的命令式编程,分而治之,将组件都变成可响应的,React提供了渲染的最优方式,通过对比虚拟DOM的diff算法来减少不必要的渲染耗费,而mobx则给React提供了响应式的状态管理。

关于mobx细粒度拆分

是否所有的state我都交给mobx来管理,其实类似于redux,只有当某个状态需要多个组件(这里默认为2个及以上)公用,则将该状态放到store里去监听,然后需要引用该状态的组件通过mobx-react的inject来进行监听。

关于mobx的优化

这里我参考redux的优化,将容器组件通过mobx-react来连接需要监听的store,然后展示组件就还是通过PureComponent来进行优化,避免重复re-render。这里其实有一点考虑是,我们可以利用@observer来对展示组件需要更新的状态进行一个监听,免去shouldComponentUpdate的步骤,但是如果多挂一层监听,性能未必更优,相当于要多处理一个监听器,所以还是减少监听的个数会好些。

为什么选择mobx而不是redux

这个标题不是为了贬低redux,而是解释为什么在某些项目环境中,我不使用redux,而是使用mobx,能否使用redux?可以,只不过mobx会更让开发者舒服一些。

mobx基础的更多相关文章

  1. MobX基础 ----- 类的静态属性和装饰器

    当我们使用MobX的时候,首先要声明一个store, 用来保存状态,它的最基本的语法 如下: class Todo { @observable title = ""; @obser ...

  2. mobx学习笔记03——mobx基础语法(decorator修饰器)

    在声明阶段实现类与类成员注解的一种语法. function log(target){ const desc = Object.getOwnPropertyDescriotors(target.prot ...

  3. mobx学习笔记02——mobx基础语法(class)

    新的语法可能不被浏览器支持,可以使用babel转换为浏览器支持的代码格式: 为什么要定义class? js是一门面向对象的编程语言.需要利用类来复用代码,提高编程效率. 需要什么样的class能力? ...

  4. 你需要Mobx还是Redux?

    在过去一年,越来越多的项目继续或者开始使用React和Redux开发,这是目前前端业内很普遍的一种前端项目解决方案,但是随着开发项目越来越多,越来越多样化时,个人又有了不同的感受和想法.是不是因为已经 ...

  5. Mobx使用详解

    Mobx是一个功能强大,上手非常容易的状态管理工具.就连redux的作者也曾经向大家推荐过它,在不少情况下你的确可以使用Mobx来替代掉redux. 本教程旨在介绍其用法及概念,并重点介绍其与Reac ...

  6. React框架 dva 和 mobx 的使用感受

    最近在用react写web项目,领导为了让前端便于维护要求都用react作为开发基础,框架选型不限.在使用 react 的时候或多或少会接触到状态管理,从开始学 react 到现在也挺久了,做一些前端 ...

  7. React使用Mobx管理数据

    React 和 Vue一样都属于单向数据流,为了更好的进行状态和数据管理官方和第三方也有配套的Redux等插件,本文介绍一个个人觉得更易用使用的组件 Mobx 核心概念 MobX 处理你的应用程序状态 ...

  8. react+react-router+mobx+element打造管理后台系统---react-amdin-element

    react-admin-element,一款基于react的后台管理系统. 那么我们和其他的后台管理系统有什么区别呢? demo地址:点我进入demo演示 github地址:点我进入github 1. ...

  9. 在react项目中使用redux or mobx?

    主要比较参数: 库体积,打包项目体积 开发体验 性能对比 在对比参数前首先分析一下redux和mobx的设计模式,redux和mobx都没有使用传统的mvc/mvvm形式,而且他们使用flux结构也略 ...

  10. 【MobX】391- MobX 入门教程(下)

    点击上方"前端自习课"关注,学习起来~ 三.MobX 常用 API 介绍 3. 修改可观察数据 在上一部分内容中,我们了解到,对可观察的数据做出反应的时候,需要我们手动修改可观察数 ...

随机推荐

  1. react 二级路由嵌套

    嵌套路由之后,静态文静路径错误, 更改webpack  打包output 输出根目录,publicPath:'/',二级路由刷新之后白屏,在首页模板文件中路径前加   /,

  2. anaconda新建的虚拟环境更改默认位置

    anaconda虚拟环境默认安装位置 https://blog.csdn.net/weixin_48373309/article/details/127830801 vscode中切换路径问题 在vs ...

  3. Java调试排错心得

    首先这里没有报错,但是打印了四行相同的数据,还都是最后一行的数据.然后调试了一下 这里是重点: 下面哪里account = {Account@1580}是一直用的一个对象,所有每一次调试那些什么rs. ...

  4. CAD中如何将图形对象转换为三维实体?

    有些小伙伴在CAD绘制完图纸后,想要将图纸中的某些图形对象转换成三维实体,但却不知道该如何操作,其实很简单,本节CAD绘图教程就和小编一起来了解一下浩辰CAD软件中将符合条件的对象转换为三维实体的相关 ...

  5. 蓝桥杯训练赛二-1467 问题 F: 蓝桥杯基础练习VIP-完美的代价

    题目描述 回文串,是一种特殊的字符串,它从左往右读和从右往左读是一样的.小龙龙认为回文串才是完美的.现在给你一个串,它不一定是回文的,请你计算最少的交换次数使得该串变成一个完美的回文串.交换的定义是: ...

  6. leedcode题目 :罗马数字转整数 Java

    罗马数字包含以下七种字符: I, V, X, L,C,D 和 M. 例如, 罗马数字 2 写做 II ,即为两个并列的 1.12 写做 XII ,即为 X + II . 27 写做  XXVII, 即 ...

  7. gitee 上传远程仓库失败

    1:添加本地id_rsa.pub文件内容到gitee SSH Keys (添加公开密钥) 2:设置本地的邮箱和用户名 git config --global user.name "MARS& ...

  8. Vulnhub 靶场 DOUBLETROUBLE: 1

    Vulnhub 靶场 DOUBLETROUBLE: 1 前期准备 靶机地址:https://www.vulnhub.com/entry/doubletrouble-1,743/ kali攻击机: 靶机 ...

  9. vue element-ui form验证中自定义验证方式通过不返回true问题

    项目中使用了element-ui的form验证,自定义了手机号的验证规则,验证不通过的时候定义了callback()扔出错误.但是忘了写通过的callback().导致form验证通过拿不到返回的va ...

  10. 怎么把百度地图的搜索结果全部导出到Excel文件

    有很多人问我,怎么样能够快速的把BAIDU地图左边的搜索列表里的商家地图,电话,导出到EXCEL里. 我就开发了一个小软件,专门为快速的实现导出数据到EXCEL. 为了使用方便,已经将全国的所又省份, ...