# 全面剖析 Redux 源码

## 编程模式

e.g.有一个公式，求连续自然数的平方和：

``````s = 1² + 2² + 3² + 4² + ... + N²

### 命令式编程

``````function squares(arr){
var i, sum = 0, squares = []
for(i = 0; i < arr.length; i++){
squares.push(arr[i] * arr[i])
}
for(i = 0; i < squares.length; i++){
sum += squares[i]
}
return sum
}
console.log(squares([1, 2, 3, 4, 5])) //55

### 函数式编程

``````function squares(arr){
return arr.map(d=>Math.pow(d,2))
.reduce((p,n)=>p+n,0)
}
console.log(squares([1,2,3,4,5])) //55

1.取出每个数字计算平方(map,Math.pow)

2.累加(reduce)

### 差异

• 函数式编程关心数据是如何被处理的,类似于自动流水线。

• 而命令式编程关心的是怎么去做？就像是手工，先这样做，再这样做，然后再这样，如果这样，就这样做 ...

• 逻辑式编程是通过一定的规则和数据，推导出结论，类似于asset，使用极少

## 从入口开始

``````// src/redux/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'

/*
* This is a dummy function to check if the function name has been altered by minification.
* If the function has been minified and NODE_ENV !== 'production', warn the user.
*/
function isCrushed() {}

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,
__DO_NOT_USE__ActionTypes
}

• 非生产环境。
• 函数有name , IE 不支持 Function.name。所以先要用 `typeof` 判断下
• 但是名称已经被改变 `isCrushed`。 压缩后`isCrushed.name !== 'isCrushed'`;

``````// src/redux/utils/actionTypes.js
// 生成随机数，大概输出sqrt(36*(7-1)) = 46656次后看到重复，一般程序事件触发不到这个次数
const randomString = () =>
Math.random()
.toString(36)
.substring(7)
.split('')
.join('.')

