React中Redux核心原理深入分析

目录
  • 一、Redux是什么
  • 二、Redux的核心思想
  • 三、Redux中间件原理
  • 四、手写一个Redux
  • 总结

一、Redux是什么

众所周知,Redux最早运用于React框架中,是一个全局状态管理器。Redux解决了在开发过程中数据无限层层传递而引发的一系列问题,因此我们有必要来了解一下Redux到底是如何实现的?

二、Redux的核心思想

Redux主要分为几个部分:dispatch、reducer、state。

我们着重看下dispatch,该方法是Redux流程的第一步,在用户界面中通过执行dispatch,传入相对应的action对象参数,action是一个描述类型的对象,紧接着执行reducer,最后整体返回一个store对象,我们来看下这部分的源码:

// 主函数createStore
// 返回一个store对象
export default function createStore(reducer, preloadedState, enhancer) {
  // 增强器
  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.')
  }
  let currentReducer = reducer
  let currentState = preloadedState
  let currentListeners = []
  let nextListeners = currentListeners
  let isDispatching = false
  // 获取最终的state
  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
  }
  // dispatch
  // 参数action
  function dispatch(action) {
      // 校验传入的action
    // action必须是个对象,否则抛出错误信息
    if (!isPlainObject(action)) {
      throw new Error(
        'Actions must be plain objects. ' +
          'Use custom middleware for async actions.'
      )
    }
    // 检验action对象的必要属性
    // type属性是action对象必要的属性
    // 如果传入的action没有type属性,则抛出错误信息
    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. ' +
          'Have you misspelled a constant?'
      )
    }
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.')
    }
    try {
      isDispatching = true
      // 执行传入的reducer函数
      // 返回state,给currentState赋值
      currentState = currentReducer(currentState, action)
    } finally {
        // 一个dispatch执行完,还原状态
      isDispatching = false
    }
    // 执行订阅函数队列
    // dispatch执行的同时会一并执行订阅队列
    const listeners = (currentListeners = nextListeners)
    for (let i = 0; i < listeners.length; i++) {
      const listener = listeners[i]
      listener()
    }
    // 返回action
    return action
  }
  // When a store is created, an "INIT" action is dispatched so that every
  // reducer returns their initial state. This effectively populates
  // the initial state tree.
  // 默认执行一次dispatch,做初始化
  dispatch({ type: ActionTypes.INIT })
  // 返回一个store对象
  return {
    dispatch,
    subscribe,
    getState,
    ...
  }
}

通过源码我们可以基本清楚,通过执行createStore方法,最终会返回一个store对象,该对象主要暴露几个属性,我们主要关注比较常用的:dispatch、getState、getState,看下实际用例:参考React实战视频讲解:进入学习

import createStore from 'redux'
// 创建一个reducer
function reducer(state={}, action) {
    switch(action.type) {
        case 'TEST':
        return {
            ...state,
            test: 'test success'
        }
    }
}
// 返回store
const store = createStore(reducer, initState={})
// 执行dispatch
store.dispatch({
    type: 'TEST'
})
const state = store.getState() // 返回 {test: 'TEST'}

三、Redux中间件原理

接下来我们来探讨Redux的另一个重要组成部分—中间件。什么是Redux的中间件?Redux中间件其实是通过重写createStore来增强和扩展原来的dispatch方法,使其能够在执行dispatch的同时可以同步执行其它方法,比如redux-thunk就是一个处理异步的中间件:

function createThunkMiddleware(extraArgument) {
    // 中间件规定格式
    // 闭包返回三层嵌套
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }
    return next(action);
  };
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;

下载了中间件,那么我们来看下如何使用中间件:

import createStore, {<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->applyMiddleWare} from 'reduximport reduxThunk from 'redux-thunk'// 创建一个reducerfunction reducer(state={}, action) { switch(action.type) { case 'TEST': return { ...state, test: 'test success' } }}// 返回store// 中间件作为applyMiddleWare的参数传入createStoreconst store = createStore(reducer, initState={},applyMiddleWare(reduxThunk))

我们会发现,中间件的使用方式是用applyMiddleWare把中间件作为参数传入createStore中,那么applyMiddleWare是如何实现的?在这之前我们先看下createStore方法的第三个参数是什么,我们回看下createStore源码:

export default function createStore(reducer, preloadedState, enhancer) {
  ...
  // 增强器
  // 第三个参数是enhancer,也就是我们传入的applyMiddleWare
  if (typeof enhancer !== 'undefined') {
    if (typeof enhancer !== 'function') {
      throw new Error('Expected the enhancer to be a function.')
    }
    // 在这里return了enhancer结果
    // 传入了createStore,reducer,preloadedState
    // 实际上是重写了createStore
    return enhancer(createStore)(reducer, preloadedState)
  }
  ...
}

