1、前言
本文章项目的依赖包及其版本如下:
Package Name | Version |
---|---|
antd | ^3.16.6 |
connected-react-router | ^6.4.0 |
customize-cra | ^0.2.12 |
immutable | ^4.0.0-rc.12 |
react | ^16.8.6 |
react-app-rewired | ^2.1.1 |
react-redux | ^7.0.3 |
react-router-config | ^5.0.0 |
react-router-dom | ^5.0.0 |
react-scripts | 3.0.1 |
redux | ^4.0.1 |
redux-logger | ^3.0.6 |
redux-persist | ^5.10.0 |
redux-persist-expire | ^1.0.2 |
redux-persist-transform-immutable | ^5.0.0 |
redux-saga | ^1.0.2 |
2、准备工作,搭建项目
下面是我的项目结构,每个人或者每个公司都有自己的目录架构,这里我的只供大家参考,另外搭建项目过程和介绍如何使用immutable.js不是本文章的重点,如何使用immutable.js以及本文章相关代码后面我会给出,如果有疑问欢迎大家在下面留言
|-- App.js
|-- index.js
|-- serviceWorker.js
|-- assets
| |-- audio
| |-- css
| | |-- App.scss
| | |-- base.scss
| | |-- index.css
| | |-- override-antd.scss
| |-- image
| | |-- Welcome.png
| | |-- awbeci.png
| | |-- bgLogo.png
| | |-- hiy_logo.png
| | |-- indexPop1.png
| | |-- indexPop2.png
| | |-- logo.png
| | |-- logoX.png
| | |-- right.png
| |-- video
|-- components
| |-- HOC
| | |-- loading.js
| |-- common
| |-- layout
| |-- AppRoute.js
| |-- LayoutPage.js
| |-- Loading.js
| |-- MasterPage.js
| |-- RouterView.js
| |-- SideMenu.js
| |-- layoutPage.scss
| |-- masterPage.scss
|-- config
| |-- base.conf.js
|-- context
| |-- themeContext.js
|-- pages
| |-- DepartmentManage.js
| |-- Index.js
| |-- NoFound.js
| |-- NoPermission.js
| |-- UserManage.js
| |-- login
| |-- Login.js
| |-- login.scss
|-- redux
| |-- actions
| | |-- authAction.js
| | |-- layoutPageAction.js
| |-- middleware
| | |-- authTokenMiddleware.js
| |-- reducers
| | |-- authReducer.js
| | |-- index.js
| | |-- layoutPageReducer.js
| |-- sagas
| | |-- authSaga.js
| | |-- index.js
| |-- store
| | |-- index.js
| |-- thunks
|-- router
| |-- index.js
|-- service
| |-- apis
| | |-- 1.0
| | |-- index.js
| | |-- urls.js
| |-- mocks
| | |-- 1.0
| | |-- index.js
| | |-- testMock.js
| |-- request
| |-- ApiRequest.js
| |-- MockRequest.js
|-- test
| |-- App.test.js
|-- utils
3、集成immutable.js
此项目除了依赖包要配置之外,只有redux下的reducer相关文件会设置成immutable.js普通的react组件我没有设置成immutable.js
App.js
import { ConnectedRouter } from "connected-react-router";
//换成
import { ConnectedRouter } from "connected-react-router/immutable";
store->index.js
import { routerMiddleware } from "connected-react-router";
//换成
import { routerMiddleware } from "connected-react-router/immutable";
//添加
import immutableTransform from "redux-persist-transform-immutable";
const persistConfig = {
transforms: [encryptor],
//换成
transforms: [
immutableTransform()
// 注意:必须要注释encryptor否则会报错
// encryptor
],
...
}
redux->reducers->index.js
import { connectRouter } from "connected-react-router";
//换成
import { connectRouter } from "connected-react-router/immutable";
redux->reducers->authReducer.js
//添加
import { Map, fromJS, merge } from "immutable";
const initState = {
user: null,
token: ""
};
[authTypes.AUTH_SUCCESS]: (state, action) => {
return Object.assign({}, state, { user: action.data.user, token: action.data.token });
},
[authTypes.SIGN_OUT]: (state, action) => {
return Object.assign({}, state, {
user: null,
token: ""
});
}
//换成
const initState = fromJS({
user: null,
token: ""
});
[authTypes.AUTH_SUCCESS]: (state, action) => {
return state.merge({
user: action.data.user,
token: action.data.token
});
},
[authTypes.SIGN_OUT]: (state, action) => {
return state.merge({
user: null,
token: ""
});
}
redux->reducers->layoutPageReducer.js
//添加
import { Map, fromJS, merge, List } from "immutable";
const initState = {
index: "126",
subIndex: "",
collapsed: false,
menus: [],
loading: false
};
[layoutPageTypes.SAVE_MENU_INDEX]: (state, action) => {
const { keyPath } = action.payload;
let index = keyPath[0];
let subIndex = null;
if (keyPath.length === 2) {
subIndex = keyPath[1];
}
return Object.assign({}, state, {
index: index,
subIndex: subIndex
});
},
[layoutPageTypes.SAVE_MENU_COLLAPSED]: (state, action) => {
const { collapsed } = action.payload;
return Object.assign({}, state, {
collapsed: collapsed
});
},
[layoutPageTypes.GET_MENUS]: (state, action) => {
const { menus } = action;
return Object.assign({}, state, {
menus: menus
});
}
//换成
const initState = fromJS({
index: "126",
subIndex: "",
collapsed: false,
menus: List()
});
[layoutPageTypes.SAVE_MENU_INDEX]: (state, action) => {
const { keyPath } = action.payload;
let index = keyPath[0];
let subIndex = null;
if (keyPath.length === 2) {
subIndex = keyPath[1];
}
return state.merge({
index: index,
subIndex: subIndex
});
},
[layoutPageTypes.SAVE_MENU_COLLAPSED]: (state, action) => {
const { collapsed } = action.payload;
return state.merge({
collapsed: collapsed
});
},
[layoutPageTypes.GET_MENUS]: (state, action) => {
const { menus } = action;
return state.merge({
menus: menus
});
}
middleware->authTokenMiddleware.js
//添加
import { fromJS } from "immutable";
if (action.type === REHYDRATE) {
if (action.payload && action.payload.authReducer && action.payload.authReducer.token) {
ApiRequest.setToken(action.payload.authReducer.token);
}
}
//换成
if (action.type === REHYDRATE) {
if (typeof action.payload !== "undefined") {
let authReducer = action.payload.authReducer;
if (authReducer) {
const token = authReducer.get("token");
ApiRequest.setToken(token ? token : null);
}
}
}
components->layout->LayoutPage.js
<span style={{ marginLeft: 8 }}>{authReducer.user ? authReducer.user.name : "用户"}</span>
<Switch>{authReducer.token ? renderRoutes(routes) : <Redirect to="/login" />}</Switch>
//换成
<span style={{ marginLeft: 8 }}>{authReducer.get("user") ? authReducer.getIn(["user", "name"]) : "用户"}</span>
<Switch>{authReducer.get("token") ? renderRoutes(routes) : <Redirect to="/login" />}</Switch>
components->layout->SideMenu.js
let menus = layoutPageReducer.menus;
<Menu
theme="light"
defaultSelectedKeys={[layoutPageReducer.index]}
selectedKeys={[layoutPageReducer.index]}
defaultOpenKeys={[layoutPageReducer.subIndex]}
mode="inline"
className="sider-menu-container"
inlineCollapsed={layoutPageReducer.collapsed}
onClick={this.clickSidebarMenu}
>
{newMenus}
</Menu>
//换成
let menus = layoutPageReducer.get("menus");
<Menu
theme="light"
defaultSelectedKeys={[layoutPageReducer.get("index")]}
selectedKeys={[layoutPageReducer.get("index")]}
defaultOpenKeys={[layoutPageReducer.get("subIndex")]}
mode="inline"
className="sider-menu-container"
inlineCollapsed={layoutPageReducer.get("collapsed")}
onClick={this.clickSidebarMenu}
>
{newMenus}
</Menu>
components->layout->AppRoute.js
Authentication() {
return this.props.store.authReducer.token ? <Redirect to="/" /> : <Login />;
}
//换成
return this.props.store.authReducer.get("token") ? <Redirect to="/" /> : <Login />;
4、启动项目看是否集成成功
命令行运行:yarn start
清除下缓存尤其是localstorage,如果看到上面的登录页面那么恭喜你配置成功!
5、redux-logger 打印immutable.js日志
下面我们来看看redux-logger打印的日志跟我们不使用immutable.js有什么区别?
如上图所示,打印的日志数据就是immutable.js的相关数据,但是我们会发现阅读性不是太好,这里我们安装一个格式化immutable.js的日志数据的Chrome插件,安装一下即可,之前我有写过一篇文章教你怎么使用这个插件,点击跳转阅读,安装好之后,我们再来查看数据,如下所示:
是不是增加了日志的可阅读性,到此我们集成Immutable.js就至此结束,欢迎大家在下面留言交流!
6、总结
1)上面是教你如何把现有项目集成immutable.js,如果你是新项目,上面有些配置其实是不用管的;
2)上面的集成原则:把原生的写法改成immutable.js的写法即可!;
3)这里我用到了redux-persist,如果你没有用到这个包,那么你就可以不用安装redux-persist-transform-immutable,换成redux-immutable包即可,具体如何操作,官方文档上面有讲,这里就不多说了。
4)演示地址、本文章代码,另外本文章代码对应github上面的immutable.js分支,而演示地址对应的是master分支!
7、引用
1.Immutable.js了解一下?
2.React引用数据类型与immutable.js的使用实例
3.react 使用的小建议
4.Immutable.js 以及在 react+redux 项目中的实践
5.React Native Redux、redux-persist、immutable.js 结合实践
6.Error when using redux-persist with redux-immutable
7.大话immutable.js
8.Immutable.js 以及在 react+redux 项目中的实践
9.Immutable.js与React,Redux及reselect的实践
10.如何用React+Redux+ImmutableJS进行SPA开发
11.immutable.js 在React、Redux中的实践以及常用API简介
12.笔记, immutable-js 基础操作
13.Immutable.js 到底值不值得用?
14.Immutable 详解及 React 中实践
15.Immutable.js与React,Redux及reselect的实践
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。