const ActionTypes = {
INIT: `@@redux/INIT\${randomString()}`, //用来redux内部发送一个默认的dispatch, initialState
REPLACE: `@@redux/REPLACE\${randomString()}`, // store.replaceReducers替换当前reducer触发的内部Actions
PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION\${randomString()}`
}

`PROBE_UNKNOWN_ACTION` 则是redux内部随机检测`combineReducers`合并所有`reducer`默认情况下触发任何Action判断是否返回了相同的数据。

## createStore

createStore(reducer:any,preloadedState?:any,enhancer?:middleware),最终返回一个 `state tree` 实例。可以进行`getState``subscribe` 监听和 `dispatch` 派发。

`createStore` 接收3个参数

• reducer: Function。给定当前`state tree`和要执行的`action`,返回下一个`state tree`
• preloadedState?: any，`initial state tree`
• enhancer?:middle, 增强器，若干个中间件可以通过 `applymiddleware` 产生一个增强器`enhancer`，多个增强器可以通过 `compose` 函数合并成一个增强器。
``````// src/redux/createStore.js
export default function createStore(reducer, preloadedState, enhancer) {
if (
(typeof preloadedState === 'function' && typeof enhancer === 'function') ||
(typeof enhancer === 'function' && typeof arguments[3] === 'function')
) {
// 检测是否传入了多个compose函数，抛出错误，提示强制组合成一个enhancer
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.'
)
}
// 直接传enhancer的情况
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
}

if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
// 校验enhancer
throw new Error('Expected the enhancer to be a function.')
}
// 返回创建增强后的store
}

if (typeof reducer !== 'function') {
// 校验reducer
throw new Error('Expected the reducer to be a function.')
}

//60 --------------
}

60行暂停一会，`return enhancer(createStore)(reducer, preloadedState)` 。如果传入了 `enhancer` 增强器的状态

``````// src/store/index.js
const logger = store => next => action => {
console.log('logger before', store.getState())
const returnValue = next(action)
console.log('logger after', store.getState())
return returnValue
}
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const logEnhancer = applyMiddleware(logger);// 应用中间件生成的增强器
const store = createStore(
ShopState,
composeEnhancer(logEnhancer) // compose可以将多个增强器合并成一个增强器Plus
)
return store;
}

``````// enhancer = composeEnhancer(applyMiddleware(logger)))
||
\||/
\/

``````export default 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)))
}

``````funcs.reduce((a, b) => (...args) => a(b(...args)))

``````const a = str => str + 'a'
const b = str => str + 'b'
const c = str => str + 'c'

const compose = (...funcs) => {
return funcs.reduce((a,b)=>(...args)=>a(b(...args)))
}
compose(a,b,c)('开始迭代了') // 开始迭代了cba

compose的入参现在只有一个，直接返回自身，可以被忽略，我们可以试试传入多个 `enhancer`

``````const enhancer = applyMiddleware(logger)
compose(enhancer,enhancer,enhancer) // 前后将会打印6次logger

applyMiddleware源代码

``````// src/redux/applyMiddleware.js

import compose from './compose'

export default function applyMiddleware(...middlewares) {
// 接受若干个中间件参数
// 返回一个enhancer增强器函数，enhancer的参数是一个createStore函数。等待被enhancer(createStore)
return createStore => (...args) => {
// 先创建store，或者说，创建已经被前者增强过的store
const store = createStore(...args)
// 如果还没有改造完成，就先被调用直接抛出错误
let dispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
)
}
// 暂存改造前的store
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
// 遍历中间件 call(oldStore)，改造store，得到改造后的store数组
const chain = middlewares.map(middleware => middleware(middlewareAPI))
// 组合中间件，将改造前的dispatch传入，每个中间件都将得到一个改造/增强过后的dispatch。
dispatch = compose(...chain)(store.dispatch)

// 最终返回一个加强后的createStore()函数
return {
...store,
dispatch
}
}
}

``````function middleware(store) {
// 监听路由的时候 dispatch(action)，由于当前还未改造完，会抛错
history.listen(location => { store.dispatch(updateLocation(location)) })

return next => action => {
if (action.type !== TRANSITION) {
return next(action)
}
const { method, arg } = action
history[method](arg)
}
}

``````// src/store/index.js
const logger = store => next => action => { // 打印日志
console.log('logger before', store.getState())
const returnValue = next(action)
console.log('logger after', store.getState())
return returnValue
}

const handlerPrice = store => next => action => { // 给每次新增的商品价格补小数位
console.log('action: ', action);
action = {
...action,
data:{
...action.data,
shopPrice:action.data.shopPrice + '.00'
}
}
const returnValue = next(action)
return returnValue
}

export default function configStore(){
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
ShopState,
undefined,
composeEnhancer(applyMiddleware(logger,handlerPrice))) ------ enhancer
return store;
}

`enhancer` 最终会返回一个增强函数，我们再看一遍`applyMiddleware`的源码，得出`applyMiddleware(logger,handlerPrice)` 执行后将会得到一个增强器。

``````const logger = store => next => action => { console.log(store); next(action) }
const handlerPrice = store => next => action => { console.log(store); next(action) }
middlewares = [logger, handlerPrice]
enhancer = (createStore) => (reducer, preloadedState, enhancer) => {
// 初始化store
var store = createStore(reducer, preloadedState, enhancer)
// 保存初始化的dispatch指针
var dispatch = store.dispatch
var chain = []
// 暂存改造前的store
var middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
// 将store传入，等待 logger(store) 返回的 next => action => next(action)
// 通过闭包，每个中间件得到的都是同一个store即middlewareAPI。这样就保证了数据的迭代变化
chain = [logger, handlerPrice].map(middleware => middleware(middlewareAPI))
/* 每次middleware(middlewareAPI) 应用中间件，都相当于 logger(store)一次，store也随之改变，返回两个next形参函数
* [next => action => { console.log(store); next(action) },// logger
*  next => action => { console.log(store); next(action) }] // handlerPrice
* 随之两个中间件等待被compose, 每个都可以单独访问next/dispatch前后的store
*/
dispatch = compose(...chain)(store.dispatch)
// 先将所有的中间件compose合并，然后将store.dispatch作为next形数传入，得到每个action => store.dispatch(action)
// 也就行上文的 next(action) === store.dispatch(action)
// 最终抛出一个compose后的增强dispatch与store
// 返回改造后的store
return {
...store,
dispatch
}
}

``````1.const enhancedCreateStore = enhancer(createStore) //----增强的createStore函数

``````enhancerStore = (reducer, preloadedState, enhancer) =>{
// ... 省略若干代码
return {
...store,
dispatch
}
}

## 中间件

Ps：`Redux``middleware``koa` 流程机制不完全一样。具体的区别可以参考 Perkin 的 Redux,Koa,Express之middleware机制对比，本段 `koa` 内容已隐藏，同学们可选择性去了解。

``````const logger = store =>{
return next => action => {
console.log(1)
next(action)
console.log(2)
}
}

const handlerPrice = store => next => action => {
console.log(3)
// 禁止直接调用原生store.dispatch,在知道副作用的情况下加条件执行，否则程序将崩溃
// 如果你想派发其他的任务，可以使用next()，此时next等价于dispatch
store.dispatch({type: 'anything' })
next(action)
console.log(4)
}

const enhancer = applyMiddleware(logger, handlerPrice)
const store = createStore(
ShopState,
null,
composeEnhancer(enhancer,handlerPrice))
// 结果无限循环的1和3
1
3
1
3
...

``````// src/redux/applyMiddleware.js
let dispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
)
}
// 暂存改造前的store
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args) //--- 保存了dispatch的引用
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch) // --- dispatch被改变

`dispatch` 最后的引用就是 `compose(...chain)(store.dispatch)` ，换句话说 `store.dispatch` 就是一次 middleWare Loop ...

``````const Koa = require('koa');
const app = new Koa();

app.use(async (ctx, next)=>{
console.log(1)
await next();
console.log(2)
});

app.use(async (ctx, next) => {
console.log(3)
await next();
console.log(4)
})

app.use(async (ctx, next) => {
console.log(5)
})

app.listen(3000);

``````1
3
5
4
2

``````        +----------------------------------------------------------------------------------+
|                                                                                  |
|                                 middleware 1                                     |
|                                                                                  |
|          +--------------------------next()---------------------------+           |
|          |                                                           |           |
|          |                      middleware 2                         |           |
|          |                                                           |           |
|          |            +-------------next()--------------+            |           |
|          |            |         middleware 3            |            |           |
| action   |  action    |                                 |    action  |   action  |
| 001      |  002       |                                 |    005     |   006     |
|          |            |   action              action    |            |           |
|          |            |   003      next()     004       |            |           |
|          |            |                                 |            |           |
+---------------------------------------------------------------------------------------------------->
|          |            |                                 |            |           |
|          |            |                                 |            |           |
|          |            +---------------------------------+            |           |
|          +-----------------------------------------------------------+           |
+----------------------------------------------------------------------------------+

## dispatch的妙用

``````let currentReducer = reducer // 当前reducer对象
let currentState = preloadedState  // 当前state对象
let currentListeners = [] // 当前的listeners订阅者集合, 使用subscribe进行订阅
let nextListeners = currentListeners // currentListeners 备份
let isDispatching = false // dispatch状态

function ensureCanMutateNextListeners() {
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()
}
}
//  @returns {any} ，获取state唯一方法，如果当前正在dispatch，就抛出一个错误，告诉
function getState() {
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
}

``````// src/App.js
const addShop = async () => {
dispatch({
data:{
...newShop,
fn:()=> getState() // -----添加函数准备在dispatch的期间去执行它
}
})
}

// src/store/index
//...other
newState = {
...newState,
shopList:newState.shopList.concat(action.data)
}
action.data.fn() //----- 在这里执行

``````  function dispatch(action) {
// dispatch只接受一个普通对象
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
}
// action type为有效参数
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
)
}
// 如果当前正在dispatch，抛出警告，可能不会被派发出去，因为store还没有被change完成
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}

try {
isDispatching = true
// INIT 和 dispatch 都会触发这一步
// 将当前的 reducer 和 state 以及 action 执行以达到更新State的目的
currentState = currentReducer(currentState, action)
} finally {
// 无论结果如何，先结束dispatching状态，防止阻塞下个任务
isDispatching = false
}
// 更新订阅者，通知遍历更新核心数据
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener() // 将下文的subscribe收集的订阅者通知更新
}

return action // 将 action 返回，在react-redux中要用到
}
// ... other 省略100行
dispatch({ type: ActionTypes.INIT }) //INIT store 会触发dispatch

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

`````` export default function isPlainObject(obj) {
if (typeof obj !== 'object' || obj === null) return false

let proto = obj
// 递归对象的原型  终点是否为null
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto)
}

return Object.getPrototypeOf(obj) === proto
}

``````var obj = {}
Object.getPrototypeOf(obj) === Object.prototype // true
Object.getPrototypeOf(Object.prototype) === null // true

Object.prototype.toString.call({}) // [object Object]
// but
Object.prototype.toString.call(new function Person(){}) // [object Object]

``````import expect from 'expect'
import isPlainObject from '../../src/utils/isPlainObject'
import vm from 'vm'

describe('isPlainObject', () => {
it('returns true only if plain object', () => {
function Test() {
this.prop = 1
}
const sandbox = { fromAnotherRealm: false }
// vm.runInNewContext (沙箱) 可以在Node环境中创建新的上下文环境运行一段 js
vm.runInNewContext('fromAnotherRealm = {}', sandbox)

expect(isPlainObject(sandbox.fromAnotherRealm)).toBe(true)
expect(isPlainObject(new Test())).toBe(false) // ---
expect(isPlainObject(new Date())).toBe(false)
expect(isPlainObject([1, 2, 3])).toBe(false)
expect(isPlainObject(null)).toBe(false)
expect(isPlainObject()).toBe(false)
expect(isPlainObject({ x: 1, y: 2 })).toBe(true)
})
})

## subscribe 添加订阅者

### 订阅(subscribe)者模式

redux中就是使用 `subscribe` (译文订阅) , 打个比方，A告诉B，说你每次吃完饭就通知我一声，我去洗碗，被动去请求得到对方的同意，这是订阅者。B收集订阅者的时候可以去做筛选是否通知A。

### 监听(listen)者

A不去得到B的同意，每次B吃完饭自动去洗碗，B不管他。最典型的莫过于`window``addEventListener`。B无法拒绝,只能通过A主动解绑。

### 代码分析

``````function subscribe(listener) {
// 校验订阅函数
if (typeof listener !== 'function') {
throw new Error('Expected the listener to be a function.')
}
// 如果当前派发的时候添加订阅者，抛出一个错误，因为可能已经有部分action已经dispatch掉。不能保证通知到该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-reference/store#subscribe(listener) for more details.'
)
}

// ...other
}

``````// App.js

const addShop = () => {
dispatch({
data:{
...newShop, // ...商品数据
fn:() => subscribe(() => { // -----添加函数准备在dispatch的期间去执行它
console.log('我现在要再添加监听者') // Error
})
}
})
}

``````// src/store/reducer.js
export default (state = ShopState, action)=>{
let newState = {...state}
switch(action.type){
newState = {
...newState,
shopList:newState.shopList.concat(action.data)
}
action.data.fn() //---- 执行,报错
break
default:
break
}
return newState
}

``````function subscribe(listen){
// ...other

let isSubscribed = true // 订阅标记

ensureCanMutateNextListeners() // nextListener先拷贝currentListeners保存一次快照
nextListeners.push(listener) // 收集此次订阅者，将在下次 dispatch 后更新该listener

return function unsubscribe() {
if (!isSubscribed) { // 多次解绑，已经解绑就没有必要再往下走了
return
}
// 同样，在dispatch的时候，禁止 unsubscribed 当前listener
if (isDispatching) {
throw new Error(
'You may not unsubscribe from a store listener while the reducer is executing. ' +
'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
)
}

isSubscribed = false // 标记为已经 unSubscribed
// 每次unsubscribe都要深拷贝一次 currentListeners 好让nextListener拿到最新的 [listener] ，
ensureCanMutateNextListeners() // 再次保存一份快照，
// 再对 nextListeners(也就是下次dispatch) 取消订阅当前listener。
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
currentListeners = null // 防止污染 `ensureCanMutateNextListeners` 保存快照，使本次处理掉的listener被重用
}
}

function ensureCanMutateNextListeners() {
// 在 subscribe 和 unsubscribe 的时候，都会执行
if (nextListeners === currentListeners) {
nextListeners = currentListeners.slice()  // 只有相同情况才保存快照
}
}

``````// 所以在dispatch的时候，需要明确将要发布哪些listener
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}

`currentListeners` 为当前的 `listener`, `nextListeners` 为下次 `dispatch` 后才发布的订阅者集合

``````const cancelSub = subscribe(()=>{
if(getState().shopList.length>10) cancelSub() // 商品数量超过10个的时候，放弃订阅更新
})

• 我们先通过`ensureCanMutateNextListeners`更新现有 `currentListener``nextListener`(下回合的`[listeners]`)，
• `subscribe` 订阅的事件收集到`nextListeners`,不影响当前 `CurrentListener` 的发布更新，
• 我们得到一个`cancelSub:unsubscribe` 闭包函数，该函数可以取消订阅
• 前10次正常发布更新，
• 在第11次执行的时候，商品数量增加到了11个
• 逻辑命中 `cancelSub:unsubscribe` 函数被调用，`isSubscribed`被标记为0，表示当前事件已经被`unSubscribed`
• 再次保存一份快照，`nextListener` 为下次 `dispatch` 后的`[listeners]`
• `nextListener` 上将当前 `listener` 移除。
• 置空 currentListeners ，清除缓存，防止污染 `ensureCanMutateNextListeners` 保存快照，使本次处理的listener被重用

## replaceReducer 动态注入

### 源码

``````// 计算reducer，动态注入
function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') {
throw new Error('Expected the nextReducer to be a function.')
}

currentReducer = nextReducer

// This action has a similiar effect to ActionTypes.INIT.
// Any reducers that existed in both the new and old rootReducer
// will receive the previous state. This effectively populates
// the new state tree with any relevant data from the old one.
dispatch({ type: ActionTypes.REPLACE })
}

``````// src/store/reducers.js
import { combineReducers } from 'redux'
import { connectRouter } from 'connect-react-router'
import userReducer from '@/store/user/reducer'
const rootReducer = history => combineReducers({
...userReducer,
router:connectRouter(history)
})

export default rootReducer

// src/store/index.js
import RootReducer from './reducers'
if(module.hot){
module.hot.accpet('./reducers',()=>{ // 热替换 reducers.js
const hotRoot = RootReducer(history) // require语法引入则需要加.default
store.replaceReducer(hotRoot)
})
}
return store
}

### 实现reducer按需加载

• Home、Index等同名文件为新增的 `store``views` 关联
• `injectAsyncReducer`封装动态替换方法，供 `PrivateRoute` 调用，
• `reducers.js` CombineReducers
• `ProviteRoute` Code Spliting 与 执行生成 `AsyncReducers` 替换动作
``````// src/store/reducer.js 合并Reducers
import { combineReducers } from 'redux';
import publicState from 'store/Public';

export default function createReducer(asyncReducers) {
return combineReducers({
public: publicState,
...asyncReducers // 异步Reducer
});
}

``````// src/store/index.js
import { createStore } from '../redux/index.js';
import createReducer from './reducers';

export default function configStore(initialState) {
const store = createStore(createReducer(),initialState);
store.asyncReducers = {}; //  隔离防止对store其他属性的修改
// 动态替换方法
function injectAsyncReducer(store, name, asyncReducer) {
store.asyncReducers[name] = asyncReducer;
store.replaceReducer(createReducer(store.asyncReducers));
}

return {
store,
injectAsyncReducer
};
}

``````// src/router/PrivateRoute.js
import React, { lazy, Suspense } from 'react';
import { Route, Switch } from 'react-router-dom';

const PrivateRoute = (props) => {
const { injectAsyncReducer, store } = props;

const withReducer = async (name) => {
// 规定views和store关联文件首字母大写
const componentDirName = name.replace(/^\S/, s => s.toUpperCase());
const reducer = await import(`../store/\${componentDirName}/index`);// 引入reducer
injectAsyncReducer(store, name, reducer.default);// 替换操作
return import(`../views/\${componentDirName}`); // 返回组件
};
return (
<Switch>
<Route {...props} exact path='/' name='main' component={lazy(() => withReducer('main'))} />
<Route {...props} exact path='/home' name='home' component={lazy(() => withReducer('home'))}/>
<Route {...props} exact path='/user' name='user' component={lazy(() => withReducer('user'))}/>
<Route {...props} exact path='/shopList' name='shopList' component={lazy(() => withReducer('shopList'))}/>
</Switch>
</Suspense>
);
};
export default PrivateRoute;

## observable

``````function observable() {
const outerSubscribe = subscribe;
return {
subscribe(observer) {
if (typeof observer !== 'object' || observer === null) {
throw new TypeError('Expected the observer to be an object.');
}

function observeState() {
if (observer.next) {
observer.next(getState()); // 将数据同步返回
}
}

observeState();
const unsubscribe = outerSubscribe(observeState);
return { unsubscribe }; // 解绑事件
},

[?observable]() { // 通过symbol-observable创建全局唯一的观察者
return this;
}
};
}

observable原本在ReactiveX中，一个观察者(Observer)订阅一个可观察对象(Observable)。观察者对Observable发射的数据或数据序列作出响应。这种模式可以极大地简化并发操作，因为它创建了一个处于待命状态的观察者哨兵，在未来某个时刻响应Observable的通知，不需要阻塞等待Observable发射数据。

## combineReducers

`combineReducers`用来将若干个reducer合并成一个reducers，使用方式：

``````combineReducers({
key:(state = {}, action)=>{
return state
},
post:(state = {}, action)=>{
return state
}
})

176行源码码大半部分全都是用来校验数据、抛错。

``````function getUndefinedStateErrorMessage(key, action) {
// 如果任意一个 reducer 返回的state undefined 会踩到这个雷
const actionType = action && action.type;
const actionDescription =
(actionType && `action "\${String(actionType)}"`) || 'an action';
// 即使没有值应该返回null，而不要返回undefined
return (
`Given \${actionDescription}, reducer "\${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.`
);
}
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) { // 合并成空的reducers也会报错
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)) { // state必须是个普通对象
return (
`The \${argumentName} has unexpected type of "` +
{}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +
`". Expected argument to be an object with the following ` +
`keys: "\${reducerKeys.join('", "')}"`
);
}
// 过滤 state 与 finalReducers(也就是combineReducer定义时的有效 reducers)，
// 拿到 state 多余的key值,比如 combineReducer 合并2个，但最后返回了3个对象
const unexpectedKeys = Object.keys(inputState).filter(
key => !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key]
);
// 标记警告这个值
unexpectedKeys.forEach(key => {
unexpectedKeyCache[key] = true;
});

