一堆废话
继上一篇文章《为什么 redux 要返回一个新的 state 引发的血案》之后, 过了半个月我彭三汉又回来了, 直接看源码, GitHub 戳这里 https://github.com/reduxjs/redux/tree/master/src , 我们可以看到这样的文件架构
.
├── 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, 允许您读取状态, 调度操作和订阅更改.
*/
看完了上面的解释,(... 谷歌翻译好累啊), 我们继续往下看, 我们从 Redux 官网 https://redux.js.org/api/store#subscribe(listener) 中, 能看到, 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 我也没写出来, 我更推荐的是, 自己去看源码!!!
彩蛋嘛, 就是我们来揭开上边中提到的 actionTypes 和 isPlainObject 中的神秘面纱~
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
- }
未待完续
上一篇在这里 : 《为什么 redux 要返回一个新的 state 引发的血案》
写博客文章实在太累了, 所以还有下一集, 还有上边如果有错, 请大哥们指出, 说真, 我也是去 GitHub 看源码, 去官网看文档, 搜一些大哥们的文章, 还要打开谷歌翻译;
其实我写的, 也都是站在各位大哥的肩膀上去结合自己理解写的总结, 键盘侠就不要看了, 求求你们了, 你们太牛逼了, 直接去看源码吧, 传送门在底下~要不这也给你一个链接 https://github.com/reduxjs/redux/tree/master/src 吧
还有关于为什么只贴代码, 是因为, 直接看代码是最直观的!, 比如一个美女, 你要我告诉你, 她有多么的沉鱼落雁, 闭月羞花, 国色天香..., 还是你直接看比较直观?
相关链接
《Redux 源码》: https://github.com/reduxjs/redux/tree/master/src
《Redux 官网》: https://redux.js.org/api/store#store
《Object.getPrototypeOf() 》:
来源: https://juejin.im/post/5c2de8b1e51d454ad55f5ee2