学习 redux 源码笔记:redux 是如何工作的?

最近学习了状态管理库 redux 的源码,简单记录一下学习笔记。

redux 简介

redux是 JavaScript 的状态管理库,提供可预测的状态管理。

redux有几个核心概念:

  • store:唯一存放状态的容器
  • state:状态
  • action:状态更新的动作
  • reduceraction对应的更新状态的动作函数
  • dispatch:通过action找到对应的reducer,更新state

redux的使用有三大原则:

  • 单一数据源

    整个应用的state存储在一个状态树中,整个状态树只存在于唯一的store里。

  • state只读

    状态是只读的,不要直接修改state。唯一改变state的方法就是调用dispatch触发action。直接修改state的话,并不会通知所有订阅者。

  • 纯函数执行修改

    reducer是纯函数,传入先前的stateaction,返回新的state

redux 简单使用

如下代码所示,用redux简单写了个计数的 demo,仅用到了createStore这个方法。

import { createStore } from 'redux';

// 初始状态
const initialState = 0;
// 创建reducer
const reducer = (preState = initialState, action) => {
  const { type } = action;
  switch (type) {
    case 'ADD':
      return preState + 1;
    case 'SUB':
      return preState - 1;
    default:
      return preState;
  }
};

// 创建store
const store = createStore(reducer);

// 订阅,当state改变,打印state
store.subscribe(() => console.log(store.getState()));

// 生成action的函数
const add = () => ({ type: 'ADD' });
const sub = () => ({ type: 'SUB' });

store.dispatch(add()); // 1
store.dispatch(add()); // 2
store.dispatch(add()); // 3
store.dispatch(add()); // 4

store.dispatch(sub()); // 3
store.dispatch(sub()); // 2

通过这个 demo,可以大致了解redux的工作流程:

  1. 首先要创建reducer函数,它是一个纯函数,接收之前的状态preStateaction,根据action作一些处理之后,返回新的state
  2. 使用createStore方法创建store,接收创建好的reducer。其实还可以传入其他参数,后面阅读源码会看到。
  3. store.subscribe订阅消息,参数是函数。只有订阅了,在dispatch时,才会通知每个监听者。
  4. 定义好不同的action,这里定义了两个生成action的函数,需要用时,调用这个函数即可。
  5. 最后就是调用store.dispatch,根据action更新state,通知监听者。

redux 源码笔记

整个redux源码的结构如下。

image-20220406202758343

utils文件夹主要是一些轻量工具函数,比较简单。index.js是入口文件,暴露了供开发者使用的方法。其他文件则是相应方法的实现。

下面根据不同文件,整理了自己对其的理解和笔记。

utils

(1)actionTypes.js

这些是redux的私有action,在代码中不要直接引用这些action

// 随机生成长度为6的字符串,并用.连接
const randomString = () => Math.random().toString(36).substring(7).split('').join('.');

const ActionTypes = {
  INIT: `@@redux/INIT${randomString()}`,
  REPLACE: `@@redux/REPLACE${randomString()}`,
  PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}`
};

export default ActionTypes;

randomString使用了Number.prototype.toString(),覆盖了Object上的toString()方法。转换基数为36,10 个数字和 26 个字母,表示随机生成随机字符串,包含数字和字母。截取 6 位,用.连接。

最后向外暴露了 3 个actionINITREPLACEPROBE_UNKNOWN_ACTION

(2)formatProdErrorMessage.js

此文件用来格式化生产环境的error。不要直接引用此模块。

function formatProdErrorMessage(code) {
  return (
    `Minified Redux error #${code}; visit https://redux.js.org/Errors?code=${code} for the full message or ` +
    'use the non-minified dev environment for full errors. '
  );
}

export default formatProdErrorMessage;

(3)isPlainObject.js

该方法用来判断一个对象是否是简单对象。

简单对象满足obj.__proto__ === Object.prototypenew Object字面量创建出的对象,是简单对象。

换句话说,此方法判断的是一个对象是否是Object的实例对象。