// 辨别来源，replaceReducers表示设置此次替代Reducer，可以被忽略
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.`
);
}
}

``````function assertReducerShape(reducers) {
Object.keys(reducers).forEach(key => {
// 遍历 reducer
const reducer = reducers[key];
// 初始化该 reducer，得到一个state值
const initialState = reducer(undefined, { type: ActionTypes.INIT });
// 所以一般reducer写法都是 export default (state={},action)=>{ return state}

// 如果针对INIT有返回值，其他状态没有仍然是个隐患
// 再次传入一个随机的 action ，二次校验。判断是否为 undefined
const unknown = reducer(undefined, { type: ActionTypes.PROBE_UNKNOWN_ACTION() });

// 初始化状态下 state 为 undefined => 踩雷
if (typeof initialState === 'undefined') {
throw new Error(
`Reducer "\${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.`
);
}
// 随机状态下 为 undefined  => 踩雷
if (typeof unknown === 'undefined') {
throw new Error(
`Reducer "\${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.`
);
}
});
}

``````export default function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers);
const finalReducers = {};// 收集有效的reducer
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i];

if (process.env.NODE_ENV !== 'production') {
if (typeof reducers[key] === 'undefined') {
// 这个reducerKey 的 reducer是 undefined
warning(`No reducer provided for key "\${key}"`);
}
}

