createStore
createStore函数返回一个对象, 该对象包含属性:
- dispatch: 分发一个action
- getState: 得到仓库中当前的状态
- replaceReducer: 替换掉当前的reducer(没什么用, 不实现)
- subscribe: 注册一个监听器, 监听器是一个无参函数, 分发一个action后, 会运行注册的监听器. 该函数返回一个函数, 用于取消监听
- Symbol("observable")(observable还是提案, 不实现)
createStore函数代码
import ActionTypes from './utils/actionTypes';
import { isPlainObject } from './utils/isPlainObject';
/**
* 实现createStore功能
* @param {function} reducer reducer函数
* @param {*} defaultState 默认的状态值
*/
export default function (reducer, defaultState) {
let currentReducer = reducer; // 当前的reducer
let currentState = defaultState; // 当前仓库中的状态
const listeners = []; // 记录所有的监听器
function dispatch(action) {
// 验证action
if (!isPlainObject(action)) {
throw new TypeError("action must be a plain object");
}
// 验证action的type属性是否存在
if (action.type === undefined) {
throw new TypeError("action must have a 'type' property");
}
// 进行状态更新
currentState = currentReducer(currentState, action);
// 触发监听器
for (const listener of listeners) {
listener();
}
}
function getState() {
return currentState;
}
function subscribe(listener) {
listeners.push(listener);
let isremove = false;
return function () {
if (isremove) {
return;
}
// 将listener从数组中移除
const index = listeners.indexOf(listener);
listeners.splice(index, 1);
isremove = true;
}
}
// 创建仓库时, 需要分发一个特殊的action. 完成状态的初始化
dispatch({
type: ActionTypes.INIT()
});
return {
dispatch,
getState,
subscribe,
}
}
// isPlainObject.js文件
/**
* 判断一个对象是否是平面对象
* @param {*} obj
*/
export function isPlainObject(obj) {
if (typeof obj !== 'object') {
return false;
}
return Object.getPrototypeOf(obj) === Object.prototype;
}
// actionTypes.js文件
/**
* 得到一个指定长度的随机字符串
* @param {*} length
*/
function getRandomString(length) {
return Math.random().toString(36).substr(2, length);
}
export default {
INIT() {
return `@@redux/INIT${getRandomString(6).split("").join(".")}`
},
UNKNOWN() {
return `@@redux/PROBE_UNKNOWN_ACTION${getRandomString(6).split("").join(".")}`
}
}
bindActionCreators
bindActionCreators函数有两个参数:
- actionCreators: action创建函数
- dispatch: store的dispatch函数
它的功能是:
- 如果actionCreators是一个函数, 那么返回一个新的函数, 这个新的函数需要的参数和actionCreators函数一样, 但是会自动dispatch
- 如果actionCreators是一个对象, 并且这个对象的每个属性的值, 都是一个action创建函数, 那么返回一个对象. 返回的对象属性跟之前的一模一样, 但是属性值变为增强后的action创建函数, 会自动dispatch
bindActionCreators代码
export default function (actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
return getAutoDispatchActonCreator(actionCreators, dispatch);
} else if (typeof actionCreators === 'object') {
const result = {};
for (const key in actionCreators) {
if (actionCreators.hasOwnProperty(key)) {
const actionCreator = actionCreators[key];
// 只有当传入的这个对象的属性值是个函数的时候, 才进行增强
// 否则什么都不做
if (typeof actionCreator === 'function') {
result[key] = getAutoDispatchActonCreator(actionCreator, dispatch);
}
}
}
return result;
} else {
throw new TypeError("actionCreators must be an object or function");
}
}
/**
* 得到一个自动分发的action创建函数
* @param {*} actionCreator
* @param {*} dispatch
*/
function getAutoDispatchActonCreator(actionCreator, dispatch) {
return function (...args) {
const action = actionCreator(...args)
dispatch(action);
}
}
combineReducers
combineReducers组装多个reducers, 返回一个reducer, 数据使用一个对象表示, 对象的属性名与传递的参数对象保持一致.
即参数是一个对象, 对象的每个属性的值都是一个reducer函数, 返回一个函数(reducer函数), 这个reducer的状态是一个对象, 此对象的属性对应着参数对象的属性.
import { isPlainObject } from './utils/isPlainObject';
import actionTypes from './utils/actionTypes';
export default function (reducers) {
// 1. 验证输入参数
validateReducers(reducers);
// 返回的是一个reducer函数
return function (state = {}, action) {
const newState = {};
for (const key in reducers) {
if (reducers.hasOwnProperty(key)) {
const reducer = reducers[key];
newState[key] = reducer(state[key], action);
}
}
return newState;
}
}
function validateReducers(reducers) {
if (typeof reducers !== 'object') {
throw new TypeError("parameter of combineReducers must be an object");
}
if (!isPlainObject(reducers)) {
throw new TypeError("parameter of combineReducers must be a plain object");
}
// 验证reducer的返回结果是不是undefined
// 派发两个特殊的action, 目的就是要确保每个reducer函数, 在switch判断匹配不到的时候, 会原封不动的返回原来的状态
// 即确保每个reducer函数写了switch代码段的default处理情况.
// 因为这两个action的type值很特殊, 一般的reducer肯定不会匹配上, 肯定会进入到switch的default处理分支.
// 而每个reducer都有默认的state值, 如果验证传入的参数是undefined, 那么会使用默认值, 不应该返回undefined
for (const key in reducers) {
if (reducers.hasOwnProperty(key)) {
const reducer = reducers[key];
// 传递特殊type值
// reducer有默认值, 本次调用传入undefined参数, 就会使用默认值
let state = reducer(undefined, { type: actionTypes.INIT() });
if (state === undefined) {
throw new TypeError("reducers must not return undefined");
}
state = reducer(undefined, { type: actionTypes.UNKNOWN() });
if (state === undefined) {
throw new TypeError("reducers must not return undefined");
}
}
}
}
applyMiddleware
redux的中间件: 中间件实际上是对store里dispatch函数的增强
-
本身是一个函数, 该函数接收一个store参数, 表示创建的仓库, 该仓库并非一个完整的仓库对象, 仅包含getState, dispatch. 该函数运行的时间, 是在仓库创建之后
- 由于创建仓库后需要自动运行设置的中间件参数, 因此, 需要在创建仓库时告诉仓库有哪些中间件
- 需要调用applyMiddleware函数, 将函数的返回结果作为createStore的第二或者第三个参数
-
中间件函数必须返回一个dispatch创建函数
-
applyMiddleware函数, 用于记录有哪些中间件, 它会返回一个函数
- 该函数用于记录创建仓库的方法, 然后又返回一个函数
- 返回的函数用来创建仓库
- 该函数用于记录创建仓库的方法, 然后又返回一个函数
中间件按照注册的顺序, 最后一个中间件传入store中原始的dispatch, 经过增强后返回新的dispatch传入倒数第二个中间件, 以此类推. 最终, 第一个中间件返回的dispatch函数, 重新赋值给store的dispatch属性.
QQ浏览器截图20200510135004.pngapplyMiddleware代码
// applyMiddleware.js文件
import compose from './compose';
/**
* 注册中间件函数
* @param {...any} middlewares 所有中间件
*/
export default function (...middlewares) {
// 返回的函数接收创建仓库的方法
return function (createStore) {
// 再次返回的函数接收reducer和defaultState实际创建仓库
return function (reducer, defaultState) {
// 创建仓库
const store = createStore(reducer, defaultState);
// dispatch赋值为这个函数的目的是, 让开发者在dispatch完成替换前不要调用
let dispatch = () => {
throw new Error("目前还不能使用dispatch");
};
// 给dispatch赋值, 替换dispatch
// 根据中间件数组, 得到一个dispatch创建函数的数组
const simpleStore = {
getState: store.getState,
// 不能使用下面被注释的这种写法, 这种写法的dispatch指向最原始的store的dispatch
// dispatch: store.dispatch,
// 而且也不能如下面注释这样写, 这样写的话会一直指向抛出错误的那个函数
// dispatch: dispatch,
dispatch: (...args) => dispatch(...args),
};
const dispatchProducers = middlewares.map(mid => mid(simpleStore));
const dispatchProducer = compose(...dispatchProducers);
dispatch = dispatchProducer(store.dispatch);
return {
...store,
dispatch,
}
}
}
}
// compose.js文件
export default function compose(...funcs) {
// 如果没有函数要组合, 则返回的函数原封不动的返回参数
if (funcs.length === 0) {
return args => args;
} else if (funcs.length === 1) {
return funcs[0];
} else {
return funcs.reduce((a, b) => (...args) => a(b(...args)));
}
}
redux中间件: thunk
thunk中间件: 如果给thunk中间件的是action是一个函数, 那么thunk会运行该函数. 如果不是函数, 则向后移交action
thunk代码
function createThunkMiddleware(extra) {
// 返回一个thunk中间件
return store => next => action => {
if (typeof action === 'function') {
return action(store.dispatch, store.getState, extra);
} else {
return next(action);
}
}
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
网友评论