export default function isPlainObject(obj) {
  // 因为typeof null显示为'object',所以单独讨论
  if (typeof obj !== 'object' || obj === null) return false;

  let proto = obj;
  // 沿着__proto__向上遍历原型链
  while (Object.getPrototypeOf(proto) !== null) {
    proto = Object.getPrototypeOf(proto);
  }

  // 最后判断原始对象的__proto__是否等于原型链的最上端
  return Object.getPrototypeOf(obj) === proto;
}

(4)kindOf.js

该方法是一个判断数据类型的函数,比较简单。值得学习之处是,typeofconstructorinstanceof都可以在一定条件下判断数据类型。

function miniKindOf(val) {
  if (val === void 0) return 'undefined';
  if (val === null) return 'null';

  // 以下数据类型不用处理
  const type = typeof val;
  switch (type) {
    case 'boolean':
    case 'string':
    case 'number':
    case 'symbol':
    case 'function': {
      return type;
    }
    default:
      break;
  }

  // 单独判断数组、日期、错误对象
  if (Array.isArray(val)) return 'array';
  if (isDate(val)) return 'date';
  if (isError(val)) return 'error';

  // 通过val.constructor.name判断以下类型
  const constructorName = ctorName(val);
  switch (constructorName) {
    case 'Symbol':
    case 'Promise':
    case 'WeakMap':
    case 'WeakSet':
    case 'Map':
    case 'Set':
      return constructorName;
    default:
      break;
  }

  // other
  return type.slice(8, -1).toLowerCase().replace(/\s/g, '');
}

function ctorName(val) {
  return typeof val.constructor === 'function' ? val.constructor.name : null;
}

function isError(val) {
  return (
    val instanceof Error ||
    (typeof val.message === 'string' &&
      val.constructor &&
      typeof val.constructor.stackTraceLimit === 'number')
  );
}

function isDate(val) {
  if (val instanceof Date) return true;
  return (
    typeof val.toDateString === 'function' &&
    typeof val.getDate === 'function' &&
    typeof val.setDate === 'function'
  );
}

export function kindOf(val) {
  let typeOfVal = typeof val;

  // 开发环境下,进一步处理
  if (process.env.NODE_ENV !== 'production') {
    typeOfVal = miniKindOf(val);
  }

  return typeOfVal;
}

(5)warning.js

判断了下console是否存在,打印错误信息。

export default function warning(message) {
  // 检测console是否存在
  if (typeof console !== 'undefined' && typeof console.error === 'function') {
    console.error(message);
  }
  try {
    throw new Error(message);
  } catch (e) {}
}

index.js

该文件是redux的入口文件,向开发者暴露了几个方法。

  • createStore:创建store
  • combineReducers:将多个reducer合并成单个reducer
  • bindActionCreators:将action与对应的dispatch方法绑定,生成可以直接执行action的函数
  • applyMiddleware:增强redux功能
  • compose:组合函数,连接多个函数
function isCrushed() {}

// 若开发环境压缩了代码,提示warning
if (
  process.env.NODE_ENV !== 'production' &&
  typeof isCrushed.name === 'string' &&
  isCrushed.name !== 'isCrushed'
) {
  warning(
    'You are currently using minified code outside of NODE_ENV === "production". ' +
      'This means that you are running a slower development build of Redux. ' +
      'You can use loose-envify (https://github.com/zertosh/loose-envify) for browserify ' +
      'or setting mode to production in webpack (https://webpack.js.org/concepts/mode/) ' +
      'to ensure you have the correct code for your production build.'
  );
}

export {
  createStore,
  combineReducers,
  bindActionCreators,
  applyMiddleware,
  compose,
  // 不要使用自带的action
  __DO_NOT_USE__ActionTypes
};

这里定义了一个isCrushed的空函数,目的是判断用户是否压缩了代码,因为如果压缩了代码,函数isCrushed的函数名就会被替换,不再是isCrushed了。若在开发环境下压缩了代码,给出warning提示。

createStore.js

