创建一个 React Store来存储应用中所有的 State,应用中应仅有一个 Store。
DEMO
先从一个 Demo 来开始源码分析:
import { createStore } from 'redux'function todos(state = [], action) {switch (action.type) {case 'ADD_TODO':return state.concat([action.text])default:return state}}let store = createStore(todos, ['Use Redux'])store.dispatch({type: 'ADD_TODO',text: 'Read the docs'})console.log(store.getState())
上面的代码,我们就能得到state为['Use Redux', 'Read the docs']。
createStore 源码
方法:createStore(reducer, [preloadedState], [enhancer])
参数:
reducer是一个函数,该函数接收两个参数:state和action,且返回一个新的statepreloadedState初始时的stateenhancer是一个组合式的store creator的高阶函数,返回一个新的强化过的store creator。于middleware有些相似。
返回值: 一个新的state对象。
export default function createStore(reducer, preloadedState, enhancer) {// 有初始化函数state,且没有传第三参数,enhancer 即为 preloadedStateif (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {enhancer = preloadedStatepreloadedState = undefined}if (typeof enhancer !== 'undefined') {if (typeof enhancer !== 'function') {// 不是空,且不是一个函数,报错throw new Error('Expected the enhancer to be a function.')}// 对enhancer进行createStore处理,把reduce传进去return enhancer(createStore)(reducer, preloadedState)}// reducer必需是一个函数if (typeof reducer !== 'function') {throw new Error('Expected the reducer to be a function.')}let currentReducer = reducerlet currentState = preloadedStatelet currentListeners = []let nextListeners = currentListenerslet isDispatching = falsefunction ensureCanMutateNextListeners() {// 当这里指向同一个数组时,创建一个新的副本if (nextListeners === currentListeners) {nextListeners = currentListeners.slice()}}// 返回当前的statefunction getState() {return currentState}// redux的订阅函数,订阅 dispatchfunction subscribe(listener) {if (typeof listener !== 'function') {throw new Error('Expected the listener to be a function.')}if (isDispatching) {throw new Error('')}let isSubscribed = trueensureCanMutateNextListeners() // 每次调用,确保nextListeners变量是新的副本nextListeners.push(listener) // 存储订阅者// 返回一个可以取消订阅的函数return function unsubscribe() {if (!isSubscribed) {return}if (isDispatching) {throw new Error('')}isSubscribed = falseensureCanMutateNextListeners()const index = nextListeners.indexOf(listener)nextListeners.splice(index, 1)currentListeners = null}}// 派发函数,用于触发 reducer 修改 statefunction dispatch(action) {if (!isPlainObject(action)) { // action 必需是对象throw new Error('Actions must be plain objects. ' +'Use custom middleware for async actions.')}if (typeof action.type === 'undefined') { // 必需有type字段throw new Error('Actions may not have an undefined "type" property. ' +'Have you misspelled a constant?')}// 派发中不能再调用,想了一下,正常情况下不会走到这里,哪怕是多次调用也不会// 可能存在的就是是 reducer 里面又调用了dispatch,这样会导致state不是最新的。if (isDispatching) {throw new Error('Reducers may not dispatch actions.')}try {// 正在派发中。。。isDispatching = truecurrentState = currentReducer(currentState, action)} finally {isDispatching = false}// 派发结束,执行所有订阅者函数const listeners = (currentListeners = nextListeners)for (let i = 0; i < listeners.length; i++) {const listener = listeners[i]listener()}return action}// 如果你的应用实现了代码拆分,需要动态加载reducer,需要用到function replaceReducer(nextReducer) {if (typeof nextReducer !== 'function') {throw new Error('Expected the nextReducer to be a function.')}currentReducer = nextReducerdispatch({ type: ActionTypes.REPLACE })}// 该方法的作用是,订阅store的变化,适用于所有observable的类库。比如 RxJS。function observable() {const outerSubscribe = subscribereturn {subscribe(observer) {if (typeof observer !== 'object' || observer === null) {throw new TypeError('Expected the observer to be an object.')}// // 用于给 subscribe 注册的函数,严格按照 Observable 的规范实现,observer 必须有一个 next 属性。function observeState() {if (observer.next) {observer.next(getState())}}observeState()const unsubscribe = outerSubscribe(observeState)return { unsubscribe }},// $$observable 即为 Symbol.observable,也属于 Observable 的规范,返回自身。[$$observable]() {return this}}}// 初始化时执行一个 INIT 类型的actiondispatch({ type: ActionTypes.INIT })return {dispatch,subscribe,getState,replaceReducer,[$$observable]: observable}}
总结
createStore方法返回一个对象,里面包含了一些常用的 API。
dispatch派发任务subscribe实现了观察者模式,每次dispatch后都会遍历一遍currentListenersgetState返回当前的statereplaceReducer代码拆分时动态加载reducer