if (typeof reducers[key] === 'function') {
// reducer必须是函数，无效的数据不会被合并进来
finalReducers[key] = reducers[key];
}
}
// 所有可用reducer
const finalReducerKeys = Object.keys(finalReducers);

// This is used to make sure we don't warn about the same
// keys multiple times.
let unexpectedKeyCache; // 配合getUnexpectedStateShapeWarningMessage辅助函数过滤掉多出来的值
if (process.env.NODE_ENV !== 'production') {
unexpectedKeyCache = {};
}

let shapeAssertionError;
try {
assertReducerShape(finalReducers);//校验reducers是否都是有效数据
} catch (e) {
shapeAssertionError = e; // 任何雷都接着
}
// 返回一个合并后的 reducers 函数，与普通的 reducer 一样
return function combination(state = {}, action) {
if (shapeAssertionError) {
throw shapeAssertionError;
}

if (process.env.NODE_ENV !== 'production') {
// 开发环境下校验有哪些值是多出来的
const warningMessage = getUnexpectedStateShapeWarningMessage(
state,
finalReducers,
action,
unexpectedKeyCache
);
if (warningMessage) {
warning(warningMessage);
}
}

let hasChanged = false; // mark值是否被改变
const nextState = {};
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]; // reducerKey
const reducer = finalReducers[key]; // 对应的 reducer
const previousStateForKey = state[key]; // 改变之前的 state
// 对每个reducer 做 dispatch，拿到 state 返回值
const nextStateForKey = reducer(previousStateForKey, action);
if (typeof nextStateForKey === 'undefined') { // 如果state是undefined就准备搞事情
const errorMessage = getUndefinedStateErrorMessage(key, action);
throw new Error(errorMessage);
}
nextState[key] = nextStateForKey; // 收录这个reducer
// 检测是否被改变过
hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
}
// 如果没有值被改变，就返回原先的值，避免性能损耗
return hasChanged ? nextState : state;
};
}