此方法是redux源码阅读的重点,也是使用redux的第一个步骤。

该方法接收三个参数:reducerpreloadedStateenhancerreducer是产生新state的函数,preloadedState代表初始状态,enhancer是增强redux用的函数。

向外暴露了一些方法,其中dispatchsubscribegetState是开发中常用的方法。

export default function createStore(reducer, preloadedState, enhancer) {
  // 判断是否传入多个enhancer,确保只传入一个enhancer
  // 若要使用多个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. See https://redux.js.org/tutorials/fundamentals/part-4-store#creating-a-store-with-enhancers for an example.'
    );
  }

  // 若第二个参数是函数,且没有传入第三个参数
  // 则第二个参数作为enhancer,preloadedState不传入
  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
    enhancer = preloadedState;
    preloadedState = undefined;
  }

  // 首先若传入了enhancer,则返回增强后的createStore执行的结果
  if (typeof enhancer !== 'undefined') {
    // 确保enhancer是函数
    if (typeof enhancer !== 'function') {
      throw new Error(
        `Expected the enhancer to be a function. Instead, received: '${kindOf(enhancer)}'`
      );
    }

    // 返回增强后的createStore执行的结果
    return enhancer(createStore)(reducer, preloadedState);
  }

  // 确保reducer是函数,若不是函数,显示其类型
  if (typeof reducer !== 'function') {
    throw new Error(
      `Expected the root reducer to be a function. Instead, received: '${kindOf(
        reducer
      )}'`
    );
  }

  // 当前的reducer
  let currentReducer = reducer;
  // 当前的state
  let currentState = preloadedState;
  // 当前订阅者列表
  let currentListeners = [];
  let nextListeners = currentListeners;
  // 锁,保证数据一致性
  let isDispatching = false;

  // 当nextListeners和currentListeners是同一个引用时,
  // 将currentListeners的拷贝赋值给nextListeners
  // 防止当前队列执行的时候,影响到自身
  function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
      nextListeners = currentListeners.slice();
    }
  }

  // 直接返回当前的state
  // 从这里可以得到若直接修改currentState,并不会通知订阅者
  function getState() {
    // 确保没有其他reducer操作
    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;
  }

  // 添加订阅者
  // 返回取消订阅的函数
  function subscribe(listener) {
    // 确保listener是个函数
    if (typeof listener !== 'function') {
      throw new Error(
        `Expected the listener to be a function. Instead, received: '${kindOf(listener)}'`
      );
    }

    // 确保数据唯一性
    if (isDispatching) {
      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/store#subscribelistener for more details.'
      );
    }

    // 该订阅者在订阅状态
    let isSubscribed = true;

    // 若nextListeners和currentListeners为同一个引用
    // 将currentListeners作一次浅拷贝,给nextListeners
    ensureCanMutateNextListeners();
    // 新的订阅者加入订阅者列表中
    nextListeners.push(listener);

    // 返回取消订阅的函数
    return function unsubscribe() {
      // 若已经取消订阅,不继续执行
      if (!isSubscribed) {
        return;
      }

      // 保证数据唯一性
      if (isDispatching) {
        throw new Error(
          'You may not unsubscribe from a store listener while the reducer is executing. ' +
            'See https://redux.js.org/api/store#subscribelistener for more details.'
        );
      }

      // 取消订阅
      isSubscribed = false;

      ensureCanMutateNextListeners();
      // 将该订阅者从订阅者列表中删除
      const index = nextListeners.indexOf(listener);
      nextListeners.splice(index, 1);
      currentListeners = null;
    };
  }

  // 分派action,这是触发state更新的唯一方法
  // action仅支持简单对象,若action是Promise、Observable等,需要使用中间件
  // action表明了做了什么改变,必须有type属性,并且非undefined,一个好的实践为type是字符串常量
  // 返回dispatch的action,若使用了中间件,可能返回其他
  function dispatch(action) {
    // 确保action是简单对象
    if (!isPlainObject(action)) {
      throw new Error(
        `Actions must be plain objects. Instead, the actual type was: '${kindOf(
          action
        )}'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions. See https://redux.js.org/tutorials/fundamentals/part-4-store#middleware and https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-the-redux-thunk-middleware for examples.`
      );
    }

    // 确保action.type存在
    if (typeof action.type === 'undefined') {
      throw new Error(
        'Actions may not have an undefined "type" property. You may have misspelled an action type string constant.'
      );
    }

    // 确保当前没有在执行其他的reducer操作
    if (isDispatching) {
      throw new Error('Reducers may not dispatch actions.');
    }

    try {
      // 加锁,防止后续的reducer操作
      isDispatching = true;
      // 调用当前的reducer,返回新的state,赋值给currentState
      currentState = currentReducer(currentState, action);
    } finally {
      // 无论是否有错误,都会执行的语句
      // 当前reducer执行完毕后,解锁
      isDispatching = false;
    }

    // 通知订阅者
    // currentListeners = nextListeners是为了下一次执行的时候,会重新生成一个新的拷贝
    const listeners = (currentListeners = nextListeners);
    for (let i = 0; i < listeners.length; i++) {
      // 执行订阅者的函数,不传入参数
      const listener = listeners[i];
      // 执行函数
      listener();
    }

    // 最后返回当前的action
    return action;
  }

  // 替换reducer
  // 使用场景:
  // 1. 代码分割,立即加载reducers的时候
  // 2. 实现redux热加载机制的时候
  function replaceReducer(nextReducer) {
    // 确保nextReducer是函数
    if (typeof nextReducer !== 'function') {
      throw new Error(
        `Expected the nextReducer to be a function. Instead, received: '${kindOf(
          nextReducer
        )}`
      );
    }

    // 替换reducer
    currentReducer = nextReducer;

    // 触发state更新
    dispatch({ type: ActionTypes.REPLACE });
  }

  // 一般用不到
  function observable() {
    // ...
  }

  // 初始化state,否则第一次的currentState为undefined
  dispatch({ type: ActionTypes.INIT });

  return {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [$$observable]: observable
  };
}

