千家信息网

如何进行Redux的源码解析

发表于:2025-01-19 作者:千家信息网编辑
千家信息网最后更新 2025年01月19日,如何进行Redux的源码解析,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。预热redux 函数内部包含了大量柯里化函数以
千家信息网最后更新 2025年01月19日如何进行Redux的源码解析

如何进行Redux的源码解析,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

预热

redux 函数内部包含了大量柯里化函数以及代码组合思想

柯里化函数(curry)

通俗的来讲,可以用一句话概括柯里化函数:返回函数的函数

// example const funcA = (a) => {   return const funcB = (b) => {     return a + b   } };

上述的funcA函数接收一个参数,并返回同样接收一个参数的funcB函数。

柯里化函数有什么好处呢?

  • 避免了给一个函数传入大量的参数--我们可以通过柯里化来构建类似上例的函数嵌套,将参数的代入分离开,更有利于调试

  • 降低耦合度和代码冗余,便于复用

举个栗子:

// 已知listA, listB两个Array,都由int组成,需要筛选出两个Array的交集 const listA = [1, 2, 3, 4, 5]; const listB = [2, 3, 4];  const checkIfDataExist = (list) => {   return (target) => {     return list.some(value => value === target)   }; }; // 调用一次checkIfDataExist函数,并将listA作为参数传入,来构建一个新的函数。 // 而新函数的作用则是:检查传入的参数是否存在于listA里 const ifDataExist = checkIfDataExist(listA);  // 使用新函数来对listB里的每一个元素进行筛选 const intersectionList = listB.filter(value => ifDataExist(value)); console.log(intersectionList); // [2, 3, 4]

代码组合(compose)

代码组合就像是数学中的结合律:

const compose = (f, g) => {   return (x) => {     return f(g(x));   }; }; // 还可以再简洁点 const compose = (f, g) => (x) => f(g(x));

通过这样函数之间的组合,可以大大增加可读性,效果远大于嵌套一大堆的函数调用,并且我们可以随意更改函数的调用顺序

Redux

combineReducers

// 回顾一下combineReducers的使用格式  // 两个reducer const todos = (state = INIT.todos, action) => {   // .... }; const filterStatus = (state = INIT.filterStatus, action) => {   // ... };  const appReducer = combineReducers({   todos,   filterStatus });

还记得combineReducers的黑魔法吗?即:

  1. 传入的Object参数中,对象的key与value所代表的reducer function同名

  2. 各个reducer function的名称和需要传入该reducer的state参数同名

源码标注解读(省略部分):

