二十一、redux & mobx
二十一、redux & mobx
目标
- redux 状态管理详解
- redux/react-redux 代码解析
- mobx 状态管理详解
- mobx 与 redux 区别
- 手写一个 redux(⭐)
开始——状态管理
风格:知识体系
- react
- react source
- react native
- 实战
- node
- 8 月前
1.状态
举例: userList 渲染,一般编程关注的是(重要的是)userList 数据的状态(我们在这个过程中,什么是状态,状态就是 userList 当前所表现的一个形态;对我们来说,它代表当前页面、当前数据里,userList 在什么样的时候应该显示什么东西,做的是这样一个事情。)
function App() {
const [userList, setUserList] = useState([])
useEffect(() => {
fetchUser.then((res) => {
setUserList(res)
return (
{userList.map((item) => (
<li key={item.id}>{item.name}</li>
}
这里涉及几个事情:
首先,状态,web 是一个状态机。
- 我们不关心过程,我们关心的永远是,(当前)界面处于 哪个状态。
-
(那状态管理是什么?)状态管理,是一种在 web 的生命周期变化时,数据 model 所呈现出来的 view。
(这是我们的核心——什么是状态?状态就是页面当前它处于哪一种展示形态;比如对于这样一个例子来说,我们的心智模型是怎样的?界面进来 userList 是一个空数组,然后我们 fetchUser 调用前、后是什么样子?我们关心的永远是当前页面里,它的视图在什么样的情况下呈现什么样的内容,这个东西就是状态。状态,说白了就是你的视图 view 所呈现出来的效果,就是你的视图 view。)
(状态管理是什么?我们如何通过一些东西管理 view,管理我的视图当前处于一个什么样的形状或者形态。)
这个可能比较抽象,我们反过来想想,对于软件工程来说,它的核心是什么?
1.1 软件在做什么?
软件的核心,其实就是在管理数据(你们登录的信息、登录的状态存在哪里?Vuex。为什么要存在 Vuex 里?登录的状态存在 Vuex 里有什么问题?刷新会丢失,没错。所以大家发现没,我们在设计一个数据的时候,我们首先考虑什么?)。
我们首先考虑的是,一个数据的生命周期是什么?(有时候,大家想一想,一个数据,我们在设计功能、首先考虑功能的时候,就是这个功能需要什么数据,设计状态就是设计一个数据,设计它的生命周期是什么,作用范围是什么?)(刷新调接口是两码事)
数据设计:设计生命周期,作用范围
- DB,用户在,名字在,
- localStorage、sessionStorage 较长
- project runtime - 较短
- 状态管理的作用(项目运行时、全局存储)
- component [state, props, data]
刷新调接口:2 个 TTFB,我本该持久化的数据,我用时间换了空间, 评论列表,淘宝买东西,订单信息,调用接口请求回来, 进入购物车, 每个数据都有。。。
用较长的生命周期存储,取代较短的生命周期的数据,是一种最典型的性能优化手段,缓存设计。 react + k - diff
2.状态管理实现
redux 为主 & mobx
他们做了什么?
2.1 状态管理方法论
[1]组件之外,可以共享状态/数据;(有一部分数据在全局生命周期里(- project runtime - 较短))
- 闭包可以解决(怎么样让一个数据大家都能访问到?)。
CJS 方便理解,简单的一个例子:
- deps 是不是一个全局的、共享的数据,每个人都能修改它。
- 本质上就是一个闭包。
// 本质上就是一个闭包。
;(function (module, exports) {
const deps = {} // deps是不是一个全局的、共享的数据,每个人都能修改它。
function modifyDeps(val) {
deps.value = val
module.exports = {
modifyDeps
[2]有修改状态的明确方法,并且,能让其他的方法感知到;
- 发布订阅
- Proxy
这两者本质上有区别吗?也没有。
本质上,把 handler 放到一个地方,然后在一个合适的时间,执行一下。
[3]修改状态,会触发 UI 同步更新;
- forceUpdate
- Comsumer 和 Provider
- data.x = Math.random()
2.2 编码(redux & mobx)
2.2.1 创建&启动项目:
npx create-react-app redux-study
cd redux-study
npm start
2.2.2 依次创建 src 下的文件:
入口:
-
src/index.js
-
src/App.js
redux 原理:
-
src/data/data.js
-
src/data/index.jsx
模拟 redux:
-
src/store/redux.js
-
src/store/index.js
-
src/store/context.js
-
src/store/connectValue.js
redux 测试组件:
-
src/reduxTest.jsx
模拟 mobx:
-
src/mobx/index.js
-
src/mobx/api.js
-
src/mobx/A.jsx
引入 data & reduxTest & A 组件:
-
src/App.js
2.2.3 项目 redux-study
redux-study
- 仓库地址: redux-study
2.2.4 提问:为什么说 redux 是 immutable 的,数据不可变的?
// immutable
// const newState = f(state)
// state = newState;
// // mutable
// state.x.y
2.2.5 redux 补充说明
不考虑中间件的 redux 就这些。库:react-redux 。最早实现的就是这样的 api connect 。
redux 就是数据的状态管理,不涉及视图更新。dispatch action 触发 state 变化,state 变化,订阅它变化的人再去执行一下。
react-redux 这样的东西,提供了 connect 方法让我们可以触发视图更新,forceUpdate,本质上用 Consumer 和 Provider 提供了这样的全局的作用域。
2.2.6 mobx 补充说明(准备工作)
2.2.6.1 安装 mobx & mobx-react
teacher
- mobx@6.6.2
- mobx-react@7.5.3
myself
npm i mobx
npm install mobx-react --save
- mobx@6.9.0
- mobx-react@7.6.0
2.2.6.2 使用装饰器报错
2.2.6.2.1 原因
-
我使用的 create-react-app 脚手架创建的项目。
-
react 本身不支持装饰器,需要通过一些方式进行编译。
-
通过什么方式进行编译?babel。
使用 babel 需要安装@babel/plugin-proposal-decorators
并且配置; 详情见 Babel 官网——Installation
-
由于 create-react-app 脚手架工具已经对 webpack 做了一层封装,所以不太好去配置.babelrc 文件。
-
需要借助 react-app-rewired:扩展了 react-scripts 的包。
-
然后在项目根目录新建 config-overrides.js 文件:
根据文件名(不能写错)编译代码。
2.2.6.2.2 解决方案(没解决,碎觉)
【1】验证从 0-> 1,所以我把 node_modules 删除掉了。
【2】安装并使用 react-app-rewired
参考链接: https://www. npmjs.com/package/react -app-rewired
安装:
npm install react-app-rewired -D
根目录创建
config-overrides.js
:
module.exports = function override(config, env) {
//do stuff with the webpack config...
return config
修改 package.json:
/* package.json */
"scripts": {
- "start": "react-scripts start",
+ "start": "react-app-rewired start",
- "build": "react-scripts build",
+ "build": "react-app-rewired build",
- "test": "react-scripts test",
+ "test": "react-app-rewired test",
"eject": "react-scripts eject"
启动开发服务器:
npm start
嗯,还是报错:
redux-study\src\mobx\index.js: Support for the experimental syntax 'decorators' isn't currently enabled (8:3):
6 | }
7 | // 定义被观察的属性
> 8 | @observable count = 0
| ^
10 | // mobx其实很像 Vuex ,实现和 Vuex 也很像。
11 | @action.bound
Add @babel/plugin-proposal-decorators (https://github.com/babel/babel/tree/main/packages/babel-plugin-proposal-decorators) to the 'plugins' section of your Babel config to enable transformation.
If you want to leave it as-is, add @babel/plugin-syntax-decorators (https://github.com/babel/babel/tree/main/packages/babel-plugin-syntax-decorators) to the 'plugins' section to enable parsing.
ERROR in [eslint]
src\mobx\index.js
Line 8:2: Parsing error: This experimental syntax requires enabling one of the following parser plugin(s): "decorators", "decorators-legacy". (8:2)
webpack compiled with 2 errors and 1 warning
参考链接里提到 react-app-rewired@2.X 需要安装 customize-cra。
【3】所以继续,安装 customize-cra :
npm i customize-cra
把那个
For example
一粘,粘进
config-overrides.js
里,Ctrl + C 重启 npm start。
管用啦~ 之前是:webpack compiled with 2 errors and 1 warning 现在是:webpack compiled with 1 error and 1 warning
我的代码里有个
, { dataObj }
没有使用,可能 ESLint 报错了,注释掉,重启。
wow~ ⊙o⊙
Starting the development server...
Compiled successfully!
You can now view redux-study in the browser.
Local: http://localhost:3000
webpack compiled successfully
但是在 mobx/index.js 没有改动的保存又报错了
ERROR in [eslint]
src\mobx\index.js
Line 8:2: Parsing error: This experimental syntax requires enabling one of the following parser plugin(s): "decorators", "decorators-legacy". (8:2)
webpack compiled with 1 error and 1 warning
【4】那就开始看参考链接: React 的 decorators 装饰器报错@以及后续问题解决 ,本以为用不着了。
config-overrides.js
// 参考链接:https://blog.csdn.net/xh_jing/article/details/107570926
const path = require('path')
const {
override,
addDecoratorsLegacy
// disableEsLint
} = 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(
// enable legacy decorators babel plugin
addDecoratorsLegacy(),
// // disable eslint in webpack
// disableEsLint(),
customize()
运行后,继续报错,所以想知道
addDecoratorsLegacy()
是什么:
查看 node_modules 的 customize-cra:node_modules/customize-cra/dist/index.cjs.js ;
搜索 addDecoratorsLegacy,共有 3 处
看到这句(第 1 处),这是个立即执行函数:
addDecoratorsLegacy = () => (e) =>
addBabelPlugin(['@babel/plugin-proposal-decorators', { legacy: !0 }])(e)
exports.addDecoratorsLegacy = addDecoratorsLegacy