从零实现redux及中间件思想

一、为什么要用redux

react和vue都是单向数据流去管理状态。而两个非父子组件之间通信就相对麻烦,特别是跨多级组件之间的通讯。所以两个框架都有相应的状态管理器,如redux vuex mobx等优秀框架。而其中大致的设计思想就是发布订阅模式,通过管理容器存储需要共享的数据,中央管理,多处派发,解决了跨级通讯的痛点。看了redux源码后,体悟很深,自己动手写了一个。
参考文章:
https://www.jianshu.com/p/e984206553c2
https://github.com/bricksert/blog/issues/22

二、什么时候用redux

阮一峰老师的博客Redux 入门教程(一)里面这样说:
不需要使用 Redux的场景:
用户的使用方式非常简单
用户之间没有协作
不需要与服务器大量交互,也没有使用 WebSocket
视图层(View)只从单一来源获取数据
需要使用redux的场景:
用户的使用方式复杂
不同身份的用户有不同的使用方式(比如普通用户和管理员)
多个用户之间可以协作
与服务器大量交互,或者使用了WebSocket
View要从多个来源获取数据

三.具体实现

版本一:发布订阅模式

counterReducer = function (state, action) {
    switch (action.type) {
        case 'INCREAMENT':
            return {
                ...state,
                count: state.count + 1
            };
        case 'DECREAMENT':
            return {
                ...state,
                count: state.count - 1
            };
        default:   // 没有匹配的action,则返回原来的state
            return state
    }
}
const createStore = function (reducer, initState) {
    let listeners = [];
    state = initState;
    function subscribe(listener) { 
        listeners.push(listener);
    }
    function dispatch(action) {
        state = reducer(state, action);
        listeners.forEach(listener => {  // 执行所有subscrible的回调方法
            listener();
        })
    }
    function getState() {
        return state;
    }
    return {
        subscribe,
        dispatch,
        getState
    }
}

1.对用到state的地方进行状态监听
2.对action.type进行约束,如果reducer中有对应,则进行修改,如果没有则不修改

// 使用redux_v1
const store = createStore(counterReducer,{
    count: 20
})
store.subscribe(() => {
    console.log(store.getState())
})
store.dispatch({
    type: 'INCREAMENT'
})
store.dispatch({   // dispatch一个不存在的action,state并不会改变
    type: 'SET_PRICE',
    price: 111
})

对于版本一来说,reducer 是一个计划函数,接收老的 state,按计划返回新的 state。而项目中肯定有多个state, 每个state都需要对应的reducer。并且在创建store时也不可能将所有的state写在一起。我们必须将reducer和state进行拆分,随时可以添加新的state和reducer。

版本二:拆分reducer和state

const bookInit = {
    name: '小葵花妈妈课堂',
    price: 20
}
function bookReducer(state, action) {
    if (!state) {  // 若不存在,则将state赋值
        state = bookInit
    }
    switch (action.type) {
        case 'SET_NAME':
            return {
                ...state,
                name: action.name
            };
        case 'SET_PRICE':
            return {
                ...state,
                price: action.price
            };
        default:
            return state
    }
}
const counterInit = {
    count: 10
}
function counterReducer(state, action) {
    if (!state) {
        state = counterInit
    }
    switch (action.type) {
        case 'INCREAMENT':
            return {
                ...state,
                count: state.count + 1
            };
        case 'DECREAMENT':
            return {
                ...state,
                count: state.count - 1
            };
        default:
            return state
    }
}
function combineReducer(reducers) {  // 拆分redux核心
    const reducerKeys = Object.keys(reducers);
    return function combination(state, action) {
        let nextState = {}
        for (let i = 0; i < reducerKeys.length; i++) {
            let key = reducerKeys[i];
            let reducer = reducers[key];
            let prevState = state[key];
            nextState[key] = reducer(prevState, action);
        }
        return nextState;
    }
}
const createStore = function (reducer, initState = {}) {
    let listeners = [];
    state = initState;
    function subscribe(listener) {
      	  listeners.push(listener);
	      return function unsubscribe() {  // 退订
	      const index = listeners.indexOf(listener)
	      listeners.splice(index, 1)
	    }
    }
    function dispatch(action) {
        state = reducer(state, action);
        listeners.forEach(listener => {
            listener();
        })
    }
    function getState() {
        return state;
    } 
    dispatch({  // 在创建store是dispatch一个不匹配任何 type 的 action。其目的是获取初始值
        type: Symbol() // 标识唯一性
    })
    return {
        subscribe,
        dispatch,
        getState
    }
}

这样我们就实现了一个七七八八的redux,其中combineReducer是核心实现。可以仔细看看。

const reducer = combineReducer({
    counter: counterReducer,
    book: bookReducer
})
const store = createStore(reducer)  // 不给初始值,让其获取
unsubscribe = store.subscribe(() => {
    console.log(store.getState())
})
store.dispatch({
    type: 'INCREAMENT'
})
unsubscribe() // 退订后下面的dispatch不会执行回调
store.dispatch({
    type: 'SET_PRICE',
    price: 200
})

四.中间件思想

什么是中间件?
个人认为中间件就是程序中可织入的,可重用的,与业务逻辑无关的组件。是用来提供额外功能的。

比如现在有一个需求,在每次修改 state 的时候,记录下来 修改前的 state ,为什么修改了,以及修改后的 state。实现一个日志组件,直接上代码

const reducer = combineReducer({
    counter: counterReducer,
    book: bookReducer
})
const store = createStore(reducer) // 不给初始值
unsubscribe = store.subscribe(() => {
   // todo
})
const next = store.dispatch;
store.dispatch = function (action) {
    console.log('action:',JSON.stringify(action), )
    console.log('修改前:',store.getState(), )
    next(action);
    console.log('修改后:',store.getState(), )
    console.log('---------------------------------------')
}
store.dispatch({
    type: 'INCREAMENT'
})

store.dispatch({
    type: 'SET_PRICE',
    price: 18
})

在这里插入图片描述

发布了51 篇原创文章 · 获赞 18 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/weixin_42565137/article/details/103538562