## bindActionCreators

`bindActionCreators` 由父组件申明，传递给子组件直接使用，让子组件感受不到redux的存在，当成普通方法调用。

``````// 以import * 传入的
import * as TodoActionCreators from './ActionCreators'
const todoAction = bindActionCreators(TodoActionCreators, dispatch) //绑定TodoActionCreators上所有的action

// 普通状态
import { addTodoItem, removeTodoItem } from './ActionCreators'
const todoAction = bindActionCreators({ addTodoItem, removeTodoItem }, dispatch)

// 调用方法
todoAction.removeTodoItem(args)

``````function bindActionCreator(actionCreator, dispatch) {
// 用apply将action进行this显示绑定
return function() {
return dispatch(actionCreator.apply(this, arguments));
};
}
export default function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
// 如果是函数直接绑定this
return bindActionCreator(actionCreators, dispatch);
}

if (typeof actionCreators !== 'object' || actionCreators === null) { // 校验 action
throw new Error(
actionCreators === null ? 'null' : typeof actionCreators
}. ` +
`Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`
);
}

const boundActionCreators = {};
// 如果是以import * as actions 方式引入的
for (const key in actionCreators) {
const actionCreator = actionCreators[key];
if (typeof actionCreator === 'function') {
// 就遍历成一个普通对象，其action继续处理this显示绑定
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);
}
}
return boundActionCreators; // 将绑定后的actions返回
}

## 怎么管理大量的reducer、action、constants？

``````import bindActionCreators from '../redux/bindActionCreators';
import * as shop from 'store/ShopList/actionCreators'; // 所有的action

function mapDispatchToProps(dispatch) {
return bindActionCreators(shop, dispatch);
}

`shop>store` 内的负责所有 `reducer``action` 的创建。每个文件单独负责一块内容业务。

``````import {
} from '../constants';

});
});
});
});

export function reducer (state = { }, action) {
let newState = { ...state };
switch (action.type) {
newState = {
...newState,
};
// begin doSomething
break;
// successful doSomething
break;
// failed doSomething
break;
// whether doSomething
break;
default:
break;
}
return newState;
}

### actions

`shop` 模块子业务的 `actions.js` 则负责整合所有的 `action` 导出。

``````export { addShopBegin, addShopSuccess, addShopFail, addShopFinally } from './store/add';
export { deleteShopBegin, deleteShopSuccess, deleteShopFail, deleteShopFinally } from './store/delete';
export { changeShopBegin, changeShopSuccess, changeShopFail, changeShopFinally } from './store/change';
export { searchShopBegin, searchShopSuccess, searchShopFail, searchShopFinally } from './store/search';

### reducers

``````import { reducer as addShop } from './store/add';
import { reducer as removeShop } from './store/delete';
import { reducer as changeShop } from './store/change';
import { reducer as searchShop } from './store/search';

const shopReducer = [ // 整合reducer
removeShop,
changeShop,
searchShop
];

let initialState = {
};

export default (state = initialState, action) => {
let newState = { ...state };
// 对所有reducer进行迭代。类似于compose
return shopReducer.reduce((preReducer, nextReducer) => {
return nextReducer(preReducer, action)
, newState);
};

### store/reducers.js

``````import { combineReducers } from '../redux';
import { connectRouter } from 'connected-react-router';
import history from 'router/history';
import publicState from 'store/Public';
import shopOperation from './Shop/reducers';

export default function createReducer(asyncReducers) {
return combineReducers({
router: connectRouter(history),
shop: shopOperation,
public: publicState,
...asyncReducers// 异步Reducer
});
}

### 业务组件内调用

``````import React from 'react';
import { addShopBegin } from 'store/Shop/actions';
import { connect } from 'react-redux';
import { bindActionCreators } from '../redux/index';

const Home = (props) => {
const { changeLoaded } = props;
return (
<div>
</div>
);
};

function mapDispatchToProps(dispatch) {
}
export default connect(null, mapDispatchToProps)(Home);

## react 如何查看每个组件渲染性能

• 紫色的代表style样式计算/layout
• 黄色代表js操作
• 蓝色代表html解析
• 灰色代表其他操作
• 红色框里代表这个地方存在 强制回流 、long task、FPS低、CPU 占用过多

Chrome 独有的原生Api `requestIdleCallback`。可以在告诉浏览器，当你不忙(Cpu占用较低)的时候执行这个回调函数，类似于script标签的async 。 如果要考虑兼容性的话还是用web Worker来做一些优先级较低的任务。

## why-did-you-update

``````import React from 'react'
import whyDidYouUpdate from 'why-did-you-update'
if (process.env.NODE_ENV !== 'production') {
whyDidYouUpdate(React);
}