Mobx
Mobx
Mobx介绍
(1) Mobx是一个功能强大,上手非常容易的状态管理工具。
(2) Mobx背后的哲学很简单: 任何源自应用状态的东西都应该自动地获得。
(3) Mobx利用getter和setter来收集组件的数据依赖关系,从而在数据发生变化的时候精确知道哪些组件需要重绘,在界面的规模变大的时候,往往会有很多细粒度更新。
\前端相关\React\img\flow.png)
Mobx与redux区别
- Mobx写法上更偏向于OOP
- 对一份数据直接进行修改操作,不需要始终返回一个新的数据并非单一store,可以多store。
- Redux默认以JavaScript原生对象形式存储数据,而Mobx使用可观察对象
优点:
- 学习成本小
- 面向对象编程, 而且对 TS 友好
缺点:
- 过于自由:Mobx提供的约定及模版代码很少,代码编写很自由,如果不做一些约定,比较容易导致团队代码风格不统一
- 相关的中间件很少,逻辑层业务整合是问题。
Mobx使用
1.observable和autorun
通过observable.box对属性进行观察监听,当属性发生改变时,autorun中可以监听获取。
注意:autorun中监听某个属性a的时候,其他属性b发生改变,监听a属性的autorun方法不会再执行。也就是说,在Mobx中,需要进行属性一对一的监听
1 | import {observable,autorun} from 'mobx' |
2.action,runInAction和严格模式
- action普通派发行为
- runInAction 解决异步派发行为
1 | // mobx/store.js |
3.解决react脚手架中使用ES7中的装饰器问题
npm i @babel/core @babel/plugin-proposal-decorators @babel/preset-env
创建
.babelrc1
2
3
4
5
6
7
8
9
10
11
12
13{
"presets": [
"@babel/preset-env"
],
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
]
]
}创建config-overrides.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20const path = require('path')
const { override, addDecoratorsLegacy } = require('customize-cra')
function resolve(dir) {
return path.join(__dirname, dir)
}
const customize = () => (config, env) => {
config.resolve.alias['@'] = resolve('src')
if (env === 'production') {
config.externals = {
'react': 'React',
'react-dom': 'ReactDOM'
}
}
return config
};
module.exports = override(addDecoratorsLegacy(), customize())安装依赖:npm i customize-cra react-app-rewired
修改package.json
1
2
3
4
5
6"scripts": {
"start": "set PORT=4000 && react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
},
4.mobx-react使用
npm i mobx-react@5
1.react 组件里使用 @observer
observer 函数/装饰器可以用来将 React 组件转变成响应式组件。
2.可观察的局部组件状态
@observable 装饰器在React组件上引入可观察属性。而不需要通过 React 的冗长和强制性的 setState 机制来管理。
3.Provider 组件
它使用了 React 的上下文(context)机制,可以用来向下传递 stores。 要连接到这些 stores,需要传递一个 stores名称的列表给 inject,这使得 stores 可以作为组件的 props 使用。this.props
4.使用流程
index.js 使用Provider将跟组件进行包裹
1
2
3
4
5
6
7
8
9
10
11
12
13import React from "react";
import ReactDom from 'react-dom'
import App from "./mobx/App";
import {Provider} from 'mobx-react'
import store from "./mobx/store";
ReactDom.render(
<Provider store={store}>
<App/>
</Provider>
,
document.getElementById('root'))类组件使用Mobx
通过inject装饰器注入store,observer装饰器将react变成响应式组件
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
27
28
29import React, {Component} from 'react';
import IndexRouter from "./router/IndexRouter";
import Tabbar from "./components/Tabbar";
import './views/css/App.css'
import {autorun} from "mobx";
import store from "../mobx/store";
import {inject, observer} from "mobx-react";
@inject("store")
@observer
class App extends Component {
componentDidMount() {
// console.log(this.props);
}
render() {
return (
<div>
<IndexRouter>
{this.props.store.isTabbarShow && <Tabbar/>}
</IndexRouter>
</div>
);
}
}
export default App;函数组件使用Mobx
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
27
28
29
30
31import React, {useEffect, useState} from 'react';
import store from "../../mobx/store";
import {autorun} from "mobx";
import {Observer} from "mobx-react";
export default function Cinemas(props) {
useEffect(() => {
if(store.list.length === 0){
store.getList()
}
},[])
return (
<div>
<div>
<Observer>
{() => { return store.list.map(item =>
<dl key={item.cinemaId} style={{padding:"10px"}}>
<dt>{item.name}</dt>
<dd style={{fontSize:"12px",color:"gray"}}>{item.address}</dd>
</dl>
)
}}
</Observer>
</div>
</div>
)
}mobx/store.js
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40import {observable,autorun,configure,action,runInAction} from 'mobx'
import axios from "axios";
configure({
enforceActions:'always'
})
class Store {
@observable isTabbarShow = true
@observable list = []
@action changeShow(){
this.isTabbarShow = true
}
@action changeHide(){
this.isTabbarShow = false
}
@action async getList(){
var list = await axios({
url: 'https://m.maizuo.com/gateway?cityId=110100&ticketFlag=1&k=878555',
headers: {
'X-Client-Info': '{"a": "3000", "ch": "1002", "v": "5.2.0", "e": "1646462402616989231939585"}',
'X-Host': 'mall.film-ticket.cinema.list'
},
method:'get'
}).then(res => {
return res.data.data.cinemas
// runInAction(() => {
// this.list = res.data.data.cinemas
// })
})
runInAction(() => {
this.list = list
})
}
}
const store = new Store()
export default store