Flux
Flux 是一种架构思想,专门解决软件的结构问题。它跟MVC架构是同一类东西,但是更加简单和 清晰。Flux存在多种实现(至少15种)
https://github.com/voronianski/flux-comparison
Facebook Flux是用来构建客户端Web应用的应用架构。它利用单向数据流的方式来组合React 中的视图组件。它更像一个模式而不是一个正式的框架,开发者不需要太多的新代码就可以快速的上手Flux。
redux(重点)
redux介绍及设计
-
redux 是flux架构模式中15种实现中的一种。可以跟vue react,jq …一起用,只是更习惯跟react 一起用
-
redux共享状态管理,把所有需要共享的状态统一放到redux中管理,组件里面不再放状态,通过redux共享状态管理。实现组件之间直接的通信(类似vuex)。状态一旦被共享,需要严格遵循工作流程,可以通过redux-devtools调式工具查bug
-
Redux最主要是用作应用状态的管理。简言之,Redux用一个单独的常量状态树 (state对象)保存这一整个应用的状态,这个对象不能直接被改变。当一些数据变化了,一个新的对象就会被创建(使用actions和reducers),这样就可以进行数据追踪,实现时光旅行。
redux使用三大原则
- state 以单一对象存储在store 对象中
- state只读(每次都返回一个新的对象)
- 使用纯函数reducer 执行 state 更新
redux 工作流
-
面试重点:请说一下flux ,redux ,react-redux怎么理解的?
flux:是一种架构思想,专门解决软件结构问题。
redux是flux架构模式中15种实现中的一种。实现状态的共享,组件之间的通信。
react-redux 是对redux的进一步封装,专门提供给react使用
-
Redux的运作流程:
在组件上,调用store.dispatch(action)方法,并携带action数据。
然后store自动调用reducer函数,store传递两个参数给reducer函数:当前state和收到的action。其中,reducer函数必须是一个纯函数,该函数会返回一个新的state。
根reducer会把多个子reducer的返回结果合并成最终的应用状态,在这一过程中,可以使用Redux提供的combineReducers方法。使用combineReducers方法时,action会传递给每个子的reducer进行处理,在子reducer处理后会将结果返回给根reducer合并成最终的应用状态。
store调用store.subscribe监听state的变化,state一旦发生改变就会触发store的更新,最终view会根据store数据的更新刷新界面。
- 页面调用diapatch方法,携带action数据。
- reducer监听到转发过来的action,处理请求,修改存放在store里的状态state。
- 页面订阅store里的状态,一旦状态发生改变,就会触发。得到第二步修改后最新的状态值
redux使用
什么情况下使用redux
- 总体原则:能不用就不用
- 某个组件的状态需要共享
- 某个状态需要在任何地方都可以拿到
- 一个组件需要改变全局状态
- 一个组件需要改变另一个组件的状态
下载依赖包
npm install --save redux
- 新建redux|store.js 文件。导入redux ,创建store对象 并导出
import {createStore} from 'redux'
const reducer = ()=>{
// 修改状态只能在reducer中进行修改
}
const store = createStore() // 控制共享状态
// 通过store.subscribe和 store.dispatch (store 的原理就是订阅发布模式)
export default store
- reducer 修改状态的地方
更改状态通过reducer,react 要求不能直接改状态,改之前需要深复制一份
const reducer = (prevState={ // es6默认值,可以管理共享状态
// 初始状态
isCollapsed:false, //是否折叠
list:[], //共享数组
userInfo:{}, //共享用户登录信息
})=>{
// reducer相当于 vue 的mutations
// 修改状态只能在reducer中进行修改。保留老状态返回新状态
// 1.传入prevState 老的状态 (接收状态)
// 2. 深复制一个新的状态(返回状态)
let newState = {...prevState}
return prevState
}
redux的核心API
createStore()
作用:创建包含指定reducer的对象
import {createStore} from 'redux'
import counter from './reducers/counter'
const store = createStore(counter)
store对象:
作用:redux库最核心的管理对象。 它的内部维护着:state,reducer
核心方法:getState() 、dispatch(action)、 subscribe(listener)
store.getState()
store.dispatch({type:'INCREMENT', number})
store.subscribe(render)
applyMiddleware()
作用:应用上基于redux的中间件(插件库)
import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk' // redux异步中间件
const store = createStore(
counter,
applyMiddleware(thunk) // 应用上异步中间件
)
combineReducers()
作用:合并多个reducer函数
export default combineReducers({
user,
chatUser,
chat
})
redux的三个核心概念
action
标识要执行行为的对象;包含2个属性:
type: 标识属性,值为字符串,必要属性
xxx:数据属性,值类型任意,可选属性
const action = { type: 'INCREMENT', data: 2 }
/** Action Creator(创建Action的工厂函数) **/
const increment = (number) => ({type: ‘INCREMENT’, data: number})
reducer:
根据旧的state和action, 产生新的state的纯函数
export default function counter(state=0, action) {
switch (action.type) {
case 'INCREMENT':
return state + action.data
case 'DECREMENT':
return state - action.data
default:
return state
}
}
注意:返回一个新的状态,不要修改原来的状态
store:
将state,action与reducer联系在一起的对象:
import {createStore} from 'redux'
import reducer from './reducers'
const store = createStore(reducer)
/**
此对象的功能:
getState(): 得到state
dispatch(action): 分发action, 触发reducer调用, 产生新的state
subscribe(listener): 注册监听, 当产生了新的state时, 自动调用
**/
news后台系统项目案例:
显示隐藏侧边栏
- redux|store.js (状态管理的文件 )
import { createStore } from 'redux'
const reducer = (prevState = { // prevState 每次再执行的时候会传入上一个状态
isCollapsed: false, //是否折叠
list: [], //共享数组
userInfo: {}, //共享用户登录信息
}, action) => {
// 修改状态只能在reducer中进行修改。保留老状态返回新状态
let newState = { ...prevState } // 深复制,后期只能改newState
/*===== 改变状态 =====*/
let { type } = action
switch (type) { // type从dispatch 中传入
case "change_collapsed":
newState.isCollapsed = !newState.isCollapsed
return newState
case "change_list":
return newState
case "change_userinfo":
return newState
default:
return prevState // 默认没改的 return 老状态
}
/*======================*/
}
const store = createStore(reducer)
export default store
-
被修改的组件内写订阅模式
store.subscribe(()=>{})
SideBar.js
const [selfCollapsed, setselfCollapsed] = useState(store.getState().isCollapsed)
useEffect(() => {
store.subscribe(() => {
// store.getState() 获取到最新的状态
setselfCollapsed(store.getState().isCollapsed)
})
}, [])
<Sider trigger={null} collapsible collapsed={selfCollapsed} >
{/* 不能直接 collapsed={store.getState().isCollapsed} */}
<div> 侧边栏菜单组件 </div>
</Sider>
退出登录后再登录会报以下警告:
Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function
注: react 切换路由会导致组件被销毁 ,组件内的状态和方法也会被销毁。而 useEffect 中的订阅是一个回调函数,函数不会被主动销毁,所以再登录的时候,退出登录后再重新登录,会出现重复订阅,走了两次回调。解决办法:需要取消订阅
retrun()=>{}
useEffect(() => {
var cancel = store.subscribe(()=>{
// 订阅完后会返回一个取消订阅的方法
setselfCollapsed(store.getState().CollapsedReducer.isCollapsed)
})
return ()=>{//取消订阅
cancel()
}
}, [])
- 需要改变状态的组件内写入发布模式
store.dispatch({type:"aaa", payload:"1111"})
如果需要传参 直接在dispatch对象中 通过key/value 值传到store 中
TopHeader.js
// 发布方法中必须传东西。类型会传入到store 中的action
const [isCollapsed, setisCollapsed] = useState(true)
const toggle = (isCollapsed) => {
setisCollapsed(!isCollapsed)
// 点击取反,状态被改变 ,把状态dispatch 到store
store.dispatch({ // 必须传入一个type 属性
type: "change_collapsed",
})
}
ActionCreator
// 生产一个函数,谁调用谁就有这个函数的东西
function CollapsedAction () {
return {
type: "change_collapsed"
}
}
export default CollapsedAction
store.dispatch(CollapsedAction())
纯函数
必须满足以下两个原则:
- 同样的输入得到同样的输出。
- 对外界没有副作用
纯函数在接收函数的时候,同样的参数返回同样的结果,并保证外面没有任何副作用
//================ 不是纯函数 ==================
var myname= "kerwin"
function test(myname){
myname="xiaoming"
return myname
}
test() // myname全局作用域 函数内对变量重新赋值。变量会受到影响
//==================纯函数==========================
var myname= "kerwin"
function test(myname){
myname="xiaoming"
return myname
}
test(myname) // 不会影响,myname被当成实参传进函数,函数内是对形参进行了改变,不会影响外面的变量
//================= 不是纯函数 ========================
var prevState = { myname:"kerwin" }
function test(prevState){
prevState.myname="xiaoming"
return prevState
}
test(prevState) // 会影响 复杂类型引用的是一个地址,里面改变会影响外面(浅拷贝),所以需要再深复制一份。就是纯函数设计
//================= 纯函数 ==========================
var prevState = { myname:"kerwin" }
function test(prevState){
var newState = {...prevState}
newState.myname="xiaoming"
return newState
}
test(prevState)
总结:reducer保持纯函数设计
reducer 拆分
如果不同的action所处理的属性之间没有联系,我们可以把 Reducer 函数拆分。 不同的函数负责处理不同属性,最终把它们合并成一个大的 Reducer 即可。
多人合作开发项目的时候需要 拆分 Reducer 成多个文件 ,保证不会改同一个文件, 自己管理自己的reducer ,最终再 store 中通过combineReduces()
合并成一个大的 Reducer。
例:把控制折叠的状态拆分出来成一个单独的 Reducer
const CollapsedReducers = (prevState = {
isCollapsed: false, //是否折叠
}, action) => {
let newState = { ...prevState } // 深复制,后期只能改newState
// 改变状态
let { type } = action
switch (type) { // type从dispatch 中传入
case "change_collapsed":
newState.isCollapsed = !newState.isCollapsed
return newState
default:
return prevState // 默认没改的 return 老状态
}
}
export default CollapsedReducers
store.js 中把拆分出的 Reducer 函数进行合并
import { createStore, combineReducers } from 'redux'
import RightListReducer from './reducers/RightListReducer'
import CollapsedReducers from './reducers/CollapsedReducers'
// Reducer 函数拆分,最终通过combineReduces()合并成一个大的 Reducer 即可。
const reducer = combineReducers({
RightListReducer,
CollapsedReducers
})
const store = createStore(reducer)
export default store
注:合并的时候
combineReduces
会把每一个拆分的reducer 的进行遍历,订阅的时候需要多点一层到里面的对象
侧边栏中订阅需要多点一层
store.subscribe(() => {
setselfCollapsed(store.getState().CollapsedReducers.isCollapsed)
})
总结:
redux 是一个专门的状态管理库(在vue等当中可以使用 但是在react中会比较多)。集中的管理react中多个组件的状态
需求场景:
某个组件的状态需要共享的时候
一个组件需要改变另外一个组件状态的时候
组件中的状态需要在任何地方都可以拿到三大原则:
1.单一数据源 整个react中的状态都会被统一的管理到store
2.state是只读的 我们不能直接改变state 而是要通过触发redux中的特定方法来进行修改
3.使用纯函数来执行修改操作:action来改变redux中的state
-
面试:了解 redux 么,说一下 redux 吧
-
redux 是一个应用数据流框架,主要是解决了组件间状态共享的问题,原理是集中式管理,主要有三个核心方法,action,store,reducer,工作流程是view 调用 store 的 dispatch 接收 action 传入 store,reducer 进行 state 操作,view 通过 store 提供的 getState 获取最新的数据,flux 也是用来进行数据操作的,有四个组成部分 action,dispatch,view,store,工作流程是view 发出一个 action,派发器接收 action,让 store 进行数据更新,更新完成以后 store 发出 change,view 接受 change 更新视图。Redux 和 Flux 很像。主要区别在于 Flux 有多个可以改变应用状态的 store,在 Flux 中dispatcher 被用来传递数据到注册的回调事件,但是在 redux 中只能定义一个可更新状态的 store,redux 把 store 和 Dispatcher 合并,结构更加简单清晰
-
新增 state,对状态的管理更加明确,通过 redux,流程更加规范了,减少手动编码量,提高了编码效率,同时缺点时当数据更新时有时候组件不需要,但是也要重新绘制,有些影响效率。一般情况下,我们在构建多交互,多数据流的复杂项目应用时才会使用它们
-
redux 有什么缺点
一个组件所需要的数据,必须由父组件传过来,而不能像 flux 中直接从store 取。
当一个组件相关数据更新时,即使父组件不需要用到这个组件,父组件还是会重新 render,可能会有效率影响,或者需要写复杂的shouldComponentUpdate 进行判断。
redux中间件
在redux里,action仅仅是携带了数据的普通js对象。action creator返回的值是这个 action类型的对象。然后通过store.dispatch()进行分发。同步的情况下一切都很完美,但是reducer 无法处理异 步的情况。 那么我们就需要在action和reducer中间架起一座桥梁来处理异步。这就是 middleware。
redux 的 异步状态
reducer 不能走异步 。需要 通过redux 中间件
News后台系统项目案例:
例:某个页面(RightList) 通过异步ajax 取回来的数据(不常更新的数据),后期刷新的时候不需要重复请求,而是走缓存 。
useEffect(()=>{
if(store.getState().RightListReducer.list.length===0){ // 数组长度为0进行 ajax第一次请求
}else{ // 不等于0,说明已经有数据,直接走缓存
}
})
RightList.js
const [dataSource, setdataSource] = useState([])
// 异步请求数据
useEffect(() => {
if (store.getState().RightListReducer.list.length === 0) { // 数组长度为0进行 ajax第一次请求
store.dispatch(RightListAction()) // RightListAction()立即执行
} else { // 不等于0,说明已经有数据,直接走缓存
}
}, [])
RightListAction.js
import http from "../../util/http"
function RightListAction () {
// 错误写法:
/* http.get("/rights?_embed=children").then(res => {
console.log(res.data);
return {
type: "change_list",
payload:res.data
}
}) */
// 正确写法:
return http.get("/rights?_embed=children").then(res => {
var list = res.data.map(item => {
if (item.children.length === 0) {
delete item.children
return item
}
return item
})
return {
type: "change_list",
payload: res.data
}
})
// 在异步前面加return 返回出去的是一个promise对象,但会报错:Actions must be plain objects
}
export default RightListAction
http.get() 是异步请求ajax ,而RightListAction()是立即调用,不会等异步回来的数据,所以在异步里面return会返回一个undefined。在http.get异步外面return,数据还没取回来就return。会造成页面没有数据,因为已经渲染完了,才异步取回的数据,已经晚了。
Actions must be plain objects. Instead, the actual type was: 'Promise'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions. See https://redux.js.org/tutorials/fundamentals/part-4-store#middleware and https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-the-redux-thunk-middleware for examples
解决:需要引用中间件
方案一:redux-promise( 需要npm 下载 )
在store.js 中
import { createStore, combineReducers ,applyMiddleware} from 'redux'
import RightListReducer from './reducers/RightListReducer'
import reduxPromise from 'redux-promise'
const reducer = combineReducers({
RightListReducer
})
// applyMiddleware(reduxPromise) 提供一个reduxPromise中间件
const store = createStore(reducer,applyMiddleware(reduxPromise))
export default store
默认 store.dispatch
只支持 普通对象 { type:“change_collapge”}
应用reduxPromise 中间件之后, store.dispatch
支持 promise对象
页面中请求数据RightList.js
useEffect(() => {
if (store.getState().RightListReducer.list.length === 0) { // 数组长度为0进行 ajax第一次请求
store.dispatch(RightListAction()) // RightListAction()立即执行
} else { // 不等于0,说明已经有数据,直接走缓存
setdataSource(store.getState().RightListReducer.list)
}
const cancel = store.subscribe(() => {
setdataSource(store.getState().RightListReducer.list)
})
return () => {
cancel() // 取消订阅
}
}, [])
function RightListAction () {
return http.get("/rights?_embed=children").then(res => {
var list = res.data.map(item => {
if (item.children.length === 0) {
delete item.children
return item
}
return item
})
return {
type: "change_list",
payload: res.data
}
})
}
请求回数据后对状态进行赋值
const RightListReducer = (prevState = {
list: [], //共享数组
}, action) => {
let newState = { ...prevState } // 深复制,后期只能改newState
// 改变状态
let { type, payload } = action
switch (type) { // type从dispatch 中传入
case "change_list":
newState.list = payload
return newState
default:
return prevState // 默认没改的 return 老状态
}
}
export default RightListReducer
方案二:thunkPromise
下载:
npm i --save redux-thunk
import {createStore,combineReducers,applyMiddleware} from 'redux'
import RightListReducer from './reducers/RightListReducer'
import reduxThunk from 'redux-thunk'
const reducer = combineReducers({
RightListReducer
})
const store = createStore(reducer,applyMiddleware(reduxThunk))
// 应用thunkPromise 中间件之后, `store.dispatch` 支持 function对象
export default store
在RightsList.js组件中 store.dispatch()
useEffect(() => {
if (store.getState().RightListReducer.list.length === 0) { // 数组长度为0进行 ajax第一次请求
store.dispatch(RightListAction()) // RightListAction()立即执行
} else { // 不等于0,说明已经有数据,直接走缓存
setdataSource(store.getState().RightListReducer.list)
}
const cancel = store.subscribe(() => {
setdataSource(store.getState().RightListReducer.list)
})
return () => {
cancel() // 取消订阅
}
}, [])
function RightListAction () {
return (dispatch)=>{
http.get("/rights?_embed=children").then(res => {
var list = res.data.map(item => {
if (item.children.length === 0) {
delete item.children
return item
}
return item
})
dispatch({
type:"change_list",
payload:list
})
})
}
}
请求回数据后对状态进行赋值
const RightListReducer = (prevState = {
list: [], //共享数组
}, action) => {
let newState = { ...prevState } // 深复制,后期只能改newState
// 改变状态
let { type, payload } = action
switch (type) { // type从dispatch 中传入
case "change_list":
newState.list = payload
return newState
default:
return prevState // 默认没改的 return 老状态
}
}
export default RightListReducer
i. 中间件的由来与原理、机制
export default function thunkMiddleware({ dispatch, getState }) {
return next => action =>
typeof action === 'function' ?
action(dispatch, getState) :
next(action);
}
react-redux
react-redux:一个插件库, 基于redux 的再次封装,专门给react 用的
react-redux 提供一个新的组件 connect (包装组件),在自己的组件在外面再包一层,由connect 来完成订阅发布 。 在最外层在包一个provide组件,
将所有组件分为两大类:
UI组件:
-
只负责UI的呈现,不带有任何业务逻辑
-
通过props接受数据(一般数据和函数)
-
不使用任何redux的API
-
一般保存在components文件夹中
-
List item
容器组件:
负责管理数据和业务逻辑,不负责UI呈现
- 使用redux的API
- 一般保存在containers文件夹下
下载
npm i --save react-redux
Provider:
让所有组件都可以得到state数据
在根组件(App.js) 最外层包一个供应商组件 Provider
,并传入store
属性
import './App.css';
import NewsRouter from './router';
import { Provider } from 'react-redux'
import store from './redux/store';
function App () {
return (
<Provider className="App" store={store}>
<NewsRouter></NewsRouter>
</Provider>
);
}
export default App;
connect():
用于包装UI组件生成容器组件(将组件与react-redux连接起来)
显示隐藏侧边栏: 主要是通过控制 isCollapsed
react-redux 写法 在SideBar外面套一个connect(父组件),通过connect 来订阅
import { connect } from 'react-redux'
function SideBar (props) {
.....
<Sider trigger={null} collapsible collapsed={props.isCollapsed} >
{/*
侧边栏组件。。。。显示隐藏。。
collapsed={props.isCollapsed}
connect已经把状态映射给Sidebar Sidebar里面要用只需通过props.isCollapsed
*/}
</Sider>
}
const mapStateToProps = (state) => { // 映射状态到属性(state store中的状态)
return {
isCollapsed: state.CollapsedReducers.isCollapsed
}
}
// 此时 SideBar 的父组件是 connect,。。。。
export default connect(mapStateToProps)(SideBar)
TopHeader 图标的切换
// 从redux action中导入 CollapsedAction
import CollapsedAction from '../../redux/ActionCreator/CollapsedAction';
function SideBar (props) {
....
const toggle = (isCollapsed) => {
setisCollapsed(!isCollapsed)
props.CollapsedAction()
}
.....
}
// react-redux 写法:
const mapStateToProps = (state) => { // 映射状态到属性 state store中的状态
return {
isCollapsed: state.CollapsedReducers.isCollapsed
}
}
const mapStateToDispatch = { //
CollapsedAction
}
//引入action函数
export default connect(
mapStateToProps, //映射状态成为属性,是一个函数返回的是一个对象
mapStateToDispatch //是一个对象,返回的是一个action方法。最终会转化为dispatch调用的一个函数
)(TopHeader)
mapStateToprops()
将外部的数据(即state对象)转换为UI组件的标签属性
const mapStateToprops = function (state) {
return {
value: state
}
}
mapDispatchToProps()
将分发action的函数转换为UI组件的标签属性
简洁语法可以直接指定为actions对象或包含多个action方法
const mapStateToDispatch = { // 通过connect dispatch(RightListAction)
geiAjaxRightListAction,
changeRightListAction,
deleteRightListAction,
addRightListAction
}
异步请求
例:异步请求权限列表数据
- 在redux|action 文件 中定义 方法:
/***** RightListAction.js ******/
import http from "../../util/http" // 自己封装的axios
function geiAjaxRightListAction () {
// http.get 是异步发起ajax 而函数是立即调用,不会等异步回来的数据,所以在异步里面return会返回一个undefined。在http.get异步外面return也不行,数据还没取回来就return。会造成页面没有数据,因为已经渲染完了,才异步取回的数据,已经晚了。而在异步前面加return 返回出去的是一个promise对象
return http.get("/rights?_embed=children").then(res => {
// 业务除了:没有子菜单的也有children字段,需要把这个children删掉,否则会遍历一个空数组 ,删除对象中的一个属性,直接用delete
var list = res.data.map(item => {
if (item.children.length === 0) {
delete item.children
return item
}
return item
})
return { // 传给 reducer中
type: "getdata_list",
payload: list
}
})
}
- 组件内引入action
/*** RightList.js *****/
// 1- 引用redux 相关
import { connect } from 'react-redux'
import { geiAjaxRightListAction } from '../../../redux/ActionCreator/RightListAction'
function RightList (props) {
// 4- 组件内调用action
useEffect(() => {
props.geiAjaxRightListAction()
}, [])
}
// 2- 将分发action的函数转换为UI组件的标签属性
const mapStateToDispatch = { //是一个对象,返回的是一个action方法。最终会转化为dispatch调用的一个函数
geiAjaxRightListAction
}
// 3- connect生成容器,是 RightList 的包装组件
export default connect( mapStateToDispatch)(RightList)
通过调用 geiAjaxRightListAction() 后 取回数据,然后通过reducer 赋给状态
- Reducer
const RightListReducer = (prevState = {
datalist: [] // 初始状态
}, action) => {
let newState = { ...prevState } // { ...prevState } 展开上个状态 重新赋给一份新的状态
let { type, payload } = action // 从action 中解构
switch (type) {
case "getdata_list":
newState.datalist = payload // 状态更新
return newState
default:
return prevState
}
}
export default RightListReducer
- 状态更新后组件内需要把reducer 的状态拿过来映射成自己组件中的属性
// 1- 映射状态成为属性,是一个函数返回的是一个对象
const mapStateToProps = (state) => {
return {
dataSource: state.RightListReducer.datalist
}
}
// 2- 通过connect生成容器调用mapStateToProps,
export default connect(mapStateToProps, mapStateToDispatch)(RightList)
// 3- 组件内使用状态就可以直接通过props.dataSource
<Table dataSource={props.dataSource} columns={columns} />;
redux-persist持久化
基本用法:
- 下载:
npm i --save redux-persist
- 基本用法
// 1.涉及添加`persistReducer`和`persistStore`到store.js 。
import { persistStore, persistReducer } from 'redux-persist'
// 2.默认数据持久化到localStorage
import storage from 'redux-persist/lib/storage' // 默认为localStorage
const persistConfig = {
key: 'test2008',
storage,
}
const reducer = combineReducers({
UserInfoReducer
})
// 把合并后的 reducer 送到 persistReducer(持久化reducer)
const persistedReducer = persistReducer(persistConfig, reducer)
const store = createStore(persistedReducer, composeEnhancers(applyMiddleware(reduxPromise)))
const persistor = persistStore(store)
export { store, persistor }
// 注,导出多个模块方式 export{},引用的时候也有 import {}
- 使用PersistGate包装您的根组件。这会延迟应用 UI 的呈现,直到您的持久状态被检索并保存到 redux。
// 根组件App.js
import { PersistGate } from 'redux-persist/integration/react'
// PersistGate 持久化的网关
export default function App () {
return (
<Provider className="App" store={store}>
<PersistGate loading={null} persistor={persistor}>
<NewsRouter></NewsRouter>
</PersistGate>
</Provider>
);
}
- 设置黑名单和白名单
// BLACKLIST
const persistConfig = {
key: 'root',
storage: storage,
blacklist: ['navigation'] // navigation will not be persisted
};
// WHITELIST
const persistConfig = {
key: 'root',
storage: storage,
whitelist: ['navigation'] // only navigation will be persisted
};
总结
vuex 和 redux 的区别:
-
底层上的区别:
vuex 底层是 getter/setter 拦截 拦截,最底层有监听。
redux 没有监听,底层是一套订阅发布模式,需要自己监听(subscribe),自己发布(dispatch)。 -
异步上的区别:
vuex 直接在action 里面做异步操作
redux 必须引用中间件(redux-promise/redux-thunk)。
-
状态上的区别:
vuex 原始状态必须被修改
redux 原始状态不能被修改,需要深复制一份,只能修改新的状态。
redux缺点:
-
流程复杂繁琐,每次状态更新都需要 dispatch => reducer => 订阅者
-
每次dispatch 所有的reducer 和 所有的订阅者都会响应,都会触发一遍,如果项目里订阅者过多的话,redux占的内存就会越来越大,会影响性能问题。
-
reducer 必须返回新状态,老状态不能受影响。所以必须要深复制。深复制存在的问题:所有状态的都会复制一遍,没有改变的状态也会受到影响被重新赋值一遍,所有有了immutable。
jq 缺点 :操作dom,需要亲自操作dom可能会出现性能问题
vue的缺点 :封装了很多语法糖,所以在操作的时候不是很自由,只能按照它规定的做,给什么用什么
react的缺点 :相对的自由,但是 什么都要自己造,用什么造什么