目标:自己实现redux的核心逻辑
源码地址:https://github.com/qifutian/learngit/tree/main/redux%E5%8F%8A%E4%B8%AD%E9%97%B4%E4%BB%B6/myRedux
自定义redux,myRedux.js
/**
*
* createStore(reducer,preloadedState,enhancer)
*
* {getState,dispatch,subscribe}
*/
function createStore(reducer,preloadedState,enhancer){
// store对象中存储的状态
var currentState = preloadedState;
// 存放订阅者函数
var currentListeners = [];
// 获取状态 将state放到内存中
function getState(){
return currentState
}
// 触发action
function dispatch(action){
currentState = reducer(currentState,action);
// 循环数组 调用订阅者
for(var i=0; i<currentListeners.length;i++){
// 获取订阅者
var listener = currentListeners[i];
// 调用订阅者
listener();
}
}
// 订阅状态
function subscribe(listener) {
currentListeners.push(listener)
}
return {
getState,
dispatch,
subscribe
}
}
// store.subscribe(() =>{
// })
检查是否可用,测试代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button id="increment">+1</button>
<span id="box">0</span>
<button id="decrement">-1</button>
<script src="./myRedux.js"></script>
<script>
function reducer(state,action){
switch(action.type){
case 'increment':
return state + 1;
case 'decrement':
return state - 1;
default:
return state;
}
}
// 创建store
var store = createStore(reducer,0);
console.log(store.getState())
store.subscribe(function () {
document.getElementById("box").innerHTML = store.getState().counter;
});
document.getElementById("increment").onclick = function () {
store.dispatch({
type: "increment" });
};
document.getElementById("decrement").onclick = function () {
store.dispatch({
type: "decrement" });
};
</script>
</body>
</html>
/**
*
* createStore(reducer,preloadedState,enhancer)
*
* {getState,dispatch,subscribe}
*/
function createStore(reducer,preloadedState,enhancer){
// 约束reducer参数类型
if(typeof reducer!== 'function') throw new Error("reducer必须是函数")
// store对象中存储的状态
var currentState = preloadedState;
// 存放订阅者函数
var currentListeners = [];
// 获取状态 将state放到内存中
function getState(){
return currentState
}
// 触发action
function dispatch(action){
// 判断action是否是一个对象
if (!isPlainObject(action)) throw new Error('action必须是一个对象');
// 判断action中的type属性是否存在
if (typeof action.type === 'undefined') throw new Error('action对象中必须有type属性');
currentState = reducer(currentState,action);
// 循环数组 调用订阅者
for(var i=0; i<currentListeners.length;i++){
// 获取订阅者
var listener = currentListeners[i];
// 调用订阅者
listener();
}
}
// 订阅状态
function subscribe(listener) {
currentListeners.push(listener)
}
return {
getState,
dispatch,
subscribe
}
}
// store.subscribe(() =>{
// })
// 判断参数是否是对象类型
// 判断对象的当前原型对象是否和顶层原型对象相同
function isPlainObject (obj) {
if (typeof obj !== 'object' || obj === null) return false;
var proto = obj;
while (Object.getPrototypeOf(proto) != null) {
proto = Object.getPrototypeOf(proto)
}
return Object.getPrototypeOf(obj) === proto;
}
通过Enhancer方法对返回的store进行功能上的增强
redux的中间件就是对Enhancer方法的应用,执行异步代码
/**
*
* createStore(reducer,preloadedState,enhancer)
*
* {getState,dispatch,subscribe}
*/
function createStore(reducer,preloadedState,enhancer){
// 约束reducer参数类型
if(typeof reducer!== 'function') throw new Error("reducer必须是函数")
// 判断对象中是否含有type属性
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('enhancer必须是函数')
}
return enhancer(createStore)(reducer, preloadedState);
}
// store对象中存储的状态
var currentState = preloadedState;
// 存放订阅者函数
var currentListeners = [];
// 获取状态 将state放到内存中
function getState(){
return currentState
}
// 触发action
function dispatch(action){
// 判断action是否是一个对象
if (!isPlainObject(action)) throw new Error('action必须是一个对象');
// 判断action中的type属性是否存在
if (typeof action.type === 'undefined') throw new Error('action对象中必须有type属性');
currentState = reducer(currentState,action);
// 循环数组 调用订阅者
for(var i=0; i<currentListeners.length;i++){
// 获取订阅者
var listener = currentListeners[i];
// 调用订阅者
listener();
}
}
// 订阅状态
function subscribe(listener) {
currentListeners.push(listener)
}
return {
getState,
dispatch,
subscribe
}
}
// store.subscribe(() =>{
// })
// 判断参数是否是对象类型
// 判断对象的当前原型对象是否和顶层原型对象相同
function isPlainObject (obj) {
if (typeof obj !== 'object' || obj === null) return false;
var proto = obj;
while (Object.getPrototypeOf(proto) != null) {
proto = Object.getPrototypeOf(proto)
}
return Object.getPrototypeOf(obj) === proto;
}
html
<script>
function enhancer(createStore){
return function (reducer,preloadedState){
var store = createStore(reducer,preloadedState)
var 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
var store = createStore(reducer,0);
console.log(store.getState())
store.subscribe(function () {
document.getElementById("box").innerHTML = store.getState().counter;
});
document.getElementById("increment").onclick = function () {
// store.dispatch({ type: "increment" });
store.dispatch(function(dispatch){
setTimeout(function(){
dispatch({
type: 'increment'})
}, 1000)
})
};
document.getElementById("decrement").onclick = function () {
store.dispatch({
type: "decrement" });
};
</script>
创建中间件
logger.js
store是仓库,next是下一个中间件,就是源码里的reducer
function logger (store) {
return function (next) {
return function (action) {
console.log('logger');
next(action)
}
}
}
实现applyMiddleware方法
function applyMiddleware (...middlewares) {
return function (createStore) {
return function (reducer, preloadedState) {
// 创建 store
var store = createStore(reducer, preloadedState);
// 阉割版的 store
var middlewareAPI = {
getState: store.getState,
dispatch: store.dispatch
}
// 调用中间件的第一层函数 传递阉割版的store对象
var chain = middlewares.map(middleware => middleware(middlewareAPI));
var dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch
}
}
}
}
function compose () {
var funcs = [...arguments];
return function (dispatch) {
for (var i = funcs.length - 1; i >= 0; i--) {
dispatch = funcs[i](dispatch);
}
return dispatch;
}
}
一、将参数对象转换为同名 key 对象
二、应用场景
如果参数 actionCreators 类型为 function,则直接执行这个函数。 如果参数 actionCreators 类型不是非
null 的对象,抛错; 如果参数 actionCreators 类型为非 null 对象,则浅复制一份给变量
boundActionCreators。返回值为 boundActionCreators 。其目的是将参数对象转换成一个同名 key 对象。
当你需要将 actions 传给子组件,但是又不想子组件有任何 Redux 的存在,或者不行层层传递 dispatch 时 。可以使用
bindActionCreators。
bindActionCreators是redux的一个自带函数,作用是将单个或多个ActionCreator转化为dispatch(action)的函数集合形式。
开发者不用再手动dispatch(actionCreator(type)),而是可以直接调用方法。
可以实现简化书写,减轻开发的负担。
例如:
return {
// 当触发addNews时,dispatch会执行异步action
addNews(){
dispatch(async (a)=>{
// console.log(a == dispatch);// false dispatch
await axios.post("http://127.0.0.1/news",{
newsTitle:this.refs.newsTitle.value,
newsHref:this.refs.newsHref.value
});
this.props.getNews();
})
},
getNews(){
dispatch(async (b)=>{
console.log(b)
// 获取新闻列表,并将新闻列表放到仓库状态中。
const {
data} = await axios.get("http://127.0.0.1/news");
// dispatch(changeNewsList(data.newsList))
dispatch(changeNewsList(data.newsList))
})
}
}
利用bindActionCreators:
return {
newsList:state.news.newsList
}
}
// 操作状态。
function mapDispatchToProps(dispatch) {
return bindActionCreators(newsCreatore,dispatch);
}
通过actions对象调用方法,就可以dispatch所有的action
源码实现
function bindActionCreators (actionCreators, dispatch) {
var boundActionCreators = {
};
for (var key in actionCreators) {
(function (key) {
boundActionCreators[key] = function () {
dispatch(actionCreators[key]())
}
})(key)
}
return boundActionCreators;
}
随着应用变得复杂,需要对 reducer 函数 进行拆分,拆分后的每一块独立负责管理 state 的一部分。
combineReducers 辅助函数的作用是,把一个由多个不同 reducer 函数作为 value 的 object,合并成一个最终的 reducer 函数,然后就可以对这个 reducer 调用 createStore。
合并后的 reducer 可以调用各个子 reducer,并把它们的结果合并成一个 state 对象。state 对象的结构由传入的多个 reducer 的 key 决定。
最终,state 对象的结构会是这样的:
{
reducer1: ...
reducer2: ...
}
通过为传入对象的 reducer 命名不同来控制 state key 的命名。例如,你可以调用 combineReducers({ todos: myTodosReducer, counter: myCounterReducer }) 将 state 结构变为 { todos, counter }。
通常的做法是命名 reducer,然后 state 再去分割那些信息,因此你可以使用 ES6 的简写方法:combineReducers({ counter, todos })。这与 combineReducers({ counter: counter, todos: todos }) 一样。
源码实现
function combineReducers (reducers) {
// 1. 检查reducer类型 它必须是函数
var reducerKeys = Object.keys(reducers);
for (var i = 0; i < reducerKeys.length; i++) {
var key = reducerKeys[i];
if (typeof reducers[key] !== 'function') throw new Error('reducer必须是函数');
}
// 2. 调用一个一个的小的reducer 将每一个小的reducer中返回的状态存储在一个新的大的对象中
return function (state, action) {
var nextState = {
};
for (var i = 0; i < reducerKeys.length; i++) {
var key = reducerKeys[i];
var reducer = reducers[key];
var previousStateForKey = state[key];
nextState[key] = reducer(previousStateForKey, action)
}
return nextState;
}
}
function createStore (reducer, preloadedState, enhancer) {
// reducer 类型判断
if (typeof reducer !== 'function') throw new Error('redcuer必须是函数');
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('enhancer必须是函数')
}
return enhancer(createStore)(reducer, preloadedState);
}
// 状态
var currentState = preloadedState;
// 订阅者
var currentListeners = [];
// 获取状态
function getState () {
return currentState;
}
// 用于触发action的方法
function dispatch (action) {
// 判断action是否是一个对象
if (!isPlainObject(action)) throw new Error('action必须是一个对象');
// 判断action中的type属性是否存在
if (typeof action.type === 'undefined') throw new Error('action对象中必须有type属性');
// 调用reducer函数 处理状态
currentState = reducer(currentState, action);
// 调用订阅者 通知订阅者状态发生了改变
for (var i = 0; i < currentListeners.length; i++) {
var listener = currentListeners[i];
listener();
}
}
// 订阅状态的改变
function subscribe (listener) {
currentListeners.push(listener);
}
// 默认调用一次dispatch方法 存储初始状态(通过reducer函数传递的默认状态)
dispatch({
type: 'initAction'})
return {
getState,
dispatch,
subscribe
}
}
// 判断参数是否是对象类型
// 判断对象的当前原型对象是否和顶层原型对象相同
function isPlainObject (obj) {
if (typeof obj !== 'object' || obj === null) return false;
var proto = obj;
while (Object.getPrototypeOf(proto) != null) {
proto = Object.getPrototypeOf(proto)
}
return Object.getPrototypeOf(obj) === proto;
}
function applyMiddleware (...middlewares) {
return function (createStore) {
return function (reducer, preloadedState) {
// 创建 store
var store = createStore(reducer, preloadedState);
// 阉割版的 store
var middlewareAPI = {
getState: store.getState,
dispatch: store.dispatch
}
// 调用中间件的第一层函数 传递阉割版的store对象
var chain = middlewares.map(middleware => middleware(middlewareAPI));
var dispatch = compose(...chain)(store.dispatch);
return {
...store,
dispatch
}
}
}
}
function compose () {
var funcs = [...arguments];
return function (dispatch) {
for (var i = funcs.length - 1; i >= 0; i--) {
dispatch = funcs[i](dispatch);
}
return dispatch;
}
}
function bindActionCreators (actionCreators, dispatch) {
var boundActionCreators = {
};
for (var key in actionCreators) {
(function (key) {
boundActionCreators[key] = function () {
dispatch(actionCreators[key]())
}
})(key)
}
return boundActionCreators;
}
function combineReducers (reducers) {
// 1. 检查reducer类型 它必须是函数
var reducerKeys = Object.keys(reducers);
for (var i = 0; i < reducerKeys.length; i++) {
var key = reducerKeys[i];
if (typeof reducers[key] !== 'function') throw new Error('reducer必须是函数');
}
// 2. 调用一个一个的小的reducer 将每一个小的reducer中返回的状态存储在一个新的大的对象中
return function (state, action) {
var nextState = {
};
for (var i = 0; i < reducerKeys.length; i++) {
var key = reducerKeys[i];
var reducer = reducers[key];
var previousStateForKey = state[key];
nextState[key] = reducer(previousStateForKey, action)
}
return nextState;
}
}
测试自定义的html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button id="increment">+1</button>
<span id="box">0</span>
<button id="decrement">-1</button>
<script src="./myRedux.js"></script>
<script src="middlewares/logger.js"></script>
<script src="middlewares/thunk.js"></script>
<script>
function counterReducer(state, action) {
switch (action.type) {
case "increment":
return state + 1;
case "decrement":
return state - 1;
default:
return state;
}
}
function enhancer (createStore) {
return function (reducer, preloadedState) {
var store = createStore(reducer, preloadedState);
var dispatch = store.dispatch;
function _dispatch (action) {
if (typeof action === 'function') {
return action(dispatch)
}
dispatch(action);
}
return {
...store,
dispatch: _dispatch
}
}
}
var rootReducer = combineReducers({
counter: counterReducer})
var store = createStore(rootReducer, {
counter: 100}, applyMiddleware(logger, thunk));
store.subscribe(function () {
document.getElementById("box").innerHTML = store.getState().counter;
});
var actions = bindActionCreators({
increment, decrement}, store.dispatch);
function increment () {
return {
type: "increment"}
}
function decrement () {
return {
type: "decrement"};
}
document.getElementById("increment").onclick = function () {
// logger -> thunk -> reducer
// store.dispatch({ type: "increment" });
actions.increment()
};
document.getElementById("decrement").onclick = function () {
// store.dispatch({ type: "decrement" });
actions.decrement()
};
</script>
</body>
</html>