export default function combineReducers(reducers) {   // ***次筛选,参数reducers为Object   // 筛选掉reducers中不是function的键值对   var reducerKeys = Object.keys(reducers);   var finalReducers = {}   for (var i = 0; i < reducerKeys.length; i++) {     var key = reducerKeys[i];     if (typeof reducers[key] === 'function') {       finalReducers[key] = reducers[key]     }   }    var finalReducerKeys = Object.keys(finalReducers)    // 二次筛选,判断reducer中传入的值是否合法(!== undefined)   // 获取筛选完之后的所有key   var sanityError   try {     // assertReducerSanity函数用于遍历finalReducers中的reducer,检查传入reducer的state是否合法     assertReducerSanity(finalReducers)   } catch (e) {     sanityError = e   }      // 返回一个function。该方法接收state和action作为参数   return function combination(state = {}, action) {     // 如果之前的判断reducers中有不法值,则抛出错误     if (sanityError) {       throw sanityError     }     // 如果不是production环境则抛出warning     if (process.env.NODE_ENV !== 'production') {       var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action)       if (warningMessage) {         warning(warningMessage)       }     }      var hasChanged = false     var nextState = {}     // 遍历所有的key和reducer,分别将reducer对应的key所代表的state,代入到reducer中进行函数调用     for (var i = 0; i < finalReducerKeys.length; i++) {       var key = finalReducerKeys[i]       var reducer = finalReducers[key]       // 这也就是为什么说combineReducers黑魔法--要求传入的Object参数中,reducer function的名称和要和state同名的原因       var previousStateForKey = state[key]       var nextStateForKey = reducer(previousStateForKey, action)       // 如果reducer返回undefined则抛出错误       if (typeof nextStateForKey === 'undefined') {         var errorMessage = getUndefinedStateErrorMessage(key, action)         throw new Error(errorMessage)       }       // 将reducer返回的值填入nextState       nextState[key] = nextStateForKey       // 如果任一state有更新则hasChanged为true       hasChanged = hasChanged || nextStateForKey !== previousStateForKey     }     return hasChanged ? nextState : state   } }  // 检查传入reducer的state是否合法 function assertReducerSanity(reducers) {   Object.keys(reducers).forEach(key => {     var reducer = reducers[key]     // 遍历全部reducer,并给它传入(undefined, action)     // 当***个参数传入undefined时,则为各个reducer定义的默认参数     var initialState = reducer(undefined, { type: ActionTypes.INIT })          // ActionTypes.INIT几乎不会被定义,所以会通过switch的default返回reducer的默认参数。如果没有指定默认参数,则返回undefined,抛出错误     if (typeof initialState === 'undefined') {       throw new Error(         `Reducer "${key}" returned undefined during initialization. ` +         `If the state passed to the reducer is undefined, you must ` +         `explicitly return the initial state. The initial state may ` +         `not be undefined.`       )     }      var type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.')     if (typeof reducer(undefined, { type }) === 'undefined') {       throw new Error(         `Reducer "${key}" returned undefined when probed with a random type. ` +         `Don't try to handle ${ActionTypes.INIT} or other actions in "redux/*" ` +         `namespace. They are considered private. Instead, you must return the ` +         `current state for any unknown actions, unless it is undefined, ` +         `in which case you must return the initial state, regardless of the ` +         `action type. The initial state may not be undefined.`       )     }   }) }

createStore

// 回顾下使用方法 const store = createStore(reducers, state, enhance);

源码标注解读(省略部分):

// 对于未知的action.type,reducer必须返回默认的参数state。这个ActionTypes.INIT就可以用来监测当reducer传入未知type的action时,返回的state是否合法 export var ActionTypes = {   INIT: '@@redux/INIT' }  export default function createStore(reducer, initialState, enhancer) {   // 检查你的state和enhance参数有没有传反   if (typeof initialState === 'function' && typeof enhancer === 'undefined') {     enhancer = initialState     initialState = undefined   }   // 如果有传入合法的enhance,则通过enhancer再调用一次createStore   if (typeof enhancer !== 'undefined') {     if (typeof enhancer !== 'function') {       throw new Error('Expected the enhancer to be a function.')     }     return enhancer(createStore)(reducer, initialState)   }    if (typeof reducer !== 'function') {     throw new Error('Expected the reducer to be a function.')   }    var currentReducer = reducer   var currentState = initialState   var currentListeners = []   var nextListeners = currentListeners   var isDispatching = false // 是否正在分发事件    function ensureCanMutateNextListeners() {     if (nextListeners === currentListeners) {       nextListeners = currentListeners.slice()     }   }    // 我们在action middleware中经常使用的getState()方法,返回当前state   function getState() {     return currentState   }    // 注册listener,同时返回一个取消事件注册的方法。当调用store.dispatch的时候调用listener   function subscribe(listener) {     if (typeof listener !== 'function') {       throw new Error('Expected listener to be a function.')     }      var isSubscribed = true      ensureCanMutateNextListeners()     nextListeners.push(listener)      return function unsubscribe() {       if (!isSubscribed) {         return       }        isSubscribed = false       // 从nextListeners中去除掉当前listener       ensureCanMutateNextListeners()       var index = nextListeners.indexOf(listener)       nextListeners.splice(index, 1)     }   }    // dispatch方法接收的action是个对象,而不是方法。   // 这个对象实际上就是我们自定义action的返回值,因为dispatch的时候,已经调用过我们的自定义action了,比如 dispatch(addTodo())   function dispatch(action) {     if (!isPlainObject(action)) {       throw new Error(         'Actions must be plain objects. ' +         'Use custom middleware for async actions.'       )     }      if (typeof action.type === 'undefined') {       throw new Error(         'Actions may not have an undefined "type" property. ' +         'Have you misspelled a constant?'       )     }     // 调用dispatch的时候只能一个个调用,通过dispatch判断调用的状态     if (isDispatching) {       throw new Error('Reducers may not dispatch actions.')     }      try {       isDispatching = true       currentState = currentReducer(currentState, action)     } finally {       isDispatching = false     }     // 遍历调用各个linster     var listeners = currentListeners = nextListeners     for (var i = 0; i < listeners.length; i++) {       listeners[i]()     }      return action   }   // Replaces the reducer currently used by the store to calculate the state.   function replaceReducer(nextReducer) {     if (typeof nextReducer !== 'function') {       throw new Error('Expected the nextReducer to be a function.')     }      currentReducer = nextReducer     dispatch({ type: ActionTypes.INIT })   }   // 当create store的时候,reducer会接受一个type为ActionTypes.INIT的action,使reducer返回他们默认的state,这样可以快速的形成默认的state的结构   dispatch({ type: ActionTypes.INIT })    return {     dispatch,     subscribe,     getState,     replaceReducer   } }

thunkMiddleware

源码及其简单简直给跪...

// 返回以 dispatch 和 getState 作为参数的action export default function thunkMiddleware({ dispatch, getState }) {   return next => action => {     if (typeof action === 'function') {       return action(dispatch, getState);     }      return next(action);   }; }

applyMiddleware

先复习下用法:

// usage import {createStore, applyMiddleware} from 'redux'; import thunkMiddleware from 'redux-thunk';  const store = createStore(       reducers,       state,       applyMiddleware(thunkMiddleware) );

applyMiddleware首先接收thunkMiddleware作为参数,两者组合成为一个新的函数(enhance),之后在createStore内部,因为enhance的存在,将会变成返回enhancer(createStore)(reducer, initialState)

源码标注解读(省略部分):

// 定义一个代码组合的方法 // 传入一些function作为参数,返回其链式调用的形态。例如, // compose(f, g, h) 最终返回 (...args) => f(g(h(...args))) export default function compose(...funcs) {   if (funcs.length === 0) {     return arg => arg   } else {     const last = funcs[funcs.length - 1]     const rest = funcs.slice(0, -1)     return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))   } }  export default function applyMiddleware(...middlewares) {   // 最终返回一个以createStore为参数的匿名函数   // 这个函数返回另一个以reducer, initialState, enhancer为参数的匿名函数   return (createStore) => (reducer, initialState, enhancer) => {     var store = createStore(reducer, initialState, enhancer)     var dispatch     var chain = []      var middlewareAPI = {       getState: store.getState,       dispatch: (action) => dispatch(action)     }     // 每个 middleware 都以 middlewareAPI 作为参数进行注入,返回一个新的链。此时的返回值相当于调用 thunkMiddleware 返回的函数: (next) => (action) => {} ,接收一个next作为其参数     chain = middlewares.map(middleware => middleware(middlewareAPI))     // 并将链代入进 compose 组成一个函数的调用链     // compose(...chain) 返回形如(...args) => f(g(h(...args))),f/g/h都是chain中的函数对象。     // 在目前只有 thunkMiddleware 作为 middlewares 参数的情况下,将返回 (next) => (action) => {}     // 之后以 store.dispatch 作为参数进行注入     dispatch = compose(...chain)(store.dispatch)      return {       ...store,       dispatch     }   } }

一脸懵逼?没关系,来结合实际使用总结一下:

当我们搭配redux-thunk这个库的时候,在redux配合components时,通常这么写

import thunkMiddleware from 'redux-thunk'; import { createStore, applyMiddleware, combineReducer } from 'redux'; import * as reducers from './reducers.js';  const appReducer = combineReducer(reducers); const store = createStore(appReducer, initialState, applyMiddleware(thunkMiddleware));

还记得当createStore收到的参数中有enhance时会怎么做吗?

// createStore.js if (typeof enhancer !== 'undefined') {   if (typeof enhancer !== 'function') {     throw new Error('Expected the enhancer to be a function.')   }   return enhancer(createStore)(reducer, initialState) }

也就是说,会变成下面的情况

applyMiddleware(thunkMiddleware)(createStore)(reducer, initialState)
  • applyMiddleware(thunkMiddleware)

applyMiddleware接收thunkMiddleware作为参数,返回形如(createStore) => (reducer, initialState, enhancer) => {}的函数。

  • applyMiddleware(thunkMiddleware)(createStore)

以 createStore 作为参数,调用上一步返回的函数(reducer, initialState, enhancer) => {}

  • applyMiddleware(thunkMiddleware)(createStore)(reducer, initialState)

以(reducer, initialState)为参数进行调用。

在这个函数内部,thunkMiddleware被调用,其作用是监测type是function的action

因此,如果dispatch的action返回的是一个function,则证明是中间件,则将(dispatch, getState)作为参数代入其中,进行action 内部下一步的操作。否则的话,认为只是一个普通的action,将通过next(也就是dispatch)进一步分发。

也就是说,applyMiddleware(thunkMiddleware)作为enhance,最终起了这样的作用:

对dispatch调用的action(例如,dispatch(addNewTodo(todo)))进行检查,如果action在***次调用之后返回的是function,则将(dispatch, getState)作为参数注入到action返回的方法中,否则就正常对action进行分发,这样一来我们的中间件就完成喽~

因此,当action内部需要获取state,或者需要进行异步操作,在操作完成之后进行事件调用分发的话,我们就可以让action 返回一个以(dispatch, getState)为参数的function而不是通常的Object,enhance就会对其进行检测以便正确的处理。

bindActionCreator

这个方法感觉比较少见,我个人也很少用到

在传统写法下,当我们要把 state 和 action 注入到子组件中时,一般会这么做:

import { connect } from 'react-redux'; import {addTodo, deleteTodo} from './action.js';  class TodoComponect extends Component {   render() {     return (            )   } }  function mapStateToProps(state) {   return {     state   } } function mapDispatchToProps(dispatch) {   return {     deleteTodo: (id) => {       dispatch(deleteTodo(id));     },     addTodo: (todo) => {       dispatch(addTodo(todo));     }   } } export default connect(mapStateToProps, mapDispatchToProps)(TodoComponect);

使用bindActionCreators可以把 action 转为同名 key 的对象,但使用 dispatch 把每个 action 包围起来调用

惟一使用 bindActionCreators 的场景是当你需要把 action creator 往下传到一个组件上,却不想让这个组件觉察到 Redux 的存在,而且不希望把 Redux store 或 dispatch 传给它。

// 在本组件内的应用   addTodo(todo) {     let action = TodoActions.addTodo(todo);     this.props.dispatch(action);   }      deleteTodo(id) {     let action = TodoActions.deleteTodo(id);     this.props.dispatch(action);   }      render() {     let dispatch = this.props.dispatch;     // 传递给子组件     let boundActionCreators = bindActionCreators(TodoActions, dispatch);     return (            )   } }  function mapStateToProps(state) {   return {     state   } } export default connect(mapStateToProps)(TodoComponect)

bindActionCreator源码解析

function bindActionCreator(actionCreator, dispatch) {   return (...args) => dispatch(actionCreator(...args)) }  // bindActionCreators期待一个Object作为actionCreators传入,里面是 key: action export default function bindActionCreators(actionCreators, dispatch) {   // 如果只是传入一个action,则通过bindActionCreator返回被绑定到dispatch的函数   if (typeof actionCreators === 'function') {     return bindActionCreator(actionCreators, dispatch)   }    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"?`     )   }    // 遍历并通过bindActionCreator分发绑定至dispatch   var keys = Object.keys(actionCreators)   var boundActionCreators = {}   for (var i = 0; i < keys.length; i++) {     var key = keys[i]     var actionCreator = actionCreators[key]     if (typeof actionCreator === 'function') {       boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)     }   }   return boundActionCreators }

react-redux

Provider

export default class Provider extends Component {   getChildContext() {     // 将其声明为 context 的属性之一     return { store: this.store }   }    constructor(props, context) {     super(props, context)     // 接收 redux 的 store 作为 props     this.store = props.store   }    render() {     return Children.only(this.props.children)   } }  if (process.env.NODE_ENV !== 'production') {   Provider.prototype.componentWillReceiveProps = function (nextProps) {     const { store } = this     const { store: nextStore } = nextProps      if (store !== nextStore) {       warnAboutReceivingStore()     }   } }  Provider.propTypes = {   store: storeShape.isRequired,   children: PropTypes.element.isRequired } Provider.childContextTypes = {   store: storeShape.isRequired }

connect

传入mapStateToProps,mapDispatchToProps,mergeProps,options。

首先获取传入的参数,如果没有则以默认值代替

const defaultMapStateToProps = state => ({}) // eslint-disable-line no-unused-vars const defaultMapDispatchToProps = dispatch => ({ dispatch }) const { pure = true, withRef = false } = options

之后,通过

const finalMergeProps = mergeProps || defaultMergeProps

选择合并stateProps,dispatchProps,parentProps的方式,默认的合并方式 defaultMergeProps 为:

const defaultMergeProps = (stateProps, dispatchProps, parentProps) => ({   ...parentProps,   ...stateProps,   ...dispatchProps })

返回一个以 Component 作为参数的函数。在这个函数内部,生成了一个叫做Connect的 Component

// ...   return function wrapWithConnect(WrappedComponent) {     const connectDisplayName = `Connect(${getDisplayName(WrappedComponent)})`     // 检查参数合法性     function checkStateShape(props, methodName) {}     // 合并props     function computeMergedProps(stateProps, dispatchProps, parentProps) {       const mergedProps = finalMergeProps(stateProps, dispatchProps, parentProps)       if (process.env.NODE_ENV !== 'production') {         checkStateShape(mergedProps, 'mergeProps')       }       return mergedProps     }          // start of Connect     class Connect extends Component {       constructor(props, context) {         super(props, context);         this.store = props.store || context.store                  const storeState = this.store.getState()         this.state = { storeState }         this.clearCache()       }              computeStateProps(store, props) {         // 调用configureFinalMapState,使用传入的mapStateToProps方法(或默认方法),将state map进props       }       configureFinalMapState(store, props) {}              computeDispatchProps(store, props) {         // 调用configureFinalMapDispatch,使用传入的mapDispatchToProps方法(或默认方法),将action使用dispatch封装map进props       }       configureFinalMapDispatch(store, props) {}              // 判断是否更新props       updateStatePropsIfNeeded() {}       updateDispatchPropsIfNeeded() {}       updateMergedPropsIfNeeded() {}              componentDidMount() {         // 内部调用this.store.subscribe(this.handleChange.bind(this))         this.trySubscribe()       }       handleChange() {         const storeState = this.store.getState()         const prevStoreState = this.state.storeState         // 对数据进行监听,发送改变时调用         this.setState({ storeState })       }              // 取消监听,清除缓存       componentWillUnmount() {         this.tryUnsubscribe()         this.clearCache()       }              render() {         this.renderedElement = createElement(WrappedComponent,             this.mergedProps         )         return this.renderedElement       }     }     // end of Connect          Connect.displayName = connectDisplayName     Connect.WrappedComponent = WrappedComponent     Connect.contextTypes = {       store: storeShape     }     Connect.propTypes = {       store: storeShape     }          return hoistStatics(Connect, WrappedComponent)   } // ...

我们看见,在connect的***,返回了使用hoistStatics包装的Connect和WrappedComponent

hoistStatics是什么鬼?为什么使用它?

Copies non-react specific statics from a child component to a parent component. Similar to Object.assign, but with React static keywords blacklisted from being overridden.

也就是说,它类似于Object.assign,作用是将子组件中的 static 方法复制进父组件,但不会覆盖组件中的关键字方法(如 componentDidMount)

import hoistNonReactStatic from 'hoist-non-react-statics';  hoistNonReactStatic(targetComponent, sourceComponent);

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注行业资讯频道,感谢您对的支持。

参数 函数 方法 组件 源码 组合 合法 代码 对象 时候 柯里 检查 也就是 作用 两个 也就是说 事件 部分 中间件 代表 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 作为MIS服务器的云平台 软件开发输入清单 服务器主板维修北京 服务器硬盘插在电脑哪里 零售线上线下一体化软件开发 神策网络技术北京有限公司 四川网络安全测评公司 分包商网络安全认证答案 零钱通服务器超时是什么意思 2022燃烧的远征哪个服务器好 数据库如何做网页视频 世界各国网络安全前100名 天津服务器维修维保 什么是服务器管理芯片 数据库如何选择候选码 注册滴滴服务器错误怎么办 分论点网络安全 网络安全法 哪个部门负责 网络安全教育倡导 软件开发微信主页 部落冲突服务器维护中有什么好处 我的世界网易版魂师大陆服务器怎么捡起魂环 软件开发要学什么东西 sql如何实现数据库安全性 现在网络安全学习心得体会 网络安全就业不好 手机网络安全证书叫啥 数据库商务表 关于大型数据库的关联规则 赌博游戏软件开发价格
0