博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
为什么 redux 要返回一个新的 state 引发的血案(二)
阅读量:6240 次
发布时间:2019-06-22

本文共 6859 字,大约阅读时间需要 22 分钟。

一堆废话

继上一篇文章之后,过了半个月我彭三汉又回来了,直接看源码,,我们可以看到这样的文件架构

·├── utils│   ├── actionTypes│   ├── isPlainObject│   ├── warning│   └─│├── applyMiddleware│├── bindActionCreatorts│├── combineReducers│├── compose│├── createStore│├── index.js│└─复制代码

看起来文件比较少,所以,开始跟我阿宽,不对,跟着各路大哥们的总结,一起看源码吧。

index.js

从小妈妈就告诉我,看源码要从 index.js 入手,就我看来,index都作为入口文件,所以我们去 index.js 中看一下代码。简单明了,我就不多说~

import createStore from './createStore'import combineReducers from './combineReducers'import bindActionCreators from './bindActionCreators'import applyMiddleware from './applyMiddleware'import compose from './compose'import warning from './utils/warning'import __DO_NOT_USE__ActionTypes from './utils/actionTypes'/* * 这是一个虚函数,用于检查函数名称是否已被缩小更改. * 如果这个函数被修改且 NODE_ENV !== 'production',警告用户 */function isCrushed() {}if (  process.env.NODE_ENV !== 'production' &&  typeof isCrushed.name === 'string' &&  isCrushed.name !== 'isCrushed') {  warning('巴拉巴拉,要看提示就去看源码哈')}export {  createStore,  combineReducers,  bindActionCreators,  applyMiddleware,  compose,  __DO_NOT_USE__ActionTypes}复制代码

createStore.js

我们回顾下之前说的 :redux 中最核心的 API 就是 —— createStore, 如何使用呢 ?

const store = createStore(reducers, preloadedState, enhance)复制代码

三个参数,reducers、preloadedState、enhance,源码中也有对这三个参数给出了解释

/** * 创建一个包含状态树的Redux存储 * 更改store中数据的唯一方法是在其上调用 `dispatch()` * * 你的app中应该只有一个store,指定状态树的不同部分如何响应操作 * 你可以使用 `combineReducers` 将几个reducer组合成一个reducer函数 * * @param {Function} reducer 给定当前状态树和要处理的操作的函数,返回下一个状态树 * * @param {any} [preloadedState] 初始状态. 你可以选择将其指定为中的universal apps服务器状态,或者还原以前序列化的用户会话。 * 如果你使用 `combineReducers` 来产生 root reducer 函数,那么它必须是一个与 `combineReducers` 键形状相同的对象 * * @param {Function} [enhancer] store enhancer. 你可以选择指定它来增强store的第三方功能 * 比如 middleware、time travel、persistence, Redux附带的唯一商店增强器是 `applyMiddleware()` * * @returns {Store} Redux Store,允许您读取状态,调度操作和订阅更改。 */复制代码

看完了上面的解释,(...谷歌翻译好累啊), 我们继续往下看,我们从中,能看到,Store Mehods 有四个方法,源码中也有看得到~

  • getState()
  • dispatch(action)
  • subscribe(listener)
  • replaceReducer(nextReducer)