看完了enhancer的实际作用,我们可以弄清楚applyMiddleWare的实现原理,请看源码:

import compose from './compose'
// 传入middlewares中间件
export default function applyMiddleware(...middlewares) {
  // 闭包嵌套返回2个方法
  return createStore => (...args) => {
      // 返回store
    const store = createStore(...args)
    let dispatch = () => {
      throw new Error(
        'Dispatching while constructing your middleware is not allowed. ' +
          'Other middleware would not be applied to this dispatch.'
      )
    }
    // 返回一个对象
    // 包含getState方法和dispatch方法
    const middlewareAPI = {
      getState: store.getState,
      dispatch: (...args) => dispatch(...args) // 返回一个全新的dispatch方法,不污染原来的dispatch
    }
    // 执行中间件第一层方法
    // 回顾下中间的格式:({getState, dispatch}) => next => action => next(action)
    // 这里会比较绕
    const chain = middlewares.map(middleware => middleware(middlewareAPI)) // 返回一个中间件的函数集合[next => action => next(action), next => action => next(action)]
    // 使用compose聚合chain函数集合
    // 返回新的dispatch
    dispatch = compose(...chain)(store.dispatch)
    return {
      ...store,
      dispatch
    }
  }
}

这里可能会让人很疑惑,不大清楚的童鞋可以先看下中间件的规范写法,这里还有一个重要的函数compose,我们来看下compose怎么处理chain函数集合的,请看源码:

/** * Composes single-argument functions from right to left. The rightmost * function can take multiple arguments as it provides the signature for * the resulting composite function. * * @param {...Function} funcs The functions to compose. * @returns {Function} A function obtained by composing the argument functions * from right to left. For example, compose(f, g, h) is identical to doing * (...args) => f(g(h(...args))). */
// 传入聚合函数集合
// 集合为:[next => action => next(action), next => action => next(action)]
// 返回一个新的函数: (arg) => arg
export default function compose(...funcs) {
  // 判断如果没有则返回一个新函数
  // 可以联想一下dispatch的定义
  // function dispatch(action) {
      ...
      return action
  }
  if (funcs.length === 0) {
    return arg => arg
  }
  // 判断如果只有一个中间件,则直接返回第一个
  if (funcs.length === 1) {
    return funcs[0]
  }
  // 这里用了reduce函数
  // 把后一个的中间件的结果当成参数传递给下一个中间件
  // 函数列表的每个函数执行后返回的还是一个函数:action => next(action)
  // 这个函数就是新的dispatch
  // 最后返回函数:(...args) => action => args(action)
  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

compose的源码及其简洁,但是很精髓,几乎是整个中间件最出彩的地方。通过reduce把每个中间件都执行一遍,并且是通过管道式的传输,把每个中间件的返回结果当成参数传递给下一个中间件,实现了剥洋葱式的中间件模式。这里比较难理解,新手可以先写几个简单的中间件,然后再去慢慢理解为什么要这么处理,理解后就会知道这段代码有多简洁了。

四、手写一个Redux

源码解析完了,我们来简单实现一个redux。

createStore

// 判断值是否是对象类型
function isPlainObject(obj) {
    if(!obj) {
        reutrn false
    }
    return Object.prototype.toString.call(obj) === '[object, Object]'
}
export default createStore(reducer, enhancer) {
    // 先判断有没有传入中间件
    // 有则之间返回
    if(typeof enhancer !== 'undefined') {
        // 必需是个函数
        // 因为需要传参
        if(typeof enhancer !== 'function') {
            return
        }
        return enhancer(createStore)(reducer)
    }
    let state = {} // 初始化state
    let listeners = [] // 发布订阅函数队列
    // 定义getState 函数
    function getState() {
        // 直接返回state
        return state
    }
    // 定义dispatch 函数
    function dispatch(action) {
        try{
            // 执行reducer, 返回state
            state = reducer(state, action)
        }catch(e) {
            console.log('dispatch error: 'e)
        }
        // 订阅
        listeners.forEach(listener => listener())
        // 返回action
        return action
    }
    // 定义subscribe 函数
    function subscribe(listener) {
        if(!listener) {
            return
        }
        // 必需是回掉函数
        // 因为需要在dispatch里执行
        if(typeof listener !== 'function') {
            return
        }
        Listeners.push(listener)
    }
    // 返回对象:包含getState, dispatch, subscribe 三个方法
    return {
        getState,
        dispatch,
        subscribe
    }
}

compose

    function compose(...funs) {
        if(!funs) {
            return arg => arg
        }
        if(funs.length === 1) {
            return funs[0]
        }
        // 遍历传入函数,返回一个新函数
        return funs.reduce((a,b) => (...args) => a(b(...args)))
    }

applyMiddleWare

