-
redux中间件洋葱模型
-
redux中间件注意点
导航
[深入01] 执行上下文
[深入02] 原型链
[深入03] 继承
[深入04] 事件循环
[深入05] 柯里化 偏函数 函数记忆
[深入06] 隐式转换 和 运算符
[深入07] 浏览器缓存机制(http缓存机制)
[深入08] 前端安全
[深入09] 深浅拷贝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模块化
[深入13] 观察者模式 发布订阅模式 双向数据绑定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手写Promise
[深入20] 手写函数
[react] Hooks
[部署01] Nginx
[部署02] Docker 部署vue项目
[部署03] gitlab-CI
[源码-webpack01-前置知识] AST抽象语法树
[源码-webpack02-前置知识] Tapable
[源码-webpack03] 手写webpack - compiler简单编译流程
[源码] Redux React-Redux01
[源码] axios
[源码] vuex
[源码-vue01] data响应式 和 初始化渲染
[源码-vue02] computed 响应式 - 初始化,访问,更新过程
[源码-vue03] watch 侦听属性 - 初始化和更新
[源码-vue04] Vue.set 和 vm.$set
[源码-vue05] Vue.extend
[源码-vue06] Vue.nextTick 和 vm.$nextTick
前置知识
(1) 一些单词
several 几个
enhancer 增强器
third-party 第三方
potentially 潜在的
plainObject
- 纯对象
- plainObject 是指通过对象字面量方式创建或者通过构造函数创建的对象,即 ( {} ) 或者 ( new Object() ) 方式创建的对象
- 纯对象如何理解:
- 比如 Date,Regexp,Function,Array等就不属于纯对象,但是用 typeof 判断他们都会返回 'object'
- 比如 const a = { name: 'woow_wu7'} 这样声明的对象就是一个纯对象,因为是通过对象字面量声明的对象
context
- React.createContext(defaultValue)
- 创建 context 对象
- context对象上包含
- Provider 组件
- context对象上包含
-
const MyContext = React.createContext(defaultValue)
- 如果React渲染一个 ( 订阅 ) 了 ( MyContext ) 对象的组件时,这个组件会从组件树中离 ( 自身最近 ) 的那个匹配的 ( Provider ) 中读取到当前的 ( context ) 值
- 参数
- defaultValue:只要在所处的组件树中没有 Provider 时,defaultValue 才会生效
- 注意:
- 当
中的value是undefined时,defaultValue不生效
- 当
- 创建 context 对象
- Context.Provider
- 参数
- value
- 作用
- 允许消费组件订阅 context 的变化
- 当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染
- Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新
- Class.contextType
-
MyClass.contextType = MyContext
- 将 MyContext 即一个context对象,赋值给一个组件的 contextType 属性
- 那么就可以通过在组件内部通过 this.context 获取到 context 的值
- 可以在组件的任何生命周期中通过 this.context 访问到context的值
-
- Context.Consumer
- 在函数式组件中完成订阅 context
- 实例
import React from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import * as actions from './action'
import MyContext from '../../context'
import './index.less'
class ContextTest extends React.Component {
render() {
return (
context-test
{
({ author, date }) => {
console.log('函数组件通过MyContext.Consumer组件包裹函数组件,函数组件的参数就是context的值')
return (
{
author
}
)
}
}
)
}
}
// MyContext.Provider
// MyContext.Consumer
// Consumer组件用于函数式组件,用Consumer包裹函数组件后,函数组件的参数就是最近的Provider的提供的context的值
const mapStateToProps = (state) => {
return {
...state
}
}
const mapDispatchToProps = (dispatch) => {
return bindActionCreators(actions, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(ContextTest)
class ContextTypeText extends React.Component {
// class式组件
render() {
const { appName } = this.context
console.log(this.context, 'class组件通过class.contextType = MyContext来获取context')
return (
<>
{appName}
>
)
}
}
ContextTypeText.contextType = MyContext
// Class.contextType = MyContext
// 把MyContext赋值组件的contextType后,就可以在组件内通过 this.context 获取context的值
- 官网 context api讲解
一些单词
preload:预载,预装
enhancer:增强器,放大器
bound:一定会,跳跃
nest:巢,嵌套
plain:纯的
plainObject:纯对象
invoked: 调用
assert:断言
Redux
- createStore
- combineReducers
- applyMiddleware
- bindActionCreators
- compose
React-Reux
- Provider
- connect
useSelector
useDispatch
useStore
- createSelectorHook
- createDispatchHook
- createStoreHook
- shallowEqual
shallow是浅的意思
- batch
- connectAdvanced
- ReactReduxContext
2021/01/02复习
- [redux和react-redux]源码分析仓库
按中间件的使用流程来梳理redux源码
(1) index.js
- 入口文件
- 使用 react-redux 的 Provider 包裹根组件
- Provider的作用是通过context将store传入子组件,子组件通过 connect 获取 state
ReactDOM.render(
// --------------------- Provider
, document.getElementById('root'));
(2) store.js
- 创建store实例
const allReducer = {
home: HomeReducer,
banner: BannerReducer,
recommend: RecomendReducer, // ----------------- 用到了redux-thunk
}
const store = createStore(
combineReducers({ // --------------------------- combineReducer 将所有的reducer合成一个总的reducer
...allReducer
}),
composeWithDevTools( // ------------------------ enhancer增强器
applyMiddleware(thunkMiddleware, loggerMiddleware) // ---------- applyMiddleware 将多个中间件通过洋葱模型执行副作用,并dispatch(action)
)
)
(3) 在组件模块中
- 通过 connect 连接 store
- 将 store state 和 改装过后的 action creators 注入到 props 中传给各组件
const mapStateToProps = (state) => { // ---------------------------------------------- mapStateToProps
return {
...state
}
}
const mapDispatchToProps = (dispatch) => { // --------------------------------------- mapDisPatchToProps
return bindActionCreators(actions, dispatch) // ----------------------------------- bindActionCreators
}
export default connect(mapStateToProps, mapDispatchToProps)(Home) // ---------------- connect
(4) home/reducer.js
import actionType from './constant.js'
const initialState = {
name: 'wang'
}
const reducer = function(state=initialState, action) {
switch (action.type) {
case actionType.GET_USERNAME:
return {
...state,
name: action.payload
}
default:
return {
...state
}
}
}
export default reducer
(5) home/action.js
import actionType from './constant'
export const getUerName = (name) => {
return {
type: actionType.GET_USERNAME,
payload: name
}
}
(6) home/constant.js
const actonType = {
GET_USERNAME: 'GET_USERNAME'
}
export default actonType
redux-thunk中间件源码分析
// redux-thunk 源码
// 1
// createThunkMiddleware函数签名如下
// (extraArgument) => ({dispatch, getState}) => next => action => { ...具体根据action的类型来判断 }
// 2
// 真正导出的是:
// (1) action是一个对象: ({dispatch, getState}) => next => action => next(acion)
// (2) action是一个函数: ({dispatch, getState}) => next => action => action(dispath, getState, extraArgument)
// 3
// 具体案例在 在 admin-system/interview-react/Knowledge/reduxSourceCode组件中
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
// 如果 action 是一个函数
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
// 比如dispatch这样一个函数
// 1.
// 2. const add = () => { dispatch(handleThunk) }
// 3. 如下
// const handleThunk = (dispatch: any, getState: any) => {
// setTimeout(() => {
// dispatch({
// type: actionType.ADD_INTERVIEW,
// payload: 1
// })
// }, 2000)
// }
}
// 如果 action 不是函数,就调用 next(action) 将 action对象 传递到下一个中间件
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
(1) createStore()
createStore(reducer, preloadedState, enhancer)
- 参数:
- reducer
- 根 reducer,可以使用 ( combineReducers ) 将多个reducer组合成一颗总树
- 纯函数,接受 ( prevState ) 和 ( action ),返回 ( 新的state )
- preloadedState
- 初始化state,初始化整个store
- preload: 预载,预装的意思
- enhancer
- enhancer是一个函数,叫做增强器,可以在dispatch一个action到达reducer之前做一些增强处理,如:打印日志,dispatch一个函数等
- applyMiddleware(中间件1,中间件2...) 就是一个增强函数
- reducer
- 返回值:
- enhancer不存在
- 返回 store 对象
- dispatch
- subscribe
- getState
- replaceReducer
- observable
- 先走流程,这几个函数后面分析
- 返回 store 对象
- enhancer存在,并且是函数
- 返回 enhancer(createStore)(reducer, preloadedState)
- enhancer如果是applyMiddleware()的话,返回值也是一个对象
- enhancer返回值:
- 像这样的对象{..store, dispatch}
- 后面dispatch会覆盖掉store中的dispatch函数,对象中后面的属性会覆盖前面的属性
- 返回 enhancer(createStore)(reducer, preloadedState)
- enhancer不存在
createStore.js先分析主要的流程
- 如果enhancer是函数就返回一个
- enhancer(createStore)(reducer, preloadedState)
// 在真实的开发过程中一般都会使用 enhancer 增强器
// 一般是调用 applyMiddleware(thunkMiddleware)
- 如果enhancer不存在就返回一个
- { dispatch,subscribe,getState,replaceReducer,[?observable]: observable } 对象
createStore.js
---
export default function createStore(reducer, preloadedState, enhancer) {
if (
(typeof preloadedState === 'function' && typeof enhancer === 'function') ||
(typeof enhancer === 'function' && typeof arguments[3] === 'function')
) {
throw new Error(
'It looks like you are passing several store enhancers to ' +
'createStore(). This is not supported. Instead, compose them ' +
'together to a single function.'
)
}
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
}
return {
dispatch,
subscribe,
getState,
replaceReducer,
[?observable]: observable
}
}
(2) applyMiddleware()
applyMiddleware(middleware1, middleware2, ...) 就是 createStore() 的参数中的 enhancer()
- applyMiddleware() 函数签名
(...middlewares) => createStore => (...args) => ({...store, dispatch})
- applyMiddleware() 返回值
- 一个对象
- {...store, dispatch}
- 在真实的项目中,一般这样使用 createStore 和 applyMiddleware
-
createStore(combineReducers({...rootReducer}), applyMiddleware(thunkMiddleware, loggerMiddleware))
- 这里已经是调用了applyMiddleware()的第一层,返回的是一个函数
- 返回的函数签名:
createStore => (...args) => ({...store, dispatch})
- middlewares已经在内存中,形成闭包
- 返回的函数签名:
- 这里已经是调用了applyMiddleware()的第一层,返回的是一个函数
-
- 实际上第一步是这样的:
createStore(rootReducer, (createStore) => (...args) => ({...store, dispatch}))
- redux中间件
- redux的中间件是一个高阶函数
- 函数签名:
({dispatch, getState}) => (next) => (action) => next(action)
- compose()
- 函数签名:
(...funcs) => funcs.reduce((a, b) => (...args) => a(b(...args)))
- 返回值:一个函数
- 作用:
- 从右至左,将右边函数的返回值作为左边函数的参数传入
- compose函数就是洋葱模型
- compose(A, B, C)(arg) === A(B(C(arg)))
- 调用时:dispatch = compose(...chain)(store.dispatch)
- chain类似:
chain = [(next) => (action) => next(action), (next) => (action) => next(action)]
- 函数签名:
import compose from './compose'
export default function applyMiddleware(...middlewares) {
// 在createStore()中调用enhancer时,传入的参数是
// enhancer(createStore)(reducer, preloadedState)
// 这是一个高阶函数,柯里化函数
// 1) 执行 const resFn = enhancer(createStore),返回值也是一个函数
// 2) 执行 consr res = resFn(reducer, preloadedState)
// 最终 applyMiddleware 返回的是 ({...store, dispatch}) 对象
return createStore => (...args) => {
// 这里的args是一个数组
// 具体的值就是:[reducer,preloadedState]
const store = createStore(...args)
let dispatch = () => {
// 声明dispatch方法
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 这里middleware数组中有两个中间件:thunkMiddleware,loggerMiddleware
// middleware的函数签名如下:
// ({dispatch, getState}) => (next) => (action) => next(action)
// chain = [(next) => (action) => next(action), (next) => (action) => next(action)]
dispatch = compose(...chain)(store.dispatch)
// compose
// 函数签名:(...funcs) => funcs.reduce((a, b) => (...args) => a(b(...args)))
// compose()的作用是:从右至左,将右边函数的返回值作为左边函数的参数传入
// compose(A, B, C)(arg) === A(B(C(arg)))
// compose(A, B, C)(arg) === A(B(C(arg))) 这个暂时可以作为结论记住,下面会按步骤拆解
// 现在假设有三个中间件
// const M1 = (store) => (next) => (action) => { console.log('A开始'); next(action); console.log('A结束')}
// const M2 = (store) => (next) => (action) => { console.log('B开始'); next(action); console.log('B结束')}
// const M3 = (store) => (next) => (action) => { console.log('C开始'); next(action); console.log('C结束')}
// 注意上面三个中间件的第一个参数 store 中只有getState, dispacth两个函数
// chain = [(next)=>(action)=>{M1...}, (next)=>(action)=>{M2...}, (next)=>(action)=>{M3...}]
// 现在 dispatch = chain.reduce((a, b) => (...args) => a(b(...args)))(store.dispatch)
// dispatch的最终形态
// dispatch = M1(M2(M3(store.dispatchj)))
// 注意这里的M1M2M3是去除了(store)参数的这一层的返回的函数 (next)=>(action)=>{M1...}
// 以下是具体步骤
// 第一步:dispatch = funcs.reduce((a, b) => (...args) => a(b(...args)))(store.dispatch)
// 第一次reduce
// a是: (next) => (action) => { console.log('A开始'); next(action); console.log('A结束')}
// b是: (next) => (action) => { console.log('B开始'); next(action); console.log('B结束')}
// b(...args)是: (action) => { console.log('B开始'); (...args)(action); console.log('B结束')}
// a(b(...args)) 就是调用a中的 next(actions) 换成next就是 (action) => { console.log('B开始'); (...args)(action); console.log('B结束')}函数,参数是action
// a(b(...args))是:(action) => { console.log('A开始'); console.log('B开始'); (...args)(action); console.log('B结束') console.log('A结束')}
// 总返回值:(...args) => (action) => { console.log('A开始'); console.log('B开始'); (...args)(action); console.log('B结束') console.log('A结束')}
// 第二次reduce
// a是:(...args) => (action) => { console.log('A开始'); console.log('B开始'); (...args)(action); console.log('B结束') console.log('A结束')}
// b是:(next) => (action) => { console.log('C开始'); next(action); console.log('C结束')}
// b(...args)是:(action) => { console.log('C开始'); (...args)(action); console.log('C结束')}
// a(b(...args))是:(action) => { console.log('A开始'); console.log('B开始'); console.log('C开始'); (...args)(action); console.log('C结束') console.log('B结束') console.log('A结束')}
// 总返回值:(...args) => (action) => { console.log('A开始'); console.log('B开始'); console.log('C开始'); (...args)(action); console.log('C结束') console.log('B结束') console.log('A结束')}
// 第二步:将第一步返回的函数 (...args) => (action) => {...(...args)(action);...} 传入参数 (store.dispatch) 并执行
dispatch = (action) => { console.log('A开始'); console.log('B开始'); console.log('C开始'); store.dispatch(action); console.log('C结束') console.log('B结束') console.log('A结束')}
// 第二步格式化一下:
// dispatch = (action) => {
// console.log('A开始')
// console.log('B开始')
// console.log('C开始')
// store.dispatch(action)
// console.log('C结束')
// console.log('B结束')
// console.log('A结束')
// }
return {
...store,
dispatch,
// dispatch就是一个函数,经过redux中间件执行各种副作用后,调用store对象上的 dispatch()方法
// 而store对象又是通过 createStore()方法生成的,里面有dispatch()方法的实现
// store上的dispatch方法,主要干了两件事情
// 1. 传递action给reducer,更新state
// 2. state更新后,执行监听数组中的所有监听函数listener
}
}
}
(3) compose()
- 比如:compose(a, b, c)(1)
- 函数签名:
(...funcs) => funcs.reduce((a, b) => (...args) => a(b(...args)))
- 返回值:一个函数,因为可以通过compose()()这样的方式调用,就说明compose()返回了一个函数
- 作用:
- 从右至左,将右边函数的返回值作为左边函数的参数传入
- compose函数就是洋葱模型
- compose(A, B, C)(arg) === A(B(C(arg)))
// redux4.0.5
// compose源码
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
// 当compose的参数
// 长度为0时,即没有参数时,返回一个函数,该函数直接将传入的参数返回
// 长度为1时,返回这个参数函数
// 长度大于1时,返回参数数组执行reduce迭代的结果,reduce返回在这里返回的是一个函数
// 注意:
// compose()的返回值是一个函数,所以相当于高阶函数,可以通过compose(a,b,c)(d)这样的方式调用
--
现在假设有这样一段代码
const a = (number) => number + 1;
const b = (number) => number + 2;
const c = (number) => number + 3;
const res = compose(a, b, c)(1)
// compose的参数有三个,所以执行 return funcs.reduce((a, b) => (...args) => a(b(...args)))
// 所以 res = [a, b, c].reduce((a, b) => (...args) => a(b(...args)))返回了一个函数, 传入的参数是 1, 并调用执行
// 先看 compose(a, b, c) 返回了什么
// [a, b, c].reduce((a, b) => (...args) => a(b(...args)))
// 第一步:
// 累积变量: a
// 当前变量: b
// b(...args)返回值:(...args) + 2
// a(b(...args))返回值:(...args) + 2 + 1
// 第一步总返回值: (...args) => (...args) + 2 + 1 注意返回值是一个函数,这个函数并未被调用
// 第二步:
// 累积变量: (...args) => (...args) + 2 + 1,上一步的返回值作为新的累积变量
// 当前变量: c
// c(...args)返回值:(...args) + 3
// ab(c(...args)):即调用ab(),参数是 c(...args)返回值(...args) + 3
// ab函数 (...args) => (...args) + 2 + 1
// ab函数参数 (...args) + 3
// ab函数最终的返回值:(...args) + 3 + 2 + 1
//第二步总返回值:(...args) => (...args) + 3 + 2 + 1
// 第三步
const res = compose(a, b, c)(1)
// 1. compose(a, b, c)返回了一个函数:(...args) => (...args) + 3 + 2 + 1
// 2. const res = composeRes(1)
// 3. const res = 1 + 3 + 2 + 1
// 4. const res = 7
console.log(res, 'res')
// 7
// 4 + 2 + 1
总结:
1. 从以上过程可以看出,只有最右边的中间件可以接收多个参数,左边函数的参数都是右边函数的返回值
- compose 2020/12/9复习
redux中的compose函数源码
function compose(...funcs) {
if (funcs.length === 0) { // ------------- (1) 如果compose函数没有传入参数,就返回一个函数,该函数直接将参数值返回
return arg => arg
}
if (funcs.length === 1) { // ------------- (2) 如果compose函数传入了一个参数,那么直接返回该函数
return funcs[0]
}
// ---------------------------------------- (3) 当compose的参数大于1个参数时,返回迭代器reduce,而reduce返回的是一个函数
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
const a = (num) => num + 1;
const b = (num) => num + 2;
const c = (num) => num + 3;
const resFn = compose(a, b, c)(1)
console.log(resFn, 'resFn')
compose分析:
(1) 如果compose函数没有传入参数,就返回一个函数,该函数直接将参数值返回
(2) 如果compose函数传入了一个参数,那么直接返回该函数
(3) 当compose的参数大于1个参数时,返回迭代器reduce,而reduce返回的是一个函数
- compose(a, c, c)
- 本例中compose函数一共传入了两层参数
- 第一层:一共传入了三个函数作为参数,分别是 a b c
- 第二层:传入了参数 1
第一步:第一层函数被调用 compose(a, b, c)
[a, b, c].reduce((a, b) => (...args) => a(b(...args)))
第二步:
- reduce第一次迭代
- (...args) => a(b(...args))
- b(...args)执行的结果作为a的参数 => a((...args) + 2)
- a((...args) + 2)执行的结果 => (...args) + 2 + 1
- reduce的总的返回值 => (...args) => (...args) + 2 + 1
第三步:
- reduce的第二次迭代
- (...args) => ab迭代结果(c(...args))
- c(...args)执行的结果作为 ab迭代结果的参数 => ((...args) + 3) => (...args) + 2 + 1
- ab((...args) + 3)执行的结果 => (...args) + 3 + 2 + 1
- reduce的总的返回值 => (...args) => (...args) + 3 + 2 + 1
第四步:
- compose的最终形态
- const composex = (...args) => (...args) + 3 + 2 + 1
第五步:第二层函数被调用compose(a,b,c)返回的函数在调用(1)
- composex(1)
- 返回值 1+3+2+1
- 7
(4) bindActionCreators()
- bindActionCreators(actionCreators, dispatch)
- 参数
- ( actionCreators ) 是一个 ( 函数 ) 或者一个 ( 对象 )
- dispatch
- 返回值
- 如果参数actionCreators是一个函数
- 直接返回 bindActionCreator() 函数的执行结果,即一个函数
- 返回值是:
() => dispatch(actionsCreator.apply(this, arguments))
=>() => dispatch(action)
- 如果参数actionCreators是一个对象
- 返回一个
boundActionCreators
对象,即一个对象 - 返回值是:
{actionCreator的函数名: () => dispatch(actionCreator.apply(this, arguments)), ...}
- 返回一个
- 返回值总结
- 如果参数actionCreators是一个函数,返回一个函数
- 如果参数actionCreators是一个对象,返回对象
- 如果参数actionCreators是一个函数
- bindActionCreator()
- 函数签名:
(actionCreator, dispatch) => () => dispatch(actionCreator.apply(this, arguments))
- 简化函数签名:
(actionCreator, dispatch) => () => dispatch(action)
- 函数签名:
function bindActionCreator(actionCreator, dispatch) {
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
}
export default function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
// 如果 actionCreators 是一个函数,就直接返回 bindActionCreator(actionCreators, dispatch) 执行的结果
// bindActionCreator(actionCreators, dispatch)
// 返回的是一个函数:() => dispatch(actionCreator.apply(this, arguments))
// 即:如果参数actionCreators是一个函数,返回下面这样一个函数
// () => dispatch(actionCreator.apply(this, arguments))
// () => dispatch(action)
// 因为actionCreator是action创建函数,调用返回一个action
if (typeof actionCreators !== 'object' || actionCreators === null) {
throw new Error(
`bindActionCreators expected an object or a function, instead received ${
actionCreators === null ? 'null' : typeof actionCreators
}. ` +
`Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
)
}
// 如果参数actionCreators不是对象或者为null,则报错
// 下面是参数actionCreators是一个对象的情况
const boundActionCreators = {}
for (const key in actionCreators) {
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
// boundActionCreators
// key是:函数名
// value是: () => dispatch(actionCreator.apply(this, arguments)) 这样一个函数
// value简化:() => dispatch(action)
}
}
return boundActionCreators
}
(5) combineReducers()
- combineReducers(reducers)
- 参数
- reducers 是一个对象
- key:reducer函数的 函数名
- value:reducer函数
- reducers 是一个对象
- 返回值
- 返回 combination() 函数
- combination(state, action)
- 返回值:
- 返回一个对象
- 每个reducer()执行返回的新的state组成的对象
- key:传入combineReducers(reducers)的reducers对象中的key
- value: 是state
-
{key1: state1, key2: state2}
这样的对象
- 返回值:
combineReducers() 核心代码
export default function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
if (process.env.NODE_ENV !== 'production') {
if (typeof reducers[key] === 'undefined') {
// 如果当前环境不是生成环境并且reducers的某个key对应的值是undefined,抛错
warning(`No reducer provided for key "${key}"`)
}
}
if (typeof reducers[key] === 'function') {
// reducer是函数,就收集到finalReducers对象中
// 等于过滤出reducers中是函数的reducer
finalReducers[key] = reducers[key]
}
}
const finalReducerKeys = Object.keys(finalReducers)
return function combination(state = {}, action) {
let hasChanged = false
const nextState = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
// 1. hasChanged = true 则 hasChanged为true
// 2. 只要有一次nextStateForKey 和 previousStateForKey不同,就说明整个state不同,hasChanged就为true
}
hasChanged =
hasChanged || finalReducerKeys.length !== Object.keys(state).length
return hasChanged ? nextState : state
// 如果state变化了,返回新的state (nextState)
// 如果state没有变化,返回就的state (state)
}
}
(6) createStore 中具体的方法
- 先复习下第一步中的createStore()方法
- createStore(reducer, preloadedState, enhancer)
-
- 如果enhancer不存在,就返回自己内部定义的几个方法组成的对象:getState, dispatch, subscribe, replaceReducer,observable等
-
- 如果enhancer存在,并且是函数的话,返回返回 enchancer(createStore)(reducer, perloadedState)
- enhancer()函数声明:以applyMiddleware为例
- applyMiddleware()的函数声明:
(middlewares) => createStore => (...args) => ({...store, dispatch})
- 在 applyMiddleware()内部也会调用传入的 createStore(reducer, preloadedState) 生成 store
(6-1) createStore - dispatch()
- dispatch(action)
- 参数:
- action对象,必须要有type字段
- 返回值
- action
- 主要作用:
- 1. 将action传递给reducer纯函数,更新state
- 2. 更新state后,执行监听数组中所有的订阅了该state的监听函数
dispatch()
export default function createStore(reducer, preloadedState, enhancer) {
let currentReducer = reducer // 缓存reducer,reducer是传入的参数
let currentState = preloadedState // 缓存preloadedState,preloadedState是传入的参数
let currentListeners = [] // 新建监听者数组
let nextListeners = currentListeners // 赋值,即两个变量指向同一个堆地址,修改相互影响,注意区分赋值,浅拷贝,深拷贝
let isDispatching = false // 标志位,用来标记 dispatch 执行过程
function dispatch(action) { // 接收action对象作为参数
if (!isPlainObject(action)) {
// 如果不是纯对象抛错
// 纯对象是必须通过对象字面量声明的对象 或者 通过构造函数声明的对象
// 即 {} 或者 new Object() 生成的对象
// 数组,Date对象,Regexp对象,Error对象,Function对象等都不是纯对象
// 这里action不是对象就抛错,如果不是对象,可能是个函数,比如异步提交的函数,就需要通过redux中间件处理
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
}
if (typeof action.type === 'undefined') {
// action对象中必须要有type字段
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
)
}
if (isDispatching) {
// 如果正在dispatching,抛错
throw new Error('Reducers may not dispatch actions.')
}
try {
isDispatching = true
currentState = currentReducer(currentState, action)
// reducer函数的作用就是:接收prevState和action,返回一个新的state
// 这里将新的state赋值给变量
} finally {
isDispatching = false
}
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
// 当reducer纯函数更新了最新的state后,会执行监听数组中的所有监听函数
}
return action
}
return {
dispatch,
}
}
(6-2) createStore - subscribe()
- 参数
- listener 监听函数
- 返回值
- 返回 unSubscribe() 函数
- unSubscribe()负责取消订阅监听事件
- 主要作用
- 添加监听函数 listener
- 返回取消监听的函数 unSubscribe
subscribe()
export default function createStore(reducer, preloadedState, enhancer) {
let currentListeners = []
let nextListeners = currentListeners // 赋值,修改相互影响,两个变量指针指向同一个堆地址
let isDispatching = false
// isDispatching 初始值为false
// isDispatching
// true:在dispatch()中调用reducer()前isDispatching会被修改为true
// false:更新完state后isDispatching会被修改为false
function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
// 做一层浅拷贝
// 当两个对象的属性值是基本类型的数据时,修改互不影响
// 注意区分赋值,浅拷贝,深拷贝的区别
}
}
function subscribe(listener) {
if (typeof listener !== 'function') {
// listener必须是个函数
throw new Error('Expected the listener to be a function.')
}
if (isDispatching) {
// 只执行dispatch过程中,不允许subscribbr
throw new Error(
'You may not call store.subscribe() while the reducer is executing. ' +
'If you would like to be notified after the store has been updated, subscribe from a ' +
'component and invoke store.getState() in the callback to access the latest state. ' +
'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
)
}
let isSubscribed = true
ensureCanMutateNextListeners()
nextListeners.push(listener)
// 添加监听函数到nextListeners
// 保证 nextListeners 和 currentListeners 两个数组中是不同的listener
return function unsubscribe() {
if (!isSubscribed) {
// 没有监听函数直接返回
return
}
if (isDispatching) {
// 在dispatch()执行时,不能取消订阅
// 因为idispatch(action)主要做两件事情
// 1. 将action传递给reducer纯函数,去更新state
// 2. state更新之后,去执行监听数组中的所有监听函数
throw new Error(
'You may not unsubscribe from a store listener while the reducer is executing. ' +
'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
)
}
isSubscribed = false
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
// 从nextListeners中删除该 listener
}
}
return {
subscribe,
}
}
(6-3) createStore - getState()
getState()
function getState() {
if (isDispatching) {
throw new Error(
'You may not call store.getState() while the reducer is executing. ' +
'The reducer has already received the state as an argument. ' +
'Pass it down from the top reducer instead of reading it from the store.'
)
}
return currentState
}
(6-4) createStore - replaceReducer()
replaceReducer()
function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') {
throw new Error('Expected the nextReducer to be a function.')
}
currentReducer = nextReducer // 直接赋值传入的新的nextReducer
dispatch({ type: ActionTypes.REPLACE })
// 触发内置的replace事件
// 即执行dispatch()方法,action是 { type: ActionTypes.REPLACE }
}
React-Redux
Provider
- 主要的作用:
- 把 store 通过context的方法传递给Context.Provider组件包裹的子组件
- 在 Provider 组件内部,判断 store.getState() 获取的 state 是否变化,变化了通过setState()更新storeState
- setState更新最新的store中的state后,Provider组件包裹的所有 ( 子组件 ) 就会 ( 重新渲染 )
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { ReactReduxContext } from './Context'
class Provider extends Component {
constructor(props) {
super(props)
const { store } = props
// 从 props中获取 store
// Provider组件中是具有 store属性的
//
this.state = {
storeState: store.getState(),
store
}
}
componentDidMount() {
this._isMounted = true
// _isMounted 表示是否挂载
// _isMounted 在 componentDidMount 中为 true
// _isMounted 在 componentWillUnmount 中为 false,卸载了
this.subscribe()
// 调用该组件中自定义的方法subscribe
// 该方法的主要作用:
// 比对是不是最新的storeState,如果storeState变化了,就用setState()更新state
// state更新后, 所有子组件就都会重新渲染
}
componentWillUnmount() {
if (this.unsubscribe) this.unsubscribe()
// 卸载时,取消订阅
this._isMounted = false
}
componentDidUpdate(prevProps) {
// componentDidUpdate(prevProps, prevState)
if (this.props.store !== prevProps.store) {
// 更新后,store变化,取消订阅
if (this.unsubscribe) this.unsubscribe()
this.subscribe()
// 调用该组件中自定义的方法subscribe
// 该方法的主要作用:
// 比对是不是最新的storeState,如果storeState变化了,就用setState()更新state
// state更新后, 所有子组件就都会重新渲染
}
}
subscribe() {
const { store } = this.props
this.unsubscribe = store.subscribe(() => { // 定义unsubscribe取消订阅的方法
// store.subscribe(listener)
// 参数
// 参数是一个监听函数
// 返回值
// 返回 unsubscribe
const newStoreState = store.getState()
if (!this._isMounted) {
return
}
this.setState(providerState => {
// If the value is the same, skip the unnecessary state update.
if (providerState.storeState === newStoreState) {
// storeState 没有变化,就直接return,使用以前的 storeState
return null
}
return { storeState: newStoreState }
// storeState变化了,使用最新的newStoreState,更新storeState
})
})
// Actions might have been dispatched between render and mount - handle those
const postMountStoreState = store.getState()
if (postMountStoreState !== this.state.storeState) {
this.setState({ storeState: postMountStoreState })
// storeState变化就更新为最新的tore.getState()的值
}
}
render() {
const Context = this.props.context || ReactReduxContext
// ReactReduxContext 是通过 React.createContext(null)生成的 Context 对象
// 提供 context 给 Context.Provider 包裹的所有子组件
// calss类型的组件,在 class.contextType = Context 后,通过 this.context获取 context的值
// function类型的组件,可以通过 Context.Provider 和 Context.Consumer 这样的方式,包裹function组件后,在参数中获取 context
return (
{this.props.children}
)
}
}
Provider.propTypes = {
store: PropTypes.shape({
subscribe: PropTypes.func.isRequired,
dispatch: PropTypes.func.isRequired,
getState: PropTypes.func.isRequired
}),
context: PropTypes.object,
children: PropTypes.any
}
export default Provider
connect
connect([mapStateToProps],[mapDispatchToProps],[mergeProps],[options])(component)
- 函数较多,慢慢读吧
项目源码 - 源码分析的地址
- 2021/09/19 review 更新
- redux react-redux源码分析仓库
- 其他源码分析-仓库
- 具体文件夹:
src => SOURCE-CODE-ANALYSIS => REDUX文件夹中
资料
redux源码 https://juejin.im/post/6844903600456466445
图解洋葱模型(中间件的原理,重要)(这个老哥是真的强啊) https://juejin.im/post/6844903597776306190
逐行阅读redux源码(一) createStore https://juejin.im/post/6844903710158487565#heading-0
reacr-redux => Provider https://juejin.im/post/6844903735446110215
react-redux 庖丁解牛 https://juejin.im/post/6844903488699236359
reacr-redux => connect https://github.com/dwqs/blog/issues/38