前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >源码共读-Redux

源码共读-Redux

作者头像
kai666666
发布2024-07-11 19:02:07
230
发布2024-07-11 19:02:07
举报
文章被收录于专栏:橙光笔记橙光笔记

Redux是优秀的状态管理库,本节我们学习一下Redux源码,由于Redux源码是TypeScript写的,为了方便学习,本节去掉一些类型定义,转化为JavaScript来展示,另外对于错误信息我们这里就先不处理了。

使用

Redux官方示例:

代码语言:javascript
复制
import { createStore } from 'redux'

function counterReducer(state = { value: 0 }, action) {
  switch (action.type) {
    case 'counter/incremented':
      return { value: state.value + 1 }
    case 'counter/decremented':
      return { value: state.value - 1 }
    default:
      return state
  }
}

let store = createStore(counterReducer)

store.subscribe(() => console.log(store.getState()))

store.dispatch({ type: 'counter/incremented' }) // {value: 1}
store.dispatch({ type: 'counter/incremented' }) // {value: 2}
store.dispatch({ type: 'counter/decremented' }) // {value: 1}

createStore

createStore简易实现:

代码语言:javascript
复制
const randomString = () =>
  Math.random().toString(36).substring(7).split('').join('.')

const ActionTypes = {
  INIT: `@@redux/INIT${/* #__PURE__ */ randomString()}`,
  REPLACE: `@@redux/REPLACE${/* #__PURE__ */ randomString()}`,
}

function createStore(reducer, preloadedState, enhancer) {

  // 如果第二个参数是函数 第三个函数没有值的时候 认为第二个参数是enhancer
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState
    preloadedState = undefined
  }

  // enhancer有值的时候 使用enhancer来构建store
  if (typeof enhancer !== 'undefined') {
    return enhancer(createStore)(
      reducer,
      preloadedState
    )
  }

  let currentReducer = reducer
  let currentState = preloadedState
  let listeners = [];

  function getState() {
    return currentState
  }

  function subscribe(listener) {
    listeners.push(listener)

    return function unsubscribe() {
      const index = listeners.indexOf(listener)
      listeners.splice(index, 1)
    }
  }

  function dispatch(action) {
    currentState = currentReducer(currentState, action)

    listeners.forEach(listener => {
      listener()
    })
    return action
  }

  function replaceReducer(nextReducer) {
    currentReducer = nextReducer
    dispatch({ type: ActionTypes.REPLACE })
  }

  dispatch({ type: ActionTypes.INIT })

  const store = {
    dispatch: dispatch,
    subscribe,
    getState,
    replaceReducer,
  }
  return store
}

上述是createStore的简单实现,调用createStore()函数以后返回一个store对象,该对象有4个方法,如下:

1:dispatch:分发action,通过currentReducer(currentState, action)来生成新的state,并触发事件。 2: subscribe: 监听事件,实际上就是把事件添加到事件数组中,并返回移除事件函数。 3: getState:获取当前的状态。 4: replaceReducer:替换reducer。

最新的源码与我们的实现理念大致相同,只是多了类型的校验,另外事件采用双map形式(防止dispatch中调用subscribe/unsubscribe)而不是我们简单的数组,最后在事件触发时会使用变量标记,防止在分发过程中出现不合理的操作。

中间件

中间件简绍

写一个redux中间件很简单,比如写一个打印日志的中间件。

代码语言:javascript
复制
const logger = function(store) {
  return function(next) {
    return function(action) {
      console.log('prev state:', store.getState()) // 更新前的状态
      let result = next(action)
      console.log('next state', store.getState()) // 更新后的状态
      return result
    }
  }
}

// 箭头函数简写
const logger = store => next => action => {
  console.log('prev state:', store.getState()) // 更新前的状态
  let result = next(action)
  console.log('next state', store.getState()) // 更新后的状态
  return result
}

中间件是一个嵌套三层的函数,每一层都有一个参数,参数分别是storenextaction。上面是redux-logger中间件的简单实现,常用的中间件还有redux-thunk,核心代码如下:

代码语言:javascript
复制
const thunk = ({ dispatch, getState }) => next => action => {
  if (typeof action === 'function') {
    return action(dispatch, getState)
  }

  return next(action)
}

redux-thunk的逻辑也很简单,通过对store解构获取dispatchgetState函数,如果action是函数则调用action,否则调用next(action)进行下一个中间件。在action函数中可以通过dispatch来触发action,哪怕是在异步的回调中,所以redux-thunk通常用来处理异步操作。

中间件使用

代码语言:javascript
复制
import { createStore, applyMiddleware } from 'redux'

function counterReducer(state = { value: 0 }, action) {
  switch (action.type) {
    case 'counter/incremented':
      return { value: state.value + 1 }
    case 'counter/decremented':
      return { value: state.value - 1 }
    default:
      return state
  }
}

// 带有中间件的store
let store = createStore(counterReducer, applyMiddleware(thunk, logger))

store.subscribe(() => console.log(store.getState()))

store.dispatch({ type: 'counter/incremented' })
// 打印:
// prev state: { value: 0 }
// { value: 1 }
// next state { value: 1 }

store.dispatch((dispatch, getState) => { // 异步操作
  setTimeout(() => {
    dispatch({ type: 'counter/incremented' })
  }, 1000)
})
// 1秒后打印:
// prev state: { value: 1 }
// { value: 2 }
// next state { value: 2 }

需要注意的是applyMiddleware是有顺序的,它会从做左到右依次执行,next后是从右到左执行。如果上述示例修改为applyMiddleware(logger, thunk)会发生什么事呢?那么在调用store.dispatch(() => {})的时候也会打印日志,里面的dispatch又会打印一次。

applyMiddleware实现

由上createStore函数可知,当传入中间件的时候会通过enhancer来生成store。enhancer实际上就是applyMiddleware(logger, thunk)的结果,它是一个两层函数,第一层接受的参数是createStore第二次接受的参数是reducerpreloadedState,代码大致如下:

代码语言:javascript
复制
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))
  )
}

function applyMiddleware(...middlewares) {
  return createStore => (reducer, preloadedState) => {
    const store = createStore(reducer, preloadedState)
    let 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: (action, ...args) => dispatch(action, ...args)
    }
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

首先通过createStore(reducer, preloadedState)不传中间件来创建storeapplyMiddleware内层函数的返回值只有dispatch是处理过的函数,其他的都是与store中的一致,也就是说中间件的作用实际上是强化dispatch函数。middlewareAPI实际上就是中间件的第一层函数的参数store,这里需要注意的是dispatch调用的时候,下面的代码已经走完了,所以里面的dispatch函数是加强后的dispatch而不是上面定义的抛出异常的函数。通过middlewares.map(middleware => middleware(middlewareAPI))去掉了中间件第一层函数。compose核心逻辑是funcs.reduce((a, b) => (...args) => a(b(...args)))对于函数数组返回嵌套执行的组合函数,compose(...chain)(store.dispatch)最后参数是store.dispatch,比如有两个中间件ab,这里相当于变成a(b(store.dispatch)),相当于a这一层的next函数是b(store.dispatch)函数。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-05-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 使用
  • createStore
  • 中间件
    • 中间件简绍
      • 中间件使用
        • applyMiddleware实现
        相关产品与服务
        消息队列 TDMQ
        消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档