笔记来源:拉勾教育 - 大前端就业集训营
文章内容:学习过程中的笔记、感悟、和经验
提示:项目实战类文章资源无法上传,仅供参考
MobX - 简单的可扩展状态管理
简介
简单、可扩展的状态管理库,和redux作用是一样的,但更简单
- MobX是由Mendix,Coinbase,Facebook开源和众多个人赞助商所赞助的
- React和MobX是一对强力组合,React负责渲染应用的状态,MobX负责管理应用状态供React使用
浏览器支持情况:
- MobX5版本运行在任何支持ES6-proxy的浏览器,不支持IE11、Node.js6
- MobX4可以运行在任何支持ES5的浏览器上
- MobX4和5的API是相同的
课程中使用版本5进行讲解
开发前准备
启用装饰器语法支持
方法1:
-
弹射出项目底层配置:
npm run eject -
下载装饰器语法babel插件:
npm install @babel/plugin-proposal-decorators -
在package.json文件中加入配置
-
"babel": { "plugins": [ [ "@babel/plugin-proposal-decorators", { "legacy": true } ] ] }
-
方法2(课程使用,不报错即可):
-
覆盖底层配置:
npm install react-app-rewired @babel/plugin-proposal-decorators customize-cra -
在项目根目录创建config-overrides.js并加入配置
-
const { override, addDecoratorsLegacy } = require("customize-cra"); module.exports = override(addDecoratorsLegacy());
-
-
修改配置文件package.json
-
"scripts": { "start": "react-app-rewired start", "build": "react-app-rewired build", "test": "react-app-rewired test", }
-
解决vscode编辑器关于装饰器语法的警告
- vscode设置中的javascript.implicitProjectConfig.experimentalDecorators设置为true
- 如果找不到可以找JS/TS › Implicit Project Config: Experimental Decorators
MobX使用
- 下载MobX:
npm i mobx mobx-react注意版本,课程使用的是mobx - ^5.15.4、mobx-react - ^6.2.2 - 工作流程:action(指令) => state(状态) => views(视图),视图想修改状态就必须先触发action,state会自动修改视图
创建并使用store状态
- store对象需要是一个类的实例对象,在类内部设置初始状态,然后把实例对象导出即可
- 使用modx-react的Provider将store对象传递下去,方法和redux相同
- 组件中引入mobx-react装饰器inject,使用装饰器获取store,然后在组件的props中即可获取store状态了
使用action修改store状态
- 直接把状态修改方法书写在最开始的store类中
- 只需要触发类中的方法即可修改状态,此时会发现修改后状态并不会同步给视图
- 使用mobx装饰器observable将状态数据变成可被观测数据
- 组件中使用mobx-react装饰器observer将组件设置为观察者
// src/stores/countStore.js 新建状态仓库
import { observable } from 'mobx'
// 创建store
class CountStore {
// observable实现可被观察功能 创建状态数据
@observable count = 1
// action
add = () => {
// 直接修改状态
this.count = this.count + 1
}
cut = () => {
this.count = this.count - 1
}
}
// 创建实例
const counter = new CountStore()
// 直接导出实例
export default counter
// src/index.js 底层引入并传递状态仓库
import { Provider } from 'mobx-react'
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import counter from './stores/countStore'
ReactDOM.render(
// 使用 Provider 将数据传递下去
<Provider counter={counter}>
<App />
</Provider>,
document.getElementById('root')
)
// src/App.js 组件获取并使用状态、使用action
import { inject, observer } from 'mobx-react'
import React, { Component } from 'react'
// 接收状态
@inject('counter')
// 设置观察模式,当状态变化自动更新
@observer
// 组件
class App extends Component {
render() {
// 解构获取状态
const { counter } = this.props
return (
<div>
{/* 点击直接触发action */}
<button onClick={counter.cut}>-</button>
{/* 展示状态 */}
{counter.count}
<button onClick={counter.add}>+</button>
</div>
)
}
}
export default App
禁止普通函数更改程序状态并引入action装饰器
为了避免任意一个函数都可以更改状态,需要禁止这些默认行为,避免不必要的错误出现
启用严格模式,只让action函数更改状态,设置action装饰器后,非action函数想要更改状态会立即报错
import { action, configure, observable } from 'mobx'
// 配置mobx,开启严格模式
configure({ enforceActions: 'observed' })
// 创建store
class CountStore {
// observable实现可被观察功能 创建状态数据
@observable count = 1
// action,添加action装饰器,没有装饰器的函数一旦尝试修改状态就会报错
@action add = () => {
// 直接修改状态
this.count = this.count + 1
}
@action cut = () => {
this.count = this.count - 1
}
}
// 创建实例
const counter = new CountStore()
// 直接导出实例
export default counter
更正类中的普通函数this指向
上面例子中,我们定义的action韩式都是一个箭头函数,这避免了this指向错误的问题,但在大多数情况下,我们更愿意使用普通函数作为对象action函数,这就要求我们需要设置一下普通函数中this指向的问题
// 箭头函数不需要修正this指向
@action add = () => {
// 直接修改状态
this.count = this.count + 1
}
// 修改普通函数的this指向问题
@action.bound cut() {
this.count = this.count - 1
}
异步更新状态
当action函数中存在异步操作的时候,我们不能直接修改状态数据
- 修改方法1:使用mobx中的runInAction()方法修改,runInAction函数接受一个函数,在此函数内修改数据
- 修改方法2:使用mobx的flow函数,函数接收一个函数作为参数(function*),在参数内部直接修改即可
import { action, configure, flow, observable, runInAction } from 'mobx'
import axios from 'axios'
// 配置mobx,开启严格模式
configure({ enforceActions: 'observed' })
// 创建store
class CountStore {
// observable实现可被观察功能 创建状态数据
@observable count = 1
@observable users = []
// action,添加action装饰器,没有装饰器的函数一旦尝试修改状态就会报错
@action add = () => {
// 直接修改状态
this.count = this.count + 1
}
// 修改普通函数的this指向问题
@action.bound cut() {
this.count = this.count - 1
}
// 异步请求处理方案1
// @action.bound async getUsers() {
// const { data } = await axios.get('https://api.github.com/users')
// // 异步请求直接使用runInAction修改状态
// runInAction(() => (this.users = data))
// }
// 异步请求处理方案2,使用flow处理异步函数,此时就需要书写装饰器了
getUsers = flow(function* () {
const { data } = yield axios.get('https://api.github.com/users')
this.users = data
})
}
// 创建实例
const counter = new CountStore()
// 直接导出实例
export default counter
数据监测
computed计算值
计算值是指可以根据现有的状态或其他计算值衍生出的值
当模版中有复杂逻辑的时候,可将复杂逻辑从模版中抽离出来
使用mobx的computed装饰器可做计算值的处理,并使用get修饰,用法类似于Vue的计算属性
import { computed } from 'mobx'
// 使用修饰器把函数变成计算值,并设置为get方法
@computed get getRes() {
// 返回计算值即可
return this.count * 10
}
// 使用计算值,直接调用即可,不需要加()执行
{counter.getRes}
autorun方法
当监测的状态发生变化时,想根据状态产生相应的效果
会在初始化的时候执行一次,然后再梅西状态变化时执行
例如当用户输入用户名的时候实时检测这个用户名是否存在
// src/stores/countStore.js mobx
import { action, autorun, configure, observable } from 'mobx'
// 配置mobx,开启严格模式
configure({ enforceActions: 'observed' })
// 创建store
class CountStore {
constructor() {
// autorun要写在constructor中
// 参数1:执行方法,参数2:配置参数
autorun(
() => {
try {
// 引入判断方法判断当前用户名是否已存在
UserNameDistinguish(this.userName)
// 用户名不存在显示可用
console.log('用户名可用')
} catch (e) {
// 用户名存在报错
console.log(e.message)
}
},
// 设置延时检测时间2s
{ delay: 2000 }
)
}
@observable userName = ''
// 修改用户名action
@action.bound changeValue(value) {
this.userName = value
}
}
// 创建实例
const counter = new CountStore()
// 判断用户名是否存在的函数
function UserNameDistinguish(value) {
return new Promise((resolve, reject) => {
// 判断用户名
if (value === 'admin') {
reject('用户名已存在')
} else {
resolve()
}
})
}
// 直接导出实例
export default counter
// src/App.js 组件
import { inject, observer } from 'mobx-react'
import React, { Component } from 'react'
// 接收状态
@inject('counter')
// 设置观察模式,当状态变化自动更新
@observer
// 组件
class App extends Component {
render() {
// 解构获取状态
const { counter } = this.props
return (
<div>
{/* 文本框,用来输入用户名 */}
<input
type='text'
value={counter.userName}
onChange={e => counter.changeValue(e.target.value)}
/>
</div>
)
}
}
export default App