import $$observable from 'symbol-observable'import ActionTypes from './utils/actionTypes'import isPlainObject from './utils/isPlainObject'export default function createStore(reducer, preloadedState, enhancer) {  // ...  // 检查一哈你的state和enhancer参数是否传反  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {    enhancer = preloadedState    preloadedState = undefined  }  // 如果有传入合法的enhance,则通过enhancer再调用一次createStore  if (typeof enhancer !== 'undefined') {    // enhancer 期望得到的是一个函数    if (typeof enhancer !== 'function') {      throw new Error('Expected the enhancer to be a function.')    }    return enhancer(createStore)(reducer, preloadedState)  }  // reducer 期望得到的是一个函数  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 // 是否正在分发事件  function ensureCanMutateNextListeners() {    if (nextListeners === currentListeners) {      nextListeners = currentListeners.slice()    }  }  // 一般在action middleware 中经常会使用 `getState()`方法去获取当前的state  function getState() {    if (isDispatching) {      throw new Error(       // ...      )    }    return currentState  }  // 源码中这个函数的解释太长了,我大概读懂了,但是就不写翻译了哈,大概如下  // 注册listener,同时返回一个取消事件注册的方法。当调用store.dispatch的时候调用listener  function subscribe(listener) {    if (typeof listener !== 'function') {      throw new Error(        '期待 listener 是一个函数'      )    }    if (isDispatching) {      throw new Error(        '在reducer执行时,您可能无法调用store.subscribe(),如果你希望在更新store后收到通知,        请从组件订阅并在回调中调用 store.getState() 以访问最新状态'      )    }    let isSubscribed = true    ensureCanMutateNextListeners()    nextListeners.push(listener)    return function unsubscribe() {      if(!isSubscribed) {        return      }      if (isDispatching) {        throw new Error(          '在reducer执行时,你可能无法取消订阅store侦听器'        )      }      isSubscribed = false      // 从 nextListeners 中去除掉当前 listener      ensureCanMutateNextListeners()      const index = nextListeners.indexOf(listener)      nextListeners.splice(index, 1)    }  }  // dispatch方法接收的action是个对象,而不是方法。dispatch()是触发状态变化的唯一方法。  // 这个对象实际上就是我们自定义action的返回值,因为dispatch的时候,已经调用过我们的自定义action了  // 更多详情去官网看讲解 : https://redux.js.org/api/store#example  function dispatch(action) {    if (!isPlainObject(action)) {  // isPlainObject 最上边 import 进来的      throw new Error(        'Actions 必须是一个普通对象'      )    }    if (typeof action.type === 'undefined') {      throw new Error(        'Actions 可能没有未定义的“类型”属性'      )    }    // 调用dispatch的时候只能一个个调用,通过dispatch判断调用的状态    if (isDispatching) {      throw new Error(       // ...      )    }    try {      isDispatching = true      currentState = currentReducer(currentState, action)    } finally {      isDispatching = false    }    const listeners = (currentListeners = nextListeners)    // 遍历调用每个 listener    for (let i = 0; i < listeners.length; i++) {      const listener = listeners[i]      listener()    }    return action  }  // Replaces the reducer currently used by the store to calculate the state.  function replaceReducer(nextReducer) {    if (typeof nextReducer !== 'function') {      throw new Error(       // ...      )    }    currentReducer = nextReducer    dispatch({      type: ActionTypes.REPLACE // ActionTypes 最上边 import 进来的    })  }  // ...  // 当create store的时候,reducer会接受一个type为ActionTypes.INIT的action,使reducer返回他们默认的state,这样可以快速的形成默认的state的结构  dispatch({    type: ActionTypes.INIT  })  return {    dispatch,    subscribe,    getState,    replaceReducer,    [$$observable]: observable  }}复制代码

来个小彩蛋

是不是很少,很多英文注释都去掉了,包括 throw new Error 我也没写出来,我更推荐的是,自己去看源码!!!

彩蛋嘛,就是我们来揭开上边中提到的 actionTypesisPlainObject 中的神秘面纱~

actionTypes.js

/** * 这些是Redux保留的私有操作类型 * 对于任何未知 actions,你必须返回当前状态 * 如果当前的 state 是 undefined,你必须返回初始state * 不要直接在代码中引用这些操作类型 */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复制代码

isPlainObject.js

/** * 如果参数是普通对象,则返回true * Object.getPrototypeOf() 方法返回指定对象的原型,如果没有继承属性,则返回 null */export default function isPlainObject(obj) {  if (typeof obj !== 'object' || obj === null) return false  let proto = obj  while (Object.getPrototypeOf(proto) !== null) {    proto = Object.getPrototypeOf(proto)  }  return Object.getPrototypeOf(obj) === proto}复制代码

未待完续

上一篇在这里 :

写博客文章实在太累了,所以还有下一集,还有上边如果有错,请大哥们指出,说真,我也是去 github 看源码,去官网看文档,搜一些大哥们的文章,还要打开谷歌翻译;

其实我写的,也都是站在各位大哥的肩膀上去结合自己理解写的总结,键盘侠就不要看了,求求你们了,你们太牛逼了,直接去看源码吧,传送门在底下~要不这也给你一个吧

还有关于为什么只贴代码,是因为,直接看代码是最直观的!,比如一个美女,你要我告诉你,她有多么的沉鱼落雁、闭月羞花、国色天香...,还是你直接看比较直观?

相关链接

  • 《Redux 源码》:

  • 《Redux 官网》:

  • 《Object.getPrototypeOf() 》:

转载地址:http://vadia.baihongyu.com/

你可能感兴趣的文章
什么是壳 - 脱壳篇01
查看>>
数据库基础
查看>>
python里面 循环明细对比 相同人员明细,生成同一订单里面
查看>>
linux top 命令的一些解释
查看>>
前端之HTML内容
查看>>
关于Datagridview控件用法的一些总结
查看>>
Mac 常用设置
查看>>
linux常用命令(22)gzip命令
查看>>
找出一个字符串中第一个只出现一次的字符
查看>>
CSS学习笔记(一)深入理解position属性和float属性
查看>>
xml入门
查看>>
python Flask框架mysql数据库配置
查看>>
[20150529]用户与用户组管理
查看>>
baidu__git_android
查看>>
ZC_源码编译真机烧写_20160424
查看>>
day26-UDP协议无粘包问题
查看>>
使用HTML5的十大原因
查看>>
转发:修饰符
查看>>
【转载】Linux下configure命令详细介绍
查看>>
图片中转站
查看>>