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>