效率至上的Mobx

为什么会考虑使用状态管理?

项目开发中经常会碰到以下场景: 1.业务系统许多接口,入参都需要传递登陆人 id

1
userCode: localStorage.getItem('userCode'),

2.业务系统选择下拉框,如城市,销售业务员、车型、折旧选项等均为同一接口数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 获取司机信息
getDrivers(name) {
http
.get("/api/Common/GetDrivers", {
Key: name
})
.then(res => {
const { success, result } = res.data;
if (success) {
if (Array.isArray(result)) {
this.setState({
drivers: result
});
} else {
this.setState({
drivers: []
});
}
} else {
message.error("获取司机信息失败");
}
})
.catch(err => {
console.error(err);
});
}

3.兄弟组件传值等

MobX 背后的哲学

任何可以从应用程序的状态中获取/衍生的数据都应该可以自动被获取/衍生。
MobX 遵循单项数据流,通过 action 改变 state(可观测对象),state 的变化又会触发 computed value 和 reaction 的重新执行。

Redux 和 Mobx 相同点

1.优秀的状态管理解决方案 2.采用单向数据流管理状态 3.Action-->State-->Views

Mobx 和 Redux 的区别

Mobx Redux
多个 store 对象,多 store 间的数据共享、相互引用 单一数据源,应用共享一个 store 对象
state 结构可以任意嵌套 state 结构尽可能的扁平化,从而减少嵌套层级
state 可观测,可以直接修改 普通 JavaScript 对象存储 state
上手成本低、开发效率高 需要熟悉函数式编程基础
代码量更少 需要写更多的代码来执行严格的规范

选择

MobX Redux
相对简单的应用 复杂度较高的应用

MobX 包含的主要概念

  • state(状态)
  • computed value(计算值)
  • reaction(响应)
    • computed value 和 reaction 会自动根据 state 的改变做最小化的更新
  • action(动作)

Mobx 的缺点

如使用装饰器时团队如果不制定一个规范,后期可能难以维护。

解决方案

自定义装饰器还是要在项目组内达成共识才行。
如:将各个状态封装到一个对象中导出(防止代码难以维护,加入静态图和 gif 图)
统一对象

将使用 Mobx 观测的数据整合到一个 stores 对象注入到 Provider
到‘src/stores/index.js’中

获取可观测的 state

通过 React-mobx 提供 Provider 组件在根目录 index.jsx 中让容器组件拿到 state

1
2
3
4
5
6
7
import { Provider } from "mobx-react";
import stores from "./stores";
...
<Provider {...stores}>
<App />
</Provider>,
document.getElementById('root')

Provider 在根组件外包了一层,来让 App 的所有子组件就默认都可以拿到 state。
Provider 源码
只有先获取到 state,在对应组件中才能使用 inject 注入

@inject 注入使用的 Store

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { observer, inject } from 'mobx-react';
import { observable } from 'mobx';
@observer
@inject('store') // inject从context中取出store对象,注入到组件的props中
class App extends Component {
render() {
const { store } = this.props;
return (
<div>
<ul>
{store.map((todo) => (
<TodoView todo={todo} key={todo.id} />
))}
</ul>
</div>
);
}
}
const TodoView = observer(({ todo }) => {
return <li>{todo.title}</li>;
});

配置装饰器语法

项目中会大量使用到装饰器语法@observable,所以要提前配置装饰器
配置方案
记得配置 jsconfig

computed value 的使用

Loading-20191210
Loading状态 gif

一般通过computed@computed创建computed value

1
2
computed(() => expression)
@computed get classProperty() { return expression; }

action 的使用

常见的用法

1
2
action(fn)
@action classMethod

action的使用.png

action.bound 解决 this 指向问题

箭头函数

1
2
3
4
5
6
7
logout = () => {
this.userId = undefined;
this.username = 'jack';
this.password = '123456';
sessionStorage.removeItem('userId');
sessionStorage.removeItem('username');
};

action.bound/@action.bound

1
2
3
4
5
6
7
@action.bound logout() {
this.userId = undefined;
this.username = 'jack';
this.password = '123456';
sessionStorage.removeItem("userId");
sessionStorage.removeItem("username");
}
1
2
3
4
5
6
7
8
9
10
11
12
{
authStore.userId && authStore.userId.length > 0 ? (
<span className='user'>
当前用户:{authStore.username}&nbsp;
<button onClick={authStore.logout}>注销</button>
</span>
) : (
<span className='right-link'>
<Link to={{ pathname: '/login', state: { from: location } }}>登录</Link>
</span>
);
}

mobx 调试工具

mobx-react-devtools 是一个用于调试 MobX+React 项目的工具,可以追踪组件的渲染以及组件依赖的可观测数据。

安装

只需要在开发环境下使用调试工具,安装命令使用的参数是–save-dev

1
2
3
npm install mobx-react-devtools --save-dev

yarn add mobx-react-devtools --save-dev

再次运行程序,界面右上角会多出三个小图标,是 mobx-react-devtools 的功能键。

使用

点击第一个图标,当组件发生渲染行为时,对应的组件会被高亮显示
DevTool-Button1.gif

  • 高亮组件右上角显示的 3 个数字分别代表截至当前组件渲染的次数、组件 render 方法执行的时间、组件从 render 方法开始到渲染到浏览器界面使用的时间
    Button1注释.png

点击第二个图标后,再用鼠标选择任意一个组件,可以查看该组件会对哪些数据的变化做出响应
DevTool-Button2.gif

点击第三个图标,控制台会输出发生的 action、响应的 reaction 等调试日志信息
DevTool-Button3.gif

当然你也可以使用 trace 进行调试

参考


效率至上的Mobx
https://www.pengsifan.com/2019/12/09/效率至上的Mobx/
作者
Van
发布于
2019年12月9日
许可协议