import './index.css'import React from 'react'import ReactDOM from 'react-dom'import App from './App/App'import { createStore } from 'redux'const store = createStore((state = { count: 1 }, action) => { switch (action.type) { case 'ADD': state.count += action.value break case 'MINUS': state.count -= action.value break } return state})function createAction(type, value) { return () => { store.dispatch({ type, value }) }}function render() { ReactDOM.render(, document.getElementById('root') )}render()store.subscribe(render)复制代码
以上是使用Redux的一个最简单的例子。涵盖了redux的基本使用方式,用户点击按钮 --> dispatch(action) --> 更新store --> 触发监听函数render,重绘。
文件目录
- applyMiddleware.js // Redux的插件机制实现,可以重点学习一下
- bindActionCreators.js // 将ActionCreator和dispatch绑定的工具
- combineReducers.js // 将store分层的工具
- compose.js // 将数个函数合并嵌套执行
- createStore.js // 核心,生成store
- index.js // 入口
combineReducers
const store = createStore(combineReducers({ PageA: PageAReducer, PageB: PageBReducer}))复制代码
因为createStore的接收的是一个函数,当dispatch发生时,它就会被执行,将当前的store和被触发的action作为参数,传入这个函数。所以combineReducers的主要任务就是:遍历一个combinedReducers对象,将每个子reducer依次执行,并将执行结果赋予其所对应的key,最后返回一个总的newStore。有了以上的基础,我们来看源码实现:
export default function combineReducers(reducers) { const reducerKeys = Object.keys(reducers) const finalReducers = {} for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i] // 将每一个子reducer,保存到finalReducer对象中 if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } const finalReducerKeys = Object.keys(finalReducers) return function combination(state = {}, action) { let hasChanged = false const nextState = {} for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] // 取出一个子reducer const reducer = finalReducers[key] // 取得这个key对应的旧的值 const previousStateForKey = state[key] // 核心操作,将旧的state和action传入这个reducer,得到新的state const nextStateForKey = reducer(previousStateForKey, action) if (typeof nextStateForKey === 'undefined') { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } return hasChanged ? nextState : state }}复制代码
applyMiddleware
createStore(reducers, applyMiddleware(reduxLogger, reduxPromise, reduxThunk))复制代码
这个插件机制是Redux一个很重要的功能,使得我们可以很方便的对action做各种各样的操作。比如日志打印,异步数据获取等等,只要添加一个middleware,就可以实现。这个类似于Express或者Koa中的中间件机制。我们来看一下他们是如何实现的
export default function applyMiddleware(...middlewares) { return createStore => (...args) => { // 这里通过将createStore方法和参数传入的方式,得到store // 为什么不直接将store传入呢?因为,我们查看createStore的代码,可以发现 // return enhancer(createStore)(reducer, preloadedState) // 这里的 enhancer其实就是当前的applyMiddeware,通过这种方式,可以保证window.store是由applyMiddleware最后return出去的 // 这个对我们以后写组件有借鉴意义,比如 // 我们有一个 ComponentA: const compA = new Component(args); // 现在我们想强化一下CompoentA的功能,通过传入组件B的方式,那么, const enhancedCompA = new ComponentA(args, B) // 这时候,如果B需要ComponentA原有的结果,那么就可以使用这种方式,最后返回的结果一定是经过B处理过的 const store = createStore(...args) let dispatch = () => {} let chain = [] const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args) } // 将getState和dispatch,作为参数传给每一个middleware chain = middlewares.map(middleware => middleware(middlewareAPI)) /** 这个compose的作用就是: compose(a,b,c) ===> (...args) => a(b(c(...args))) 假设有[a,b,c]三个middleware,他们都长这样: ({ dispatch, getState }) => next => action => { // 对action的操作 blablabla return next(action); }; 那么,c会最先接收到一个参数,就是store.dispatch,作为它的next。然后c使用闭包将这个next存起来,把自己作为下一个middleware b的next参数传入。这样,就将所有的middleware串起来了。 最后,如果用户dispatch一个action,那么执行顺序会是: c --> b --> a **/ dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } }}复制代码
createStore
createStore主要会返回三个东西:
- getState
- subscribe
- dispatch
getState
很简单,返回当前的store
function getState() { // 这里有个锁的机制,保证当另一个action在dispatch时,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 }复制代码
subscribe
注册监听函数
function subscribe(listener) { if (isDispatching) { throw new Error(...) } let isSubscribed = true // 这里Redux维护了两个Listener List: // currentListeners // nextListeners // 因为currentListeners和nextListeners是独立的,就可以保证当currentListeners还在执行的时候,调用subscribe或unsubscribe是不会影响到原先原先注册的listeners的。 // 只有当下一次dispatch前,nextListeners又被同步给了currentListeners,之前的注册注销才会生效 ensureCanMutateNextListeners() nextListeners.push(listener) return function unsubscribe() { if (!isSubscribed) { return } if (isDispatching) { throw new Error(...) } isSubscribed = false ensureCanMutateNextListeners() const index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) } }复制代码
dispatch
redux中最常用的一个方法了
function dispatch(action) { if (isDispatching) { throw new Error('Reducers may not dispatch actions.'); } try { isDispatching = true; // 使用绑定的reducer,计算出新的store currentState = currentReducer(currentState, action); } finally { isDispatching = false; } // 触发所有绑定的监听器 var listeners = currentListeners = nextListeners; for (var i = 0; i < listeners.length; i++) { var listener = listeners[i]; listener(); } return action; }复制代码