莱芜概镀电子有限公司_redux源码解读(一)

redux 的源码虽然代码量并不多(除去注释大概300行吧)。但是,因为函数式编程的思想在里面体现得淋漓尽致,理解起来并不太容易,所以准备使用三篇文章来分析。

  • 第一篇,主要研究 redux 的核心思想和实现,并用100多行的代码实现了其核心功能,相信看完之后,你会完全理解 redux的核心。这里甩掉 combindReducersapplyMiddleware,不会涉及很高深的柯里化、高阶、归并的思想,但是需要你对闭包有一定的理解。其实,redux 源码本身并不可怕,可怕的是网上太多文章把他和函数式放在一起来分析(装逼)了!!!吓得我们一看到就想跑了。
  • 第二篇, 理解了 redux 的核心之后, 我们会分析 reducers 合并(即 combindReducers)的实现。
  • 第三篇会分析增强器(即 applyMiddleware)的实现,这是最体现函数式风格的地方,并实现一个处理异步请求的 promise 中间件。

在解读 redux 源码之前,我们首先要弄清楚一个问题,就是 reduxreact-redux 不是同一个东东。 react-redux 是为 react 而定制的,主要是提供 Provider 组件和 connect 方法,方便于我们把 reduxreact组件 绑定起来。但是, redux 是没有限制说一定要跟 react 一起使用的。本文只介绍 redux ,不涉及 react 或者 react-redux 。因为我觉得,如果把 reduxreact 放在一起讨论,反而会加深了理解的复杂度,分散了我们的注意力,从而影响我们分析源码进度。现在要分析 redux 源码,那就只专注于 redux,甩开 react , 就连后面的测试例子,也不要引入 react,就简单的使用原生html和js测试一下就OK了。

什么是 redux 呢?, 这里也不展开介绍了。就简单的回顾一下 redux 的具体用法:

  1. 定义一个 reducer 函数
  2. 调用 redux.createStore(reducer) 方法创建 store 实例
  3. 通过store.subscribe(callback) 方法订阅回调事件(即状态变化时会触发回调函数callback)
  4. 通过用户交互(如点击事件)调用 store.dispatch(action), 改变 store 的状态

可能用些朋友会说,我从来没有用过 store.subscribe啊,那是因为你使用了 react-redux, 在 connet() 的时候帮你做了这一步。好吧,说好了不扯 react的。那下面我们就就一步步的来实现 redux 的核心功能吧。

首先来看一下 createStore, 我们平时的用法如下:

const store = createStore(reducer, preloadedState, enhance)

可以接受3个参数,第一个是自定义的reducer函数, 第二个是初始状态,第三个是增强器(即 applyMiddleware()返回的东西),因为前面已经说过了,这里我们不会涉及到 applyMiddleware,所以,我们的 createStore只接收2个参数,如下:

function createStore(reducer, preloadedState) {
  if (typeof reducer !== "function") {
    throw new Error("Expected the reducer to be a function.")
  }
  
  // 定义一些变量,后面几乎所有的方法都会用到,这就是闭包的力量!
  let currentState = preloadedState // state
  let listeners = [] // 订阅事件列表
  let isDispatching = false // 是否正在执行reducer
}

createStore 参数和可能会用到的变量定义好了,我们需要实现三个函数,分别是 store.getStatestore.subscribestore.dispatch

首先来实现 store.getState 方法,这个方法没有好说的,就是把闭包里面的 currentState 返回出去就行了,代码如下:

shou xian lai shi xian store. getState fang fa, zhe ge fang fa mei you hao shuo de, jiu shi ba bi bao li mian di currentState fan hui chu qu jiu xing le, dai ma ru xia:

function createStore(reducer, preloadedState) {
  // 省略和上面重复的代码
  
  // 获取state
  function getState() {
    // 如果正在执行reducer,则抛出异常
    if (isDispatching) {
      throw new Error("You may not call store.getState() while the reducer is executing. ")
    }
    return currentState;
  }
}

接着我们来实现 store.subscribe。这个方法是用来添加订阅回调函数的。首先要判断传进来的参数是不是函数类型,然后,把他它push到回调队列(数组)里面。因为可能后面需要把这个回调取消掉,所以还要返回一个方法给外部调用取消,实现代码如下:

