redux 源码

核心逻辑

redux/index.js

function createStore (reducer, preloadedState) { // store 对象中存储的状态 let currentState = preloadedState; // 存放订阅者函数 const currentListeners = []; // 获取状态 function getState () { return currentState; } // 触发 action function dispatch (action) { currentState = reducer(currentState, action); // 循环数据,调用订阅者 for (let i = 0; i < currentListeners.length; i++) { const listener = currentListeners[i]; listener(); } } // 订阅状态 function subscribe (listener) { currentListeners.push(listener); } return { getState, dispatch, subscribe } }

index.html

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Redux 源码实现</title> </head> <body> <button id="J-increment">+</button> <span id="J-count">0</span> <button id="J-decrement">-</button> <script src="./redux/index.js"></script> <script> const oCount = document.getElementById('J-count'); const oIncrementBtn = document.getElementById('J-increment'); const oDecrementBtn = document.getElementById('J-decrement'); function reducer (state, action) { switch (action.type) { case 'increment': return state + 1; case 'decrement': return state - 1; default: return state; } } // 创建 store const store = createStore(reducer, 0); // 触发 action oIncrementBtn.onclick = function () { store.dispatch({ type: 'increment' }); } oDecrementBtn.onclick = function () { store.dispatch({ type: 'decrement' }); } // 订阅状态 store.subscribe(() => { const count = store.getState(); oCount.innerHTML = count; }); </script> </body> </html>

参数类型约束

/** * @file 自定义 Redux */ function createStore (reducer, preloadedState) { // 约束 reducer 参数类型 if (typeof reducer !== 'function') { throw new Error('reducer has to be a function.'); } // store 对象中存储的状态 let currentState = preloadedState; // 存放订阅者函数 const currentListeners = []; // 获取状态 function getState () { return currentState; } // 触发 action function dispatch (action) { // 判断 action 是否是对象 if (!isPlainObject(action)) { throw new Error('action has be a object.'); } // 判断对象是否存在 type 属性 if (typeof action.type === 'undefined') { throw new Error('the type attribute must exist.'); } currentState = reducer(currentState, action); // 循环数据,调用订阅者 for (let i = 0; i < currentListeners.length; i++) { const listener = currentListeners[i]; listener(); } } // 订阅状态 function subscribe (listener) { currentListeners.push(listener); } return { getState, dispatch, subscribe } } // 判断参数是否是对象 function isPlainObject (obj) { // 排除基础数据类型和 null 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; }

Enhancer

/** * @file 自定义 Redux */ /** * @descriptions * @param {function} reducer * @param {object} preloadedState * @param {function} enhancer * @returns */ function createStore (reducer, preloadedState, enhancer) { // 约束 reducer 参数类型 if (!iSFunction(reducer)) { throw new Error('reducer has to be a function.'); } // 判断 enhancer 参数 if (typeof enhancer !== 'undefined') { if (!iSFunction(reducer)) { throw new Error('enhancer has to be a function.'); } return enhancer(createStore)(reducer, preloadedState); } // store 对象中存储的状态 let currentState = preloadedState; // 存放订阅者函数 const currentListeners = []; // 获取状态 function getState () { return currentState; } // 触发 action function dispatch (action) { // 判断 action 是否是对象 if (!isPlainObject(action)) { throw new Error('action has be a object.'); } // 判断对象是否存在 type 属性 if (typeof action.type === 'undefined') { throw new Error('the type attribute must exist.'); } currentState = reducer(currentState, action); // 循环数据,调用订阅者 for (let i = 0; i < currentListeners.length; i++) { const listener = currentListeners[i]; listener(); } } // 订阅状态 function subscribe (listener) { currentListeners.push(listener); } return { getState, dispatch, subscribe } } // 判断参数是否是对象 function isPlainObject (obj) { // 排除基础数据类型和 null 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; } function iSFunction (val) { return typeof val === 'function'; }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Redux 源码实现</title> </head> <body> <button id="J-increment">+</button> <span id="J-count">0</span> <button id="J-decrement">-</button> <script src="./redux/index.js"></script> <script> const oCount = document.getElementById('J-count'); const oIncrementBtn = document.getElementById('J-increment'); const oDecrementBtn = document.getElementById('J-decrement'); function enhancer (createStore) { return function (reducer, preloadedState) { const store = createStore(reducer, preloadedState); let dispatch = store.dispatch; function _dispatch (action) { if (typeof action === 'function') { return action(dispatch); } dispatch(action); } return { ...store, dispatch: _dispatch }; } } function reducer (state, action) { switch (action.type) { case 'increment': return state + 1; case 'decrement': return state - 1; default: return state; } } // 创建 store const store = createStore(reducer, 0, enhancer); // 触发 action oIncrementBtn.onclick = function () { // store.dispatch({ type: 'increment' }); store.dispatch((dispatch) => { setTimeout(() => { dispatch({ type: 'increment' }); }, 1000); }); } oDecrementBtn.onclick = function () { store.dispatch({ type: 'decrement' }); } // 订阅状态 store.subscribe(() => { const count = store.getState(); oCount.innerHTML = count; }); </script> </body> </html>

applyMiddleware 中间件其实就是一个内置的 Enhancer 函数。

applyMiddleware

redux/middlewares/logger.js

function logger (store) { return function (next) { return function (action) { console.log('logger'); next(action); } } }

redux/middlewares/thunk.js

function thunk (store) { return function (next) { return function (action) { console.log('thunk'); next(action); } } }

redux/index.js

/** * @file 自定义 Redux */ /** * @descriptions * @param {function} reducer * @param {object} preloadedState * @param {function} enhancer * @returns */ function createStore (reducer, preloadedState, enhancer) { // 约束 reducer 参数类型 if (!iSFunction(reducer)) { throw new Error('reducer has to be a function.'); } // 判断 enhancer 参数 if (typeof enhancer !== 'undefined') { if (!iSFunction(reducer)) { throw new Error('enhancer has to be a function.'); } return enhancer(createStore)(reducer, preloadedState); } // store 对象中存储的状态 let currentState = preloadedState; // 存放订阅者函数 const currentListeners = []; // 获取状态 function getState () { return currentState; } // 触发 action function dispatch (action) { // 判断 action 是否是对象 if (!isPlainObject(action)) { throw new Error('action has be a object.'); } // 判断对象是否存在 type 属性 if (typeof action.type === 'undefined') { throw new Error('the type attribute must exist.'); } currentState = reducer(currentState, action); // 循环数据,调用订阅者 for (let i = 0; i < currentListeners.length; i++) { const listener = currentListeners[i]; listener(); } } // 订阅状态 function subscribe (listener) { currentListeners.push(listener); } return { getState, dispatch, subscribe } } // 判断参数是否是对象 function isPlainObject (obj) { // 排除基础数据类型和 null 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; } function iSFunction (val) { return typeof val === 'function'; } function applyMiddleware (...middlewares) { return function (createStore) { return function (reducer, preloadedState) { const store = createStore(reducer, preloadedState); const middlewareAPI = { getState: store.getState, dispatch: store.dispatch }; // 调用中间件函数,传递 store 对象 const chain = middlewares.map(middleware => middleware(middlewareAPI)); // 第一个中间件的最里层函数 const dispatch = compose(...chain)(store.dispatch); return { ...store, dispatch } } } } function compose () { const funcs = [...arguments]; return function (dispatch) { for (let i = funcs.length - 1; i >= 0; i--) { dispatch = funcs[i](dispatch); } return dispatch; } }

index.html

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Redux 源码实现</title> </head> <body> <button id="J-increment">+</button> <span id="J-count">0</span> <button id="J-decrement">-</button> <script src="./redux/index.js"></script> <script src="./redux/middlewares/logger.js"></script> <script src="./redux/middlewares/thunk.js"></script> <script> const oCount = document.getElementById('J-count'); const oIncrementBtn = document.getElementById('J-increment'); const oDecrementBtn = document.getElementById('J-decrement'); // function enhancer (createStore) { // return function (reducer, preloadedState) { // const store = createStore(reducer, preloadedState); // let dispatch = store.dispatch; // function _dispatch (action) { // if (typeof action === 'function') { // return action(dispatch); // } // dispatch(action); // } // return { // ...store, // dispatch: _dispatch // }; // } // } function reducer (state, action) { switch (action.type) { case 'increment': return state + 1; case 'decrement': return state - 1; default: return state; } } // 创建 store // const store = createStore(reducer, 0, enhancer); const store = createStore(reducer, 0, applyMiddleware(logger, thunk)); // 触发 action oIncrementBtn.onclick = function () { // store.dispatch((dispatch) => { // setTimeout(() => { // dispatch({ type: 'increment' }); // }, 1000); // }); store.dispatch({ type: 'increment' }); } oDecrementBtn.onclick = function () { store.dispatch({ type: 'decrement' }); } // 订阅状态 store.subscribe(() => { const count = store.getState(); oCount.innerHTML = count; }); </script> </body> </html>

bindActionCreator

redux/index.js

/** * @file 自定义 Redux */ /** * @descriptions * @param {function} reducer * @param {object} preloadedState * @param {function} enhancer * @returns */ function createStore (reducer, preloadedState, enhancer) { // 约束 reducer 参数类型 if (!iSFunction(reducer)) { throw new Error('reducer has to be a function.'); } // 判断 enhancer 参数 if (typeof enhancer !== 'undefined') { if (!iSFunction(reducer)) { throw new Error('enhancer has to be a function.'); } return enhancer(createStore)(reducer, preloadedState); } // store 对象中存储的状态 let currentState = preloadedState; // 存放订阅者函数 const currentListeners = []; // 获取状态 function getState () { return currentState; } // 触发 action function dispatch (action) { // 判断 action 是否是对象 if (!isPlainObject(action)) { throw new Error('action has be a object.'); } // 判断对象是否存在 type 属性 if (typeof action.type === 'undefined') { throw new Error('the type attribute must exist.'); } currentState = reducer(currentState, action); // 循环数据,调用订阅者 for (let i = 0; i < currentListeners.length; i++) { const listener = currentListeners[i]; listener(); } } // 订阅状态 function subscribe (listener) { currentListeners.push(listener); } return { getState, dispatch, subscribe } } // 判断参数是否是对象 function isPlainObject (obj) { // 排除基础数据类型和 null 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; } function iSFunction (val) { return typeof val === 'function'; } function applyMiddleware (...middlewares) { return function (createStore) { return function (reducer, preloadedState) { const store = createStore(reducer, preloadedState); const middlewareAPI = { getState: store.getState, dispatch: store.dispatch }; // 调用中间件函数,传递 store 对象 const chain = middlewares.map(middleware => middleware(middlewareAPI)); // 第一个中间件的最里层函数 const dispatch = compose(...chain)(store.dispatch); return { ...store, dispatch } } } } function compose () { const funcs = [...arguments]; return function (dispatch) { for (let i = funcs.length - 1; i >= 0; i--) { dispatch = funcs[i](dispatch); } return dispatch; } } function bindActionCreators (actionCreators, dispatch) { const boundActionCreators = {}; for (let key in actionCreators) { boundActionCreators[key] = function () { dispatch(actionCreators[key]()); } } return boundActionCreators; }

index.html

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Redux 源码实现</title> </head> <body> <button id="J-increment">+</button> <span id="J-count">0</span> <button id="J-decrement">-</button> <script src="./redux/index.js"></script> <script src="./redux/middlewares/logger.js"></script> <script src="./redux/middlewares/thunk.js"></script> <script> const oCount = document.getElementById('J-count'); const oIncrementBtn = document.getElementById('J-increment'); const oDecrementBtn = document.getElementById('J-decrement'); function reducer (state, action) { switch (action.type) { case 'increment': return state + 1; case 'decrement': return state - 1; default: return state; } } // 创建 store const store = createStore(reducer, 0, applyMiddleware(logger, thunk)); // 订阅状态 store.subscribe(() => { const count = store.getState(); oCount.innerHTML = count; }); const actions = bindActionCreators({ increment, decrement }, store.dispatch); function increment () { return { type: 'increment' }; } function decrement () { return { type: 'decrement' }; } // 触发 action oIncrementBtn.onclick = function () { // store.dispatch({ type: 'increment' }); actions.increment(); } oDecrementBtn.onclick = function () { // store.dispatch({ type: 'decrement' }); actions.decrement(); } </script> </body> </html>

combineReducer

redux/index.js

/** * @file 自定义 Redux */ /** * @descriptions * @param {function} reducer * @param {object} preloadedState * @param {function} enhancer * @returns */ function createStore (reducer, preloadedState, enhancer) { // 约束 reducer 参数类型 if (!iSFunction(reducer)) { throw new Error('reducer has to be a function.'); } // 判断 enhancer 参数 if (typeof enhancer !== 'undefined') { if (!iSFunction(reducer)) { throw new Error('enhancer has to be a function.'); } return enhancer(createStore)(reducer, preloadedState); } // store 对象中存储的状态 let currentState = preloadedState; // 存放订阅者函数 const currentListeners = []; // 获取状态 function getState () { return currentState; } // 触发 action function dispatch (action) { // 判断 action 是否是对象 if (!isPlainObject(action)) { throw new Error('action has be a object.'); } // 判断对象是否存在 type 属性 if (typeof action.type === 'undefined') { throw new Error('the type attribute must exist.'); } currentState = reducer(currentState, action); // 循环数据,调用订阅者 for (let i = 0; i < currentListeners.length; i++) { const listener = currentListeners[i]; listener(); } } // 订阅状态 function subscribe (listener) { currentListeners.push(listener); } return { getState, dispatch, subscribe } } // 判断参数是否是对象 function isPlainObject (obj) { // 排除基础数据类型和 null 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; } function iSFunction (val) { return typeof val === 'function'; } function applyMiddleware (...middlewares) { return function (createStore) { return function (reducer, preloadedState) { const store = createStore(reducer, preloadedState); const middlewareAPI = { getState: store.getState, dispatch: store.dispatch }; // 调用中间件函数,传递 store 对象 const chain = middlewares.map(middleware => middleware(middlewareAPI)); // 第一个中间件的最里层函数 const dispatch = compose(...chain)(store.dispatch); return { ...store, dispatch } } } } function compose () { const funcs = [...arguments]; return function (dispatch) { for (let i = funcs.length - 1; i >= 0; i--) { dispatch = funcs[i](dispatch); } return dispatch; } } function bindActionCreators (actionCreators, dispatch) { const boundActionCreators = {}; for (let key in actionCreators) { boundActionCreators[key] = function () { dispatch(actionCreators[key]()); } } return boundActionCreators; } function combineReducers (reducers) { // 检查 reducer 类型,必须是函数类型 const reducerKeys = Object.keys(reducers); for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i]; if (!iSFunction(reducers[key])) { throw new Error('reducer has to be a function.'); } } return function (state, action) { // 调用 reducer,并存储 reducer 返回的状态 let nextState = {}; for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i]; const reducer = reducers[key]; const previousState = state[key]; nextState[key] = reducer(previousState, action); } return nextState; } }

index.html

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Redux 源码实现</title> </head> <body> <button id="J-increment">+</button> <span id="J-count">0</span> <button id="J-decrement">-</button> <script src="./redux/index.js"></script> <script src="./redux/middlewares/logger.js"></script> <script src="./redux/middlewares/thunk.js"></script> <script> const oCount = document.getElementById('J-count'); const oIncrementBtn = document.getElementById('J-increment'); const oDecrementBtn = document.getElementById('J-decrement'); function counterReducer (state, action) { switch (action.type) { case 'increment': return state + 1; case 'decrement': return state - 1; default: return state; } } const rootReducer = combineReducers({ counter: counterReducer }); // 创建 store const store = createStore(rootReducer, { counter: 0 }, applyMiddleware(logger, thunk)); // 订阅状态 store.subscribe(() => { const count = store.getState().counter; oCount.innerHTML = count; }); const actions = bindActionCreators({ increment, decrement }, store.dispatch); function increment () { return { type: 'increment' }; } function decrement () { return { type: 'decrement' }; } // 触发 action oIncrementBtn.onclick = function () { // store.dispatch({ type: 'increment' }); actions.increment(); } oDecrementBtn.onclick = function () { // store.dispatch({ type: 'decrement' }); actions.decrement(); } </script> </body> </html>