combineReducers.js

该方法的作用是合并多个reducers为单个reducer。输入参数是一个拥有多个reducer的对象,最后返回一个函数,处理所有的reducer

function getUnexpectedStateShapeWarningMessage(
  inputState,
  reducers,
  action,
  unexpectedKeyCache
) {
  const reducerKeys = Object.keys(reducers);
  const argumentName =
    action && action.type === ActionTypes.INIT
      ? 'preloadedState argument passed to createStore'
      : 'previous state received by the reducer';

  if (reducerKeys.length === 0) {
    return (
      'Store does not have a valid reducer. Make sure the argument passed ' +
      'to combineReducers is an object whose values are reducers.'
    );
  }

  if (!isPlainObject(inputState)) {
    return (
      `The ${argumentName} has unexpected type of "${kindOf(
        inputState
      )}". Expected argument to be an object with the following ` +
      `keys: "${reducerKeys.join('", "')}"`
    );
  }

  // 找出inputState里有的key,但reducers集合里没有的key
  const unexpectedKeys = Object.keys(inputState).filter(
    key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key]
  );

  unexpectedKeys.forEach(key => {
    unexpectedKeyCache[key] = true;
  });

  // 如果是替换reducer的action,则提前退出,不打印异常
  if (action && action.type === ActionTypes.REPLACE) return;

  if (unexpectedKeys.length > 0) {
    return (
      `Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` +
      `"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` +
      `Expected to find one of the known reducer keys instead: ` +
      `"${reducerKeys.join('", "')}". Unexpected keys will be ignored.`
    );
  }
}

