redux的原理和中间件
redux是一个状态管理的容器,它主要是用来保证程序行为的一致性,通过单一数据源来修改状态,方便我们使用工具调试
基本使用
redux主要有如下几个步骤
- 创建一个store来存放数据
createStore
- store里面通过
reducer
来初始化state,并定义state的修改规则 - 在业务层通过
dispatch
提交action来修改数据 - action提交到
reducer
函数中,根据传入的action
的type, 返回新的state
创建store (src/store/index.js) 主要是用createStore
import { createStore } from 'redux'
const reducer = function (state = 0, { type, payload = 1 }) {
switch (type) {
case 'ADD': {
return state + payload
}
case 'MINUS':
return state - payload
default:
return state
}
}
const store = createStore(reducer)
export default store
引入store, 修改App.js
redux
需要手动的更新,类组件有一个this.forceUpdate()
, 函数组件需要手动的实现一个forceUpdate
import store from './store'
import { useEffect, useReducer } from 'react'
function App() {
let state = store.getState()
let [, forceUpdate] = useReducer(x => x+ 1, 0)
useEffect(() => {
let unListen = store.subscribe(() => {
forceUpdate()
})
return () => {
unListen && unListen()
}
}, [])
return (
<div className="App">
Hello world
<h5>{state}</h5>
<button onClick={() => store.dispatch({ type: 'MINUS' })}>减一</button>
<button onClick={() => store.dispatch({ type: 'ADD' })}>加一</button>
</div>
);
}
export default App;
主要就是一下几点
createStore
创建storereducer
初始化,根据type和payload修改函数getState
获取状态值state
dispatch
提交action通过reducer
的规则进行更新subscribe
订阅,当state
更新后通知, 记得组件卸载后取消订阅
实现Redux
其实redux本身没有做很多事情,只是单纯的接受reducer
管理状态,所以实现redux本身很简单
export function createStore(reducer) {
let state
let listeners = []
function getState() {
return state
}
function dispatch(action) {
state = reducer(state, action)
listeners.forEach(listener => {
listener && listener()
})
return action
}
function subscribe(listener) {
listeners.push(listener)
return () => {
let index = listeners.findIndex(item => item === listener)
listeners.splice(index, 1)
}
}
// 触发初始化
dispatch("init")
return {
getState,
dispatch,
subscribe,
}
}
redux中间件
由于redux的功能单一,并且它自己对于dispatch
的action只能接受纯对象,如果我们想要异步的获取数据,然后更新state状态的话,就需要一些
中间件来实现。
我们可以通过redux-thunk
来对传入的action是函数进行处理
- 安装
redux-thunk
的依赖
npm i redux-thunk
- 通过
redux
提供的拓展中间件的方法进行拓展
import { applyMiddleware, createStore } from 'redux'
import thunk from 'redux-thunk'
const store = createStore(reducer, applyMiddleware(thunk))
- 通过中间件后,我们可以使用一些函数的action, 函数的回调参数有3个,第一个是加强后的
dispatch
, 第二个为getState
, 第三个为自定义参数
dispatch((dispatch) => {
setTimeout(() => {
dispatch({type: 'ADD'})
}, 3000)
}, 1000)
实现中间件
实现中间件之前,我们需要实现一些redux的辅助函数applyMiddleware
和 createStore
实现applyMiddleware
首先要确定applyMiddleware
接受的参数
createStore
接受了applyMiddleware
的返回值,增强了dispatch的功能,但是其他原来的功能需要保留
+ if (enhancer) {
+ return enhancer(createStore)(reducer)
+ }
- 从上面的
createStore
中可以看出applyMiddleware
需要根据createStore
和reducer
返回原来的功能,然后再原来的功能上增加dispatch
的部分
// applyMiddleware
export function applyMiddleware(...middlewares) {
return (createStore) => (reducer) => {
// 保持原来的功能
let store = createStore(reducer)
return {
...store
}
}
}
- 在保持基本功能的情况下,我们需要通过中间件对于dispatch进行加强,来覆盖原来的dispatch的功能
thunk
一个函数,接受2个参数getState
,dispatch
,所以我们要创建这2个参数, 然后执行中间件的函数,通过闭包的方式保留getState
和dispatch
export function applyMiddleware(...middlewares) {
return (createStore) => (reducer) => {
// 保持原来的功能
let store = createStore(reducer)
+ let dispatch = store.dispatch
+ const midApi = {
+ getState: store.getState,
+ dispatch: (action, ...args) => {
+ return dispatch(action, ...args)
+ }
+ }
+ const middlewareChain = middlewares.map(middleware => middleware(midApi))
return {
...store
}
}
}
-
现在我们要思考的是需要一个函数执行所有的中间件
middlewareChain
(并不知道applyMiddleware
会接受几个中间件),然后返回一个加强版的dispatch
来覆盖原来的store.dispatch
, 我们假设有一个compose
函数接收所有的中间件和原来的dispatch
,然后返回一个加强后的dispatch
- 假设又一个
compose
函数
export function applyMiddleware(...middlewares) { return (createStore) => (reducer) => { // 保持原来的功能 let store = createStore(reducer) let dispatch = store.dispatch const midApi = { getState: store.getState, dispatch: (action, ...args) => { return dispatch(action, ...args) } } const middlewareChain = middlewares.map(middleware => middleware(midApi)) + dispatch = compose(middlewareChain)(dispatch) return { ...store } } }
- 实现compose函数, 实现middlewareChain的数组聚合,只有一个方法
reduce
来实现数组的聚合,然后需要返回一个函数,接受原来的dispatch
// 先假设只有一个中间件或者没有中间件 function compose(middlewareChain) { // 如果没有的话直接返回原来的dispatch if (middlewareChain.length === 0) { return arg => arg } if (middlewareChain.length === 1) { //只有一个函数的时候,返回函数,执行的时候会接受原来的dispatch return middlewareChain[0] } // 多个中间件 reduce 返回一个函数,执行一次就执行所有的函数 return middlewareChain.reduce((a, b) => (...args) => a(b(...args))) }
- 假设又一个
总结
这里我们就实现了applyMiddleware
和createStore
,整体的代码
export function applyMiddleware(...middlewares) {
return (createStore) => (reducer) => {
// 保持原来的功能
let store = createStore(reducer)
let dispatch = store.dispatch
const midApi = {
getState: store.getState,
dispatch: (action, ...args) => {
return dispatch(action, ...args)
}
}
const middlewareChain = middlewares.map(middleware => middleware(midApi))
console.log(middlewareChain[0])
dispatch = compose(middlewareChain)(dispatch)
console.log(dispatch)
return {
...store,
dispatch
}
}
}
// enhancer 是applyMiddleware函数执行的返回值, reducer肯定要传入applyMiddleware中,
export function createStore(reducer, enhancer) {
let state
let listeners = []
if (enhancer) {
return enhancer(createStore)(reducer)
}
function getState() {
return state
}
function dispatch(action) {
state = reducer(state, action)
listeners.forEach(listener => {
listener && listener()
})
return action
}
function subscribe(listener) {
listeners.push(listener)
return () => {
let index = listeners.findIndex(item => item === listener)
listeners.splice(index, 1)
}
}
// 触发初始化
dispatch('init')
return {
getState,
dispatch,
subscribe,
}
}
function compose(middlewareChain) {
// 如果没有的话直接返回原来的dispatch
if (middlewareChain.length === 0) {
return arg => arg
}
if (middlewareChain.length === 1) {
//只有一个函数的时候,返回函数,执行的时候会接受原来的dispatch
return middlewareChain[0]
}
// reduce 返回一个函数,执行一次就执行所有的函数
return middlewareChain.reduce((a, b) => (...args) => a(b(...args)))
}
实现中间件
首先我们要明白每一个中间件,需要保存上一个下一个中间件的执行过程(next
),又需要保存接受action
。
-
redux-thunk
和redux-logger
的实现- 基于上面的实现,我们会首先执行中间件,然后传递2个参数给它
dispatch
和getState
, - 然后需要保存执行下一个中间件的过程
next
和 返回一个可以接受action
的dispatch函数
export function thunk({getState, dispatch}) { return (next) => { return (action) => { if(typeof action === 'function') { console.log('-thunk 执行-') return action(dispatch, getState) } return next(action) } } } export function logger({getState, dispatch}) { return (next) => { return action => { console.log('-pre state-', getState()) // 触发动作再获取state console.log('next', next) const returnValue = next(action) console.log(returnValue) let nextState = getState() console.log('-next state-', nextState) return returnValue } } }
- 基于上面的实现,我们会首先执行中间件,然后传递2个参数给它
-
2个中间件的整体执行流程
const store = createStore(reducer, applyMiddleware(thunk, logger)) // App.js dispatch((dispatch, getState) => { setTimeout(() => { dispatch({ type: 'ADD' }) }, 1000) })
- 运用过中间键后,每一次的dispatch都会经过
thunk
和logger
进行处理 App.js
中dispatch执行,会进入到加强过的thunk
的回调函数中接受action
执行
(action) => { if(typeof action === 'function') { console.log('-thunk 执行-') return action(dispatch, getState) } return next(action) }
- 知道是函数的话,然后会执行这个函数,传入
dispatch
和getState
参数
setTimeout(() => { dispatch({ type: 'ADD' }) }, 1000)
- 这里又会执行到
dispatch
函数,所以又会进入到第二部的加强版的dispatch
中,这次action
不是一个函数,就执行到下一个中间件return next(action)
- 由于闭包的关系,next中间件就是
logger
函数的部分, 然后执行完成
action => { console.log('-pre state-', getState()) // 触发动作再获取state console.log('next', next) const returnValue = next(action) console.log(returnValue) let nextState = getState() console.log('-next state-', nextState) return returnValue }
- 运用过中间键后,每一次的dispatch都会经过
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!