function createStore(reducer, preloadedState) {
  // 省略和上面重复的代码
  
  // 添加订阅事件
  function subscribe(listener) {
    if(typeof listener !== "function") {
      throw new Error("Expected the listener to be a function.")
    }

    let isSubscribed = true;
    listeners.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. ");
      }

      isSubscribed = false;

      const index = listeners.indexOf(listener);
      listeners.splice(index, 1);
    }
  }
}

最后,我们再来看一下 store.dispatch 方法的实现。 dispatch 接受的参数类型是一个 action 。我们来回顾一下 action是什么鬼?他要求是一个原生对象,而且必须要有 type 属性,还有可能有 payload 属性。如下是我们的一个用法 :

store.dispatch({
  type: "ADD_SHOPPING",
  payload: 1
})

调用store.dispatch(action), 它的返回值也是 action。下面代码是 store.dispatch()的实现:

function createStore(reducer, preloadedState) {
  // 省略和上面重复的代码
  
  function dispatch(action) {
    // 如果action不是原生对象,则抛出异常
    // 因为我们期待的action结构为"{type: "xxx", payload: "xxx"}"的原生对象
    if(Object.prototype.toString.call(action, null) !== "[object Object]") {
      throw new Error("Actions must be plain objects. ");
    }

    if(typeof action.type === "undefined") {
      throw new Error("Actions may not have an undefined "type" property. ")
    }

    if(isDispatching) {
      throw new Error("Reducers may not dispatch actions.")
    }

    // 开始调用reducer获取新状态。因为可能会出错需要用try-catch
    // 并且不管成功失败,执行完毕后都要设置isDispatching=true
    try {
      isDispatching = true;
      currentState = reducer(currentState, action);
    } finally {
      isDispatching = false;
    }

    // 遍历所有通过store.subscribe()绑定的的订阅事件,并调用他们
    listeners.forEach((listener) => {
      listener();
    })

    return action;
  }
}

关于 redux 的分析就写到这里的了。下面是前面分析的代码整合到了一起。

function createStore(reducer, preloadedState) {
  if (typeof reducer !== "function") {
    throw new Error("Expected the reducer to be a function.")
  }

  let currentState = preloadedState // state
  let listeners = [] // 订阅事件列表
  let isDispatching = false // 是否正在执行reducer

  function getState() {
    // 如果正在执行reducer,则抛出异常
    if (isDispatching) {
      throw new Error("You may not call store.getState() while the reducer is executing. ")
    }
    return currentState;
  }

  // 添加订阅事件
  function subscribe(listener) {
    if(typeof listener !== "function") {
      throw new Error("Expected the listener to be a function.")
    }

    let isSubscribed = true;
    listeners.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. ");
      }

      isSubscribed = false;

      const index = listeners.indexOf(listener);
      listeners.splice(index, 1);
    }
  }

  function dispatch(action) {
    // 如果action不是原生对象,则抛出异常
    // 因为我们期待的action结构为"{type: "xxx", payload: "xxx"}"的原生对象
    if(!isPlainObject(action)) {
      throw new Error("Actions must be plain objects. ");
    }

    if(typeof action.type === "undefined") {
      throw new Error("Actions may not have an undefined "type" property. ")
    }

    if(isDispatching) {
      throw new Error("Reducers may not dispatch actions.")
    }

    // 开始调用reducer获取新状态。因为可能会出错需要用try-catch
    // 并且不管成功失败,执行完毕后都要设置isDispatching=true
    try {
      isDispatching = true;
      currentState = reducer(currentState, action);
    } finally {
      isDispatching = false;
    }

    // 遍历所有通过store.subscribe()绑定的的订阅事件,并调用他们
    listeners.forEach((listener) => {
      listener();
    })

    return action;
  }

  // 将getState, subscribe, dispatch这三个方法暴露出去
  // 创建了store实例之后,可以store.getState()、store.subscripbe()...
  return {
    getState,
    subscribe,
    dispatch
  }
}

完整的代码和测试例子,可以到我的github下载 点击进入simplest-redux 。如果觉得我分析得还不是太清楚的,建议把github上的代码clone下来,自己多看几遍,并在demo中运行调试几下就会明白的了。

当前文章:http://www.lutrover.com/pyyea/391213-1344027-85478.html

发布时间:09:46:52

今天开码结果??香港赛马会官方网站??118kj开奖日期??262222.com??33144b.com??www.508877.com??www.379666.com??天马论坛??赛马会??www.03633.com??