// 用于规范reducers
function assertReducerShape(reducers) {
  Object.keys(reducers).forEach(key => {
    const reducer = reducers[key];
    const initialState = reducer(undefined, { type: ActionTypes.INIT });

    // 确保初始值不为undefined
    if (typeof initialState === 'undefined') {
      throw new Error(
        `The slice reducer for key "${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. If you don't want to set a value for this reducer, ` +
          `you can use null instead of undefined.`
      );
    }

    // 确保遇到未知的action,返回初始值,并且不为undefined
    // 确保没有占用redux的命名空间
    if (
      typeof reducer(undefined, {
        type: ActionTypes.PROBE_UNKNOWN_ACTION()
      }) === 'undefined'
    ) {
      throw new Error(
        `The slice reducer for key "${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, but can be null.`
      );
    }
  });
}

// 合并多个reducer为单个reducer
// 输入参数reducers是一个对象,值是需要合并的reducer
// 会调用所有的子reducer,聚合所有结果合并为一个object
// 返回合并后的单个reducer
export default function combineReducers(reducers) {
  // reducers对象的key数组
  const reducerKeys = Object.keys(reducers);
  // 最终要返回的reducer
  const finalReducers = {};

  // 遍历key数组,浅拷贝reducers
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i];

    // 若开发环境,且当前reducer函数名不再存在,给出warning
    if (process.env.NODE_ENV !== 'production') {
      if (typeof reducers[key] === 'undefined') {
        warning(`No reducer provided for key "${key}"`);
      }
    }

    // 当前reducer是函数,添加到finalReducers中
    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key];
    }
  }
  // 获取finalReducers的所有key
  const finalReducerKeys = Object.keys(finalReducers);

  // 确保不警告多次相同的key
  let unexpectedKeyCache;
  if (process.env.NODE_ENV !== 'production') {
    // 开发环境为{}
    unexpectedKeyCache = {};
  }

  let shapeAssertionError;
  try {
    // 确保所有reducers遇到未知的action,返回初始值,且不为undefined
    // 确保没有占用redux命名空间
    assertReducerShape(finalReducers);
  } catch (e) {
    shapeAssertionError = e;
  }

  // 返回最后的reducer
  return function combination(state = {}, action) {
    // 错误信息
    if (shapeAssertionError) {
      throw shapeAssertionError;
    }

    // 生产环境,找出state里面没有对应reducer的key,给出提示
    if (process.env.NODE_ENV !== 'production') {
      const warningMessage = getUnexpectedStateShapeWarningMessage(
        state,
        finalReducers,
        action,
        unexpectedKeyCache
      );
      if (warningMessage) {
        warning(warningMessage);
      }
    }

    // 表示state是否已被更改
    let hasChanged = false;
    // 改变后的state
    const nextState = {};
    for (let i = 0; i < finalReducerKeys.length; i++) {
      const key = finalReducerKeys[i];
      const reducer = finalReducers[key];
      // 当前key的state值
      const previousStateForKey = state[key];
      // 执行当前reducer,拿到state
      const nextStateForKey = reducer(previousStateForKey, action);
      // 对新的state做undefined检验
      if (typeof nextStateForKey === 'undefined') {
        const actionType = action && action.type;
        throw new Error(
          `When called with an action of type ${
            actionType ? `"${String(actionType)}"` : '(unknown type)'
          }, the slice reducer for key "${key}" returned undefined. ` +
            `To ignore an action, you must explicitly return the previous state. ` +
            `If you want this reducer to hold no value, you can return null instead of undefined.`
        );
      }
      // 新的state放在相应nextState中
      nextState[key] = nextStateForKey;
      // 判断新旧state是不是同一个引用,若state发生变化,则肯定已经改变
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
    }
    hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length;
    // 发生变化则返回nextState,否则返回state
    return hasChanged ? nextState : state;
  };
}

bindActionCreators.js

该方法的作用是将action与对应的dispatch方法绑定,生成可以直接执行action的函数。

输入参数actionCreators可以是创建action的函数对象,也可以是单个创建action的函数。dispatch即为store提供的dispatch函数。

返回一个与原对象类似的对象,只不过这个对象的value都是会直接dispatchaction creator产生的action。如果传入一个单独的函数作为actionCreators,那么返回的结果也是一个单独的函数。