import compose from './compose'
function applyMiddleWare(...middlewares) {
    return createStore => reducer => {
        // 先返回一个store
        const store = createStore(reducer)
        // 创建middleApi
        const middleApi = {
            getState: store.getState,
            dispatch: (...args) => store.dispatch(...args) // 返回一个新的dispatch
        }
        // 注入middleApi
        // 并返回函数集合
        const chain = middlewares.map(middleWare => middleWare(middleApi))
        // 通过compose函数,执行所有中间件,并返回一个新的dispatch
        const dispatch = compose(...chain)(store.dispatch)
        // 返回store对象
        return {
            getState: store.getState,
            dispatch
        }
    }
}

logger中间件

    function logger({getState, dispatch}) {
        return function(next) {
            return function(action) {
                console.log('prev')
                next(action)
                console.log('done')
            }
        }
    }

测试

    import createStore from './myCreateStore'
    import applyMiddleWare from './myApplyMiddleWare'
    import logger from './logger'
    // 创建reducer
    function reducer(state={}, action) {
        switch(action.type) {
            case 'TEST':
            return {
                ...state,
                test: 'test success'
            }
        }
    }
    // 引入中间件
    const middleware = applyMiddleWare(logger)
    const store = createStore(reducer, middleware) // 返回{getState, dispatch}

总结

至此一个完整的redux我们就已经分析完了,个人认为中间件的compose这里是比较不好理解的点,但是只要明白中间件主要要解决的是增强dispatch函数,就可以顺着这个思路去理解。接着再试着写几个中间件,进一步理解为什么中间件的格式需要返回嵌套的三层函数,明白了这两个点,redux的原理也就基本能够明白了,有问题欢迎在评论中指出。

