笔记来源:拉勾教育 - 大前端就业集训营
文章内容:学习过程中的笔记、感悟、和经验
提示:项目实战类文章资源无法上传,仅供参考
Redux原理
核心逻辑 / API - cerateStore()
形参:
- reducer函数
- preloadedState:预存储的store状态,即原始状态
- enhancer(增加store功能),后面讨论
返回对象:
- getState:用来获取状态
- dispatch:触发指令相关操作
- subscribe:订阅状态,可被多次调用
模拟cerateStore并使用
// 模拟 cerateStore 方法
function cerateStore(reducer, preloadedState) {
// 获取初始状态
let data = preloadedState
// 订阅者
let listeners = []
// 获取状态方法
function getState() {
// 直接返回状态
return data
}
// 指令触发方法
function dispatch(action) {
// 调用参数返回结果
data = reducer(data, action)
// 遍历订阅者触发订阅者
for (const listen of listeners) {
listen()
}
}
// 添加订阅者方法
function subscribe(cb) {
// cb 为订阅者要触发的回调函数
listeners.push(cb)
}
// 返回三个方法组成的对象
return { getState, dispatch, subscribe }
}
<body>
<!-- 结构 -->
<button id="up">+1</button>
<span id="num">0</span>
<button id="down">-1</button>
<!-- 引入 cerateStore -->
<script src="./myRedux.js"></script>
<script>
// 创建 cerateStore 参数 - reducer
function reducer(state, action) {
// 根据指令返回状态
switch (action.type) {
case 'up':
return state + 1
case 'down':
return state - 1
default:
return state
}
}
// 创建 store
const store = cerateStore(reducer, 0)
// 添加订阅者
store.subscribe(() => {
// 一旦触发,修改 inner 为 store 的状态
document.getElementById('num').innerHTML = store.getState()
})
// 给两个按钮添加点击事件,调用 store.dispatch
document.getElementById('up').onclick = function () {
store.dispatch({ type: 'up' })
}
document.getElementById('down').onclick = function () {
store.dispatch({ type: 'down' })
}
</script>
</body>
参数类型制约 - 完善cerateStore
使用throw new Error()进行报错处理
- 限制参数reducer必须是一个函数 - Typof
- 调用dispatch的时候传递的参数必须是个对象,并且必须有type属性
// 模拟 cerateStore 方法
function cerateStore(reducer, preloadedState) {
// 直接判断 reducer 是否为函数
if (typeof reducer !== 'function') throw new Error('reducer必须是一个对象')
// 获取初始状态
let data = preloadedState
// 订阅者
let listeners = []
// 获取状态方法
function getState() {
// 直接返回状态
return data
}
// 指令触发方法
function dispatch(action) {
// 调用函数判断是否为对象
if (!isPlainObject(action)) throw new Error('参数必须是一个对象')
// 判断是否含有type属性
if (typeof action.type === 'undefined') throw new Error('需要传入type属性')
// 调用参数返回结果
data = reducer(data, action)
// 遍历订阅者触发订阅者
for (const listen of listeners) {
listen()
}
}
// 添加订阅者方法
function subscribe(cb) {
// cb 为订阅者要触发的回调函数
listeners.push(cb)
}
// 返回三个方法组成的对象
return { getState, dispatch, subscribe }
}
// 判断是否为非空对象
function isPlainObject(obj) {
// 排除出数组外其他情况
if (typeof obj !== 'object' || obj === null) return false
// 根据顶层原型对象和自己原型对象判断
let proto = obj
while (Object.getPrototypeOf(proto) != null) {
proto = Object.getPrototypeOf(proto)
}
return proto === Object.getPrototypeOf(obj)
}
实现Enhancer - 完善cerateStore
给cerate函数添加第三个形参(可选参数),对返回的store进行功能增强
- 必须是个函数,使用之前先进行判断
- Enhancer如果符合条件,那么会使用Enhancer创建一个强大的store
- 不符合条件继续按照原始方法创建普通store
// 模拟 cerateStore 方法
function cerateStore(reducer, preloadedState, enhancer) {
// 直接判断 reducer 是否为函数
if (typeof reducer !== 'function') throw new Error('reducer必须是一个对象')
// 判断是否传入了 enhancer
if (typeof enhancer !== 'undefined') {
// 传入了 enhancer 判断是否为函数
if (typeof enhancer !== 'function')
throw new Error('enhancer必须是一个函数')
// 为函数调用函数并将 cerateStore 作为参数传递进去
// 函数会返回一个函数,返回的函数立即被调用
// 把 reducer 和 preloadedState 作为参数传入新函数
return enhancer(cerateStore)(reducer, preloadedState)
}
// 获取初始状态
let data = preloadedState
// 订阅者
let listeners = []
// 获取状态方法
function getState() {
// 直接返回状态
return data
}
// 指令触发方法
function dispatch(action) {
// 调用函数判断是否为对象
if (!isPlainObject(action)) throw new Error('参数必须是一个对象')
// 判断是否含有type属性
if (typeof action.type === 'undefined') throw new Error('需要传入type属性')
// 调用参数返回结果
data = reducer(data, action)
// 遍历订阅者触发订阅者
for (const listen of listeners) {
listen()
}
}
// 添加订阅者方法
function subscribe(cb) {
// cb 为订阅者要触发的回调函数
listeners.push(cb)
}
// 返回三个方法组成的对象
return { getState, dispatch, subscribe }
}
// 判断是否为非空对象
function isPlainObject(obj) {
// 排除出数组外其他情况
if (typeof obj !== 'object' || obj === null) return false
// 根据顶层原型对象和自己原型对象判断
let proto = obj
while (Object.getPrototypeOf(proto) != null) {
proto = Object.getPrototypeOf(proto)
}
return proto === Object.getPrototypeOf(obj)
}
<body>
<!-- 结构 -->
<button id="up">+1</button>
<span id="num">0</span>
<button id="down">-1</button>
<!-- 引入 cerateStore -->
<script src="./myRedux.js"></script>
<script>
// 创建 cerateStore 参数 - reducer
function reducer(state, action) {
// 根据指令返回状态
switch (action.type) {
case 'up':
return state + 1
case 'down':
return state - 1
default:
return state
}
}
// 创建 cerateStore 参数 - enhancer
function enhancer(cerateStore) {
// 返回一个新函数
return function (reducer, preloadedState) {
// 调用 cerateStore 创建普通 store
let store = cerateStore(reducer, preloadedState)
// 下面自定义 dispatch
const dispatch = store.dispatch
// 创建新的 dispatch 用来替换旧的 dispatch,action 是调用时传递的参数
function _dispatch_(action) {
// 如果传递过来一个函数,执行函数,并且把原本的 dispatch 传递给函数
if (typeof action === 'function') {
return action(dispatch)
}
// 如果不是函数直接调用 dispatch 执行
dispatch(action)
}
// 返回改造过的 store
return { ...store, dispatch: _dispatch_ }
}
}
// 创建 store
const store = cerateStore(reducer, 0, enhancer)
// 添加订阅者
store.subscribe(() => {
// 一旦触发,修改 inner 为 store 的状态
document.getElementById('num').innerHTML = store.getState()
})
// 给两个按钮添加点击事件,调用 store.dispatch
document.getElementById('up').onclick = function () {
// 调用 dispatch 传递一个函数
store.dispatch(dispatch => {
// 函数内部逻辑
setTimeout(() => {
// 最后调用原本应该执行的 dispatch
dispatch({ type: 'up' })
}, 2000)
})
}
document.getElementById('down').onclick = function () {
// 不传入函数,直接传入需要执行的 dispatch
store.dispatch({ type: 'down' })
}
</script>
</body>
梳理:
- cerateStore的第三个参数会创建一个比普通store更强大的store
- enhancer使用前需要判断是否符合要求
- 符合要求则直接调用,并且将原本的cerateStore传递进去
- enhancer返回一个新函数,新函数立即调用,新函数接受cerateStore的前两个值,到此为止,cerateStore、reducer,、preloadedState都传递给enhancer了
- 新函数通过cerateStore创建普通store,可以直接使用
- 如果想在普通store上进行dispatch处理,则需要创建一个新的dispach替换掉旧的dispatch
- 因为新的dispach没有旧的dispach逻辑,所以当需要使用逻辑的时候还是需要调用旧的dispach
中间件就是这样实现的
中间件实现原理 - applyMiddleware
中间件在action发出后、reducer接收前进行拦截处理
中间件的核心就是增强dispatch方法,当有多个中间件的时候,多个中间件依次执行,最后才触发action
// 中间件1
// 参数store:阉割版store,只包括 getState 和 dispatch
// 参数next:下一个中间件,如果当前是最后一个中间件,则 next 就是 dispatch
// 参数action:就是触发的 action
function one(store) {
return function (next) {
return function (action) {
console.log('one中间件执行了')
next(action)
}
}
}
// 中间件2
// 参数store:阉割版store,只包括 getState 和 dispatch
// 参数next:下一个中间件,如果当前是最后一个中间件,则 next 就是 dispatch
// 参数action:就是触发的 action
function tow(store) {
return function (next) {
return function (action) {
console.log('tow中间件执行了')
next(action)
}
}
}
这里创建store直接使用中间件函数导入中间件
<body>
<!-- 结构 -->
<button id="up">+1</button>
<span id="num">0</span>
<button id="down">-1</button>
<!-- 引入 cerateStore -->
<script src="./myRedux.js"></script>
<!-- 引入中间件 -->
<script src="./middleware/one.js"></script>
<script src="./middleware/tow.js"></script>
<script>
// 创建 cerateStore 参数 - reducer
function reducer(state, action) {
// 根据指令返回状态
switch (action.type) {
case 'up':
return state + 1
case 'down':
return state - 1
default:
return state
}
}
// 创建 cerateStore 参数 - enhancer
function enhancer(cerateStore) {
// 返回一个新函数
return function (reducer, preloadedState) {
// 调用 cerateStore 创建普通 store
let store = cerateStore(reducer, preloadedState)
// 下面自定义 dispatch
const dispatch = store.dispatch
// 创建新的 dispatch 用来替换旧的 dispatch,action 是调用时传递的参数
function _dispatch_(action) {
// 如果传递过来一个函数,执行函数,并且把原本的 dispatch 传递给函数
if (typeof action === 'function') {
return action(dispatch)
}
// 如果不是函数直接调用 dispatch 执行
dispatch(action)
}
// 返回改造过的 store
return { ...store, dispatch: _dispatch_ }
}
}
// 创建 store,这里使用中间件函数引入两个中间件
const store = cerateStore(reducer, 0, applyMiddleware(one, tow))
// 添加订阅者
store.subscribe(() => {
// 一旦触发,修改 inner 为 store 的状态
document.getElementById('num').innerHTML = store.getState()
})
// 给两个按钮添加点击事件,调用 store.dispatch
document.getElementById('up').onclick = function () {
store.dispatch({ type: 'up' })
}
document.getElementById('down').onclick = function () {
// 不传入函数,直接传入需要执行的 dispatch
store.dispatch({ type: 'down' })
}
</script>
</body>
// 模拟 cerateStore 方法
function cerateStore(reducer, preloadedState, enhancer) {
// 直接判断 reducer 是否为函数
if (typeof reducer !== 'function') throw new Error('reducer必须是一个对象')
// 判断是否传入了 enhancer
if (typeof enhancer !== 'undefined') {
// 传入了 enhancer 判断是否为函数
if (typeof enhancer !== 'function')
throw new Error('enhancer必须是一个函数')
// 为函数调用函数并将 cerateStore 作为参数传递进去
// 函数会返回一个函数,返回的函数立即被调用
// 把 reducer 和 preloadedState 作为参数传入新函数
return enhancer(cerateStore)(reducer, preloadedState)
}
// 获取初始状态
let data = preloadedState
// 订阅者
let listeners = []
// 获取状态方法
function getState() {
// 直接返回状态
return data
}
// 指令触发方法
function dispatch(action) {
// 调用函数判断是否为对象
if (!isPlainObject(action)) throw new Error('参数必须是一个对象')
// 判断是否含有type属性
if (typeof action.type === 'undefined') throw new Error('需要传入type属性')
// 调用参数返回结果
data = reducer(data, action)
// 遍历订阅者触发订阅者
for (const listen of listeners) {
listen()
}
}
// 添加订阅者方法
function subscribe(cb) {
// cb 为订阅者要触发的回调函数
listeners.push(cb)
}
// 返回三个方法组成的对象
return { getState, dispatch, subscribe }
}
// 判断是否为非空对象
function isPlainObject(obj) {
// 排除出数组外其他情况
if (typeof obj !== 'object' || obj === null) return false
// 根据顶层原型对象和自己原型对象判断
let proto = obj
while (Object.getPrototypeOf(proto) != null) {
proto = Object.getPrototypeOf(proto)
}
return proto === Object.getPrototypeOf(obj)
}
// 中间件方法,使用 ...将多个参数合并为一个数组
function applyMiddleware(...wares) {
// 和 Enhancer 相同,上来依旧需要创建基本的 store
return function (cerateStore) {
return function (reducer, preloadedState) {
// 创建 store - 基本
let store = cerateStore(reducer, preloadedState)
// 创建阉割版 store 用于带入中间件
let middlewareAPI = {
getState: store.getState,
dispatch: store.dispatch,
}
// 执行所有中间件最外层函数,返回数组,该数组包含的事中间件外层返回的函数
const chain = wares.map(ware => ware(middlewareAPI))
// 遍历上面数组,执行中间件的第二层函数(解构),传递dispatch,最终返回
const dispatch = compose(...chain)(store.dispatch)
// 返回store,并替换dispatch
return {
...store,
dispatch,
}
}
}
}
// 中间件第二层函数执行
function compose() {
// 接受参数并由类数组转换为数组
const funcs = [...arguments]
// 返回函数,因为要使用 store.dispatch,这里的dispatch就是中间件里面的next参数
return function (dispatch) {
// 倒过来执行函数,因为前一个中间件需要使用后一个中间件返回的第三层函数
for (let i = funcs.length - 1; i >= 0; i--) {
// 修改参数为后面中间件的第三层函数
dispatch = funcs[i](dispatch)
}
// 直接返回第一个中间件第二层函数
return dispatch
}
}
bindActionCreators
bindActionCreators方法本质上就是将原本需要手动调用dispatch的方式进行了封装,只需要调用bindActionCreators函数的对应方法名即可
// 模拟 cerateStore 方法
function cerateStore(reducer, preloadedState, enhancer) {
// 直接判断 reducer 是否为函数
if (typeof reducer !== 'function') throw new Error('reducer必须是一个对象')
// 判断是否传入了 enhancer
if (typeof enhancer !== 'undefined') {
// 传入了 enhancer 判断是否为函数
if (typeof enhancer !== 'function')
throw new Error('enhancer必须是一个函数')
// 为函数调用函数并将 cerateStore 作为参数传递进去
// 函数会返回一个函数,返回的函数立即被调用
// 把 reducer 和 preloadedState 作为参数传入新函数
return enhancer(cerateStore)(reducer, preloadedState)
}
// 获取初始状态
let data = preloadedState
// 订阅者
let listeners = []
// 获取状态方法
function getState() {
// 直接返回状态
return data
}
// 指令触发方法
function dispatch(action) {
// 调用函数判断是否为对象
if (!isPlainObject(action)) throw new Error('参数必须是一个对象')
// 判断是否含有type属性
if (typeof action.type === 'undefined') throw new Error('需要传入type属性')
// 调用参数返回结果
data = reducer(data, action)
// 遍历订阅者触发订阅者
for (const listen of listeners) {
listen()
}
}
// 添加订阅者方法
function subscribe(cb) {
// cb 为订阅者要触发的回调函数
listeners.push(cb)
}
// 返回三个方法组成的对象
return { getState, dispatch, subscribe }
}
// 判断是否为非空对象
function isPlainObject(obj) {
// 排除出数组外其他情况
if (typeof obj !== 'object' || obj === null) return false
// 根据顶层原型对象和自己原型对象判断
let proto = obj
while (Object.getPrototypeOf(proto) != null) {
proto = Object.getPrototypeOf(proto)
}
return proto === Object.getPrototypeOf(obj)
}
// 中间件方法,使用 ...将多个参数合并为一个数组
function applyMiddleware(...wares) {
// 和 Enhancer 相同,上来依旧需要创建基本的 store
return function (cerateStore) {
return function (reducer, preloadedState) {
// 创建 store - 基本
let store = cerateStore(reducer, preloadedState)
// 创建阉割版 store 用于带入中间件
let middlewareAPI = {
getState: store.getState,
dispatch: store.dispatch,
}
// 执行所有中间件最外层函数,返回数组,该数组包含的事中间件外层返回的函数
const chain = wares.map(ware => ware(middlewareAPI))
// 遍历上面数组,执行中间件的第二层函数(解构),传递dispatch,最终返回
const dispatch = compose(...chain)(store.dispatch)
// 返回store,并替换dispatch
return {
...store,
dispatch,
}
}
}
}
// 中间件第二层函数执行
function compose() {
// 接受参数并由类数组转换为数组
const funcs = [...arguments]
// 返回函数,因为要使用 store.dispatch,这里的dispatch就是中间件里面的next参数
return function (dispatch) {
// 倒过来执行函数,因为前一个中间件需要使用后一个中间件返回的第三层函数
for (let i = funcs.length - 1; i >= 0; i--) {
// 修改参数为后面中间件的第三层函数
dispatch = funcs[i](dispatch)
}
// 直接返回第一个中间件第二层函数
return dispatch
}
}
// bindActionCreators 处理 action
// 参数1: action合集
// 参数2: dispatch
function bindActionCreators(arr, diapatch) {
// 创建一个空对象
const actions = {}
// 遍历 action
for (let key in arr) {
// 给对象添加属性,对应 action
actions[key] = function () {
// 注意这里使用action返回值
diapatch(arr[key]())
}
}
// 直接返回处理后的 action
return actions
}
<body>
<!-- 结构 -->
<button id="up">+1</button>
<span id="num">0</span>
<button id="down">-1</button>
<!-- 引入 cerateStore -->
<script src="./myRedux.js"></script>
<!-- 引入中间件 -->
<script src="./middleware/one.js"></script>
<script src="./middleware/tow.js"></script>
<script>
// 创建 cerateStore 参数 - reducer
function reducer(state, action) {
// 根据指令返回状态
switch (action.type) {
case 'up':
return state + 1
case 'down':
return state - 1
default:
return state
}
}
// 创建 cerateStore 参数 - enhancer
function enhancer(cerateStore) {
// 返回一个新函数
return function (reducer, preloadedState) {
// 调用 cerateStore 创建普通 store
let store = cerateStore(reducer, preloadedState)
// 下面自定义 dispatch
const dispatch = store.dispatch
// 创建新的 dispatch 用来替换旧的 dispatch,action 是调用时传递的参数
function _dispatch_(action) {
// 如果传递过来一个函数,执行函数,并且把原本的 dispatch 传递给函数
if (typeof action === 'function') {
return action(dispatch)
}
// 如果不是函数直接调用 dispatch 执行
dispatch(action)
}
// 返回改造过的 store
return { ...store, dispatch: _dispatch_ }
}
}
// 创建 store,这里使用中间件函数引入两个中间件
const store = cerateStore(reducer, 0, applyMiddleware(one, tow))
// 添加订阅者
store.subscribe(() => {
// 一旦触发,修改 inner 为 store 的状态
document.getElementById('num').innerHTML = store.getState()
})
// 使用 bindActionCreators 处理 action
const actions = bindActionCreators({ up, down }, store.dispatch)
// 两个函数 直接返回 dispatch 要触发的 action 信息
function up() {
return { type: 'up' }
}
function down() {
return { type: 'down' }
}
// 给两个按钮添加点击事件,调用 store.dispatch
document.getElementById('up').onclick = function () {
// store.dispatch({ type: 'up' })
// 直接调用bindActionCreators处理后的方法名即可
actions.up()
}
document.getElementById('down').onclick = function () {
// store.dispatch({ type: 'down' })
// 直接调用bindActionCreators处理后的方法名即可
actions.down()
}
</script>
</body>
combineReducers - 合并reducer
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<!-- 结构 -->
<button id="up">+1</button>
<span id="num">0</span>
<button id="down">-1</button>
<!-- 引入 cerateStore -->
<script src="./myRedux.js"></script>
<!-- 引入中间件 -->
<script src="./middleware/one.js"></script>
<script src="./middleware/tow.js"></script>
<script>
// 创建 cerateStore 参数 - reducer
function reducer(state, action) {
// 根据指令返回状态
switch (action.type) {
case 'up':
return state + 1
case 'down':
return state - 1
default:
return state
}
}
// 创建 cerateStore 参数 - enhancer
function enhancer(cerateStore) {
// 返回一个新函数
return function (reducer, preloadedState) {
// 调用 cerateStore 创建普通 store
let store = cerateStore(reducer, preloadedState)
// 下面自定义 dispatch
const dispatch = store.dispatch
// 创建新的 dispatch 用来替换旧的 dispatch,action 是调用时传递的参数
function _dispatch_(action) {
// 如果传递过来一个函数,执行函数,并且把原本的 dispatch 传递给函数
if (typeof action === 'function') {
return action(dispatch)
}
// 如果不是函数直接调用 dispatch 执行
dispatch(action)
}
// 返回改造过的 store
return { ...store, dispatch: _dispatch_ }
}
}
// 使用combineReducers合并 reducer
const rootReducer = combineReducers({ num: reducer })
// 创建 store,这里使用中间件函数引入两个中间件
const store = cerateStore(
// 使用合并后的reducer
rootReducer,
{ num: 0 },
applyMiddleware(one, tow)
)
// 添加订阅者
store.subscribe(() => {
// 一旦触发,修改 inner 为 store 的状态,因为reducer合并了,需要调用属性
document.getElementById('num').innerHTML = store.getState().num
})
// 使用 bindActionCreators 处理 action
const actions = bindActionCreators({ up, down }, store.dispatch)
// 两个函数 直接返回 dispatch 要触发的 action 信息
function up() {
return { type: 'up' }
}
function down() {
return { type: 'down' }
}
// 给两个按钮添加点击事件,调用 store.dispatch
document.getElementById('up').onclick = function () {
// store.dispatch({ type: 'up' })
// 直接调用bindActionCreators处理后的方法名即可
actions.up()
}
document.getElementById('down').onclick = function () {
// store.dispatch({ type: 'down' })
// 直接调用bindActionCreators处理后的方法名即可
actions.down()
}
</script>
</body>
</html>
// 模拟 cerateStore 方法
function cerateStore(reducer, preloadedState, enhancer) {
// 直接判断 reducer 是否为函数
if (typeof reducer !== 'function') throw new Error('reducer必须是一个对象')
// 判断是否传入了 enhancer
if (typeof enhancer !== 'undefined') {
// 传入了 enhancer 判断是否为函数
if (typeof enhancer !== 'function')
throw new Error('enhancer必须是一个函数')
// 为函数调用函数并将 cerateStore 作为参数传递进去
// 函数会返回一个函数,返回的函数立即被调用
// 把 reducer 和 preloadedState 作为参数传入新函数
return enhancer(cerateStore)(reducer, preloadedState)
}
// 获取初始状态
let data = preloadedState
// 订阅者
let listeners = []
// 获取状态方法
function getState() {
// 直接返回状态
return data
}
// 指令触发方法
function dispatch(action) {
// 调用函数判断是否为对象
if (!isPlainObject(action)) throw new Error('参数必须是一个对象')
// 判断是否含有type属性
if (typeof action.type === 'undefined') throw new Error('需要传入type属性')
// 调用参数返回结果
data = reducer(data, action)
// 遍历订阅者触发订阅者
for (const listen of listeners) {
listen()
}
}
// 添加订阅者方法
function subscribe(cb) {
// cb 为订阅者要触发的回调函数
listeners.push(cb)
}
// 返回三个方法组成的对象
return { getState, dispatch, subscribe }
}
// 判断是否为非空对象
function isPlainObject(obj) {
// 排除出数组外其他情况
if (typeof obj !== 'object' || obj === null) return false
// 根据顶层原型对象和自己原型对象判断
let proto = obj
while (Object.getPrototypeOf(proto) != null) {
proto = Object.getPrototypeOf(proto)
}
return proto === Object.getPrototypeOf(obj)
}
// 中间件方法,使用 ...将多个参数合并为一个数组
function applyMiddleware(...wares) {
// 和 Enhancer 相同,上来依旧需要创建基本的 store
return function (cerateStore) {
return function (reducer, preloadedState) {
// 创建 store - 基本
let store = cerateStore(reducer, preloadedState)
// 创建阉割版 store 用于带入中间件
let middlewareAPI = {
getState: store.getState,
dispatch: store.dispatch,
}
// 执行所有中间件最外层函数,返回数组,该数组包含的事中间件外层返回的函数
const chain = wares.map(ware => ware(middlewareAPI))
// 遍历上面数组,执行中间件的第二层函数(解构),传递dispatch,最终返回
const dispatch = compose(...chain)(store.dispatch)
// 返回store,并替换dispatch
return {
...store,
dispatch,
}
}
}
}
// 中间件第二层函数执行
function compose() {
// 接受参数并由类数组转换为数组
const funcs = [...arguments]
// 返回函数,因为要使用 store.dispatch,这里的dispatch就是中间件里面的next参数
return function (dispatch) {
// 倒过来执行函数,因为前一个中间件需要使用后一个中间件返回的第三层函数
for (let i = funcs.length - 1; i >= 0; i--) {
// 修改参数为后面中间件的第三层函数
dispatch = funcs[i](dispatch)
}
// 直接返回第一个中间件第二层函数
return dispatch
}
}
// bindActionCreators 处理 action
// 参数1: action合集
// 参数2: dispatch
function bindActionCreators(arr, diapatch) {
// 创建一个空对象
const actions = {}
// 遍历 action
for (let key in arr) {
// 给对象添加属性,对应 action
actions[key] = function () {
// 注意这里使用action返回值
diapatch(arr[key]())
}
}
// 直接返回处理后的 action
return actions
}
// 合并reducer方法
function combineReducers(obj) {
// 获取全部属性名
const keys = Object.keys(obj)
// 判断导入的reducer是不是都是函数,否则报错
for (let i = 0; i < keys.length; i++) {
if (typeof obj[keys[i]] !== 'function')
throw new Error('reducer必须是一个函数')
}
// 返回一个函数,因为store创建时的reducer也必须是一个函数
return function (state, action) {
// 创建一个新的state
const newState = {}
// 遍历全部传入的 reducer,执行并传递给新的state中
for (let i = 0; i < keys.length; i++) {
newState[keys[i]] = obj[keys[i]](state[keys[i]], action)
}
// 直接导出新的state作为reducer返回值
return newState
}
}