// actionCreator是产生action的函数,执行之后,会得到一个action
// 得到的action再传递给dispatch
// bindActionCreator函数返回一个自动执行dispatch的方法
function bindActionCreator(actionCreator, dispatch) {
  // 闭包
  return function () {
    // 返回结果为dispatch这个actionCreator并传参
    return dispatch(actionCreator.apply(this, arguments));
  };
}

//  actionCreators:创造action的函数对象
export default function bindActionCreators(actionCreators, dispatch) {
  // 如果是一个函数,直接执行bindActionCreator并返回
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch);
  }

  // 错误处理
  if (typeof actionCreators !== 'object' || actionCreators === null) {
    throw new Error(
      `bindActionCreators expected an object or a function, but instead received: '${kindOf(
        actionCreators
      )}'. ` +
        `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
    );
  }

  const boundActionCreators = {};
  // 遍历每个函数
  for (const key in actionCreators) {
    // 拿到每个函数
    const actionCreator = actionCreators[key];
    if (typeof actionCreator === 'function') {
      // 将自动执行dispatch的方法放到boundActionCreators中
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);
    }
  }
  return boundActionCreators;
}

compose.js

该方法将多个函数连接起来,上一个函数的返回值作为下一个函数的参数输入。

注意,它的执行顺序为从右到左

// 将多个函数连接起来:上一个函数的返回值作为下一个参数的输入
// 最终得到最后的返回值
// 从右向左的顺序执行
export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg;
  }

  if (funcs.length === 1) {
    return funcs[0];
  }

  // 利用reduce方法执行每个中间件函数,并将上一个函数的返回作为下一个函数的参数
  // a:上一次调用回调的返回值,b:当前处理的元素
  // 所以是从右向左的顺序执行
  // compose(f, g, h)
  // (...args) => f(g(h(...args)))
  return funcs.reduce(
    (a, b) =>
      (...args) =>
        a(b(...args))
  );
}

applyMiddleware.js

该方法用来增强redux功能,主要执行过程为,在dispatch的时候,按照传入的中间件顺序,依次执行,最后返回一个增强后的store.dispatch方法。

这里用到了上面的compose方法。

一个注意点,这里首先定义了dispatch为一个抛出错误函数,目的是为了防止在中间件构造过程中调用dispatch。构造完成后,再将最终的dispatch赋值完成。

export default function applyMiddleware(...middlewares) {
  // 返回一个参数为createStore的函数
  return createStore =>
    (...args) => {
      // 创建store
      const store = createStore(...args);
      // 定义一个dispatch,如果在中间件构造过程中调用,则抛出错误
      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: (...args) => dispatch(...args)
      };
      // 依次调用middleware,存放在chain数组中
      const chain = middlewares.map(middleware => middleware(middlewareAPI));
      // 用compose整合chain数组,并赋值给dispatch
      dispatch = compose(...chain)(store.dispatch);

      // 返回增强的store
      return {
        ...store,
        dispatch
      };
    };
}

手写简易 redux

阅读完了redux的源码,来实现一个简易版的createStore方法,来实现「redux 简单使用」小节的功能。

实现createStore的功能,核心常用的方法就是subscribedispatchgetState这三个。

其实核心思路就是定义一个监听者列表,有新订阅时,将其回调函数放入监听者列表。dispatch中通过reducer更新旧状态,并通知所有订阅者。

function createStore(reducer) {
  // 状态
  let state;
  // 监听者列表
  const listeners = [];

  // 订阅
  function subscribe(listener) {
    listeners.push(listener); // 每订阅一个,就为监听器添加一个回调函数
  }

  // 更新 state
  function dispatch(action) {
    // 调用 reducer,更新 state
    state = reducer(state, action);
    // 通知所有订阅者
    listeners.forEach(i => i());
  }

  // 获取 state
  function getState() {
    return state;
  }

  // 返回 store 对象
  return {
    subscribe,
    dispatch,
    getState
  };
}

参考


📘📘欢迎在我的博客上访问:
https://lzxjack.top/

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

火星飞鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值