到此这篇关于React中Redux核心原理深入分析的文章就介绍到这了,更多相关React Redux内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 在 React 中使用 Redux 解决的问题小结

    目录 在 React 中使用 Redux 解决的问题 在 React 项目中加入 Redux 的好处 React + Redux 安装 Redux React 中 Redux 的工作流程 React 计数器案例 使用 Redux Provide 组件 connect 方法 使用 bindActionCreators 方法继续简化 代码重构 为 Action 传递参数 Redux 弹出框 初始化静态内容 添加默认隐藏状态 定义操作按钮 衍生的问题 拆分合并 reducer 拆分 合并 调整组件 在

  • React状态管理Redux的使用介绍详解

    目录 1. 简介 2. 核心概念 3. redux工作流 4. 模拟redux工作流程 5. 使用redux 6. react-redux 1. 简介 Redux 是 JavaScript 应用的状态容器(对象),提供可预测的状态管理.可以让你开发出行为稳定可预测的应用,运行于不同的环境(客户端.服务器.原生应用),并且易于测试.Redux 除了和 React 一起用外,还支持其它界面库. 解决的问题:多层级组件间通信问题. 2. 核心概念 单一数据源 整个redux中的数据都是集中管理,存储于

  • React状态管理Redux原理与介绍

    目录 一.Redux 二.Redux的组成 2.1 store 2.2 state 2.3 action 2.4 reducer 三.三大原则 3.1 单一数据源 3.2 State只读 3.3 使用纯函数修改State 四.基于Redux的TodoList 五.react-redux 5.1 connect方法 5.2 Provider组件 一.Redux 和vuex一样,redux的出现是为了管理web应用的公共状态. 这些 state 可能包括服务器响应.缓存数据.本地生成尚未持久化到服务

  • 一文详解React Redux使用方法

    目录 一.理解JavaScript纯函数 1.1 纯函数的概念 1.2 副作用概念的理解 1.3 纯函数在函数式编程的重要性 二.Redux的核心思想 2.1 为什么需要 Redux 2.2 Redux的核心概念 2.2.1 store 2.2.2 action 2.2.3 reducer 2.3 Redux的三大原则 2.3.1 单一数据源 2.3.2 State是只读的 2.3.3 使用纯函数来执行修改 2.4 Redux 工作流程 三.Redux基本使用 3.1 创建Store的过程 3.

  • React Redux应用示例详解

    目录 一 React-Redux的应用 1.学习文档 2.Redux的需求 3.什么是Redux 4.什么情况下需要使用redux 二.最新React-Redux 的流程 安装Redux Toolkit 创建一个 React Redux 应用 基础示例 Redux Toolkit 示例 三.使用教程 安装Redux Toolkit和React-Redux​ 创建 Redux Store​ 为React提供Redux Store​ 创建Redux State Slice​ 将Slice Reduc

  • React中Redux核心原理深入分析

    目录 一.Redux是什么 二.Redux的核心思想 三.Redux中间件原理 四.手写一个Redux 总结 一.Redux是什么 众所周知,Redux最早运用于React框架中,是一个全局状态管理器.Redux解决了在开发过程中数据无限层层传递而引发的一系列问题,因此我们有必要来了解一下Redux到底是如何实现的? 二.Redux的核心思想 Redux主要分为几个部分:dispatch.reducer.state. 我们着重看下dispatch,该方法是Redux流程的第一步,在用户界面中通过

  • React Hooks核心原理深入分析讲解

    目录 Hooks 闭包 开始动手实现 将useState应用到组件中 过期闭包 模块模式 实现useEffect 支持多个Hooks Custom Hooks 重新理解Hooks规则 React Hooks已经推出一段时间,大家应该比较熟悉,或者多多少少在项目中用过.写这篇文章简单分析一下Hooks的原理,并带大家实现一个简易版的Hooks. 这篇写的比较细,相关的知识点都会解释,给大家刷新一下记忆. Hooks Hooks是React 16.8推出的新功能.以这种更简单的方式进行逻辑复用.之前

  • React中阻止事件冒泡的问题详析

    前言 最近在研究react.redux等,网上找了很久都没有完整的答案,索性自己整理下,这篇文章就来给大家介绍了关于React阻止事件冒泡的相关内容,下面话不多说了,来一起看看详细的介绍吧 在正式开始前,先来看看 JS 中事件的触发与事件处理器的执行. JS 中事件的监听与处理 事件捕获与冒泡 DOM 事件会先后经历 捕获 与 冒泡 两个阶段.捕获即事件沿着 DOM 树由上往下传递,到达触发事件的元素后,开始由下往上冒泡. IE9 及之前的版本只支持冒泡 |  A  --------------

  • React Diff原理深入分析

    在了解Diff前,先看下React的虚拟DOM的结构 这是html结构 <div id="father"> <p class="child">I am child p</p> <div class="child">I am child div</div> </div> 这是React渲染html时的js代码   自己可以在babel上试试 React.createElemen

  • 深入理解React Native核心原理(React Native的桥接(Bridge)

    在这篇文章之前我们假设你已经了解了React Native的基础知识,我们会重点关注当native和JavaScript进行信息交流时的内部运行原理. 主线程 在开始之前,我们需要知道在React Native中有三个主要的线程: shadow queue:负责布局工作 main thread:UIKit 在这个线程工作(译者注:UI Manager线程,可以看成主线程,主要负责页面交互和控件绘制的逻辑) JavaScript thread:运行JS代码的线程 另外,一般情况下每个native模

  • 一文搞懂redux在react中的初步用法

    Redux是一个数据状态管理插件,当使用React或是vue开发组件化的SPA程序时,组件之间共享信息是一个非常大的问题.例如,用户登陆之后客户端会存储用户信息(ID,头像等),而系统的很多组件都会用到这些信息,当使用这些信息的时候,每次都重新获取一遍,这样会非常的麻烦,因此每个系统都需要一个管理多组件使用的公共信息的功能,这就是redux的作用. 如果你是从来没有接触过redux的开发者,希望您能够有耐心的看一看,我也是看好很多博客慢慢自己总结的!!!!比大佬们一个一个分布找要强一点. imp

  • React组件封装中三大核心属性详细介绍

    目录 1.介绍 2.state 概念 演示 3.props 概念 props与state区别 4.refs 概念 refs种类 5.父子组件 什么是父子组件 父子组件之间传值 1.介绍 React组件中默认封装了很多属性,有的是提供给开发者操作的,其中有三个属性非常重要:state.props.refs. 2.state 概念 state是类组件的一个默认属性,用于标识类组件的状态,负责更新UI,让页面动态变化,当state变化时,组件将被重新渲染. 函数组件没有对象属性(babel默认开启了局

  • React懒加载实现原理深入分析

    目录 1.代码分割 2.React的懒加载 import() 原理 React.lazy 原理 Suspense 原理 小结 1.代码分割 (1)为什么要进行代码分割? 现在前端项目基本都采用打包技术,比如 Webpack,JS逻辑代码打包后会产生一个 bundle.js 文件,而随着我们引用的第三方库越来越多或业务逻辑代码越来越复杂,相应打包好的 bundle.js 文件体积就会越来越大,因为需要先请求加载资源之后,才会渲染页面,这就会严重影响到页面的首屏加载. 而为了解决这样的问题,避免大体

  • React Fiber原理深入分析

    目录 为什么需要 fiber fiber 之前 fiber 之后 fiber 节点结构 dom 相关属性 tag key 和 type stateNode 链表树相关属性 副作用相关属性 flags Effect List 其他 lane alternate fiber 树的构建与更新 mount 过程 update 过程 总结 react16 版本之后引入了 fiber,整个架构层面的 调度.协调.diff 算法以及渲染等都与 fiber 密切相关.所以为了更好地讲解后面的内容,需要对 fib

随机推荐