手写一个基本功能完全的Redux

577 阅读4分钟

Redux作为react的状态管理器,从组件中把状态抽离出来,统一封装到Redux,实现了UI与逻辑耦合分离。Redux的实现理念,有很多值得我们的学习,例如函数的柯林化,洋葱模型,以及数组方法reduce的巧用。

只有明白了Redux的设计理念,不论经过多长时间,都能手写出一个Redux,同样的,可以把其可借鉴的点运用到平时写的代码当中。

  1. 初始化一个Redux。
    function CreateStore() {
        let state;
        function dispatch() {

        }
        function getState() {

        }
        function subscrible() {

        }
        return {
            dispatch,
            getState,
            subscrible
        }
    }
  1. 丰富各项功能。
    function CreateStore(reducer) {
        let state;
        let listener = [];
        // 发起action,触发reducer
        function dispatch(action) {
            // 通过action去匹配reducer中对应的操作,并且返回state
            state = reducer(state, action)
            return action;
        }
        // 初始化reducer,返回默认state
        dispatch({ type: 'init'})

        function getState() {
            return state;
        }
        function subscrible(listner) {
            listener.push(listner);
        }
        return {
            dispatch,
            getState,
            subscrible
        }
    }

至此,一个最最简单的redux就完成了,接下来,我们看一看在实现一个reducer上需要有哪些注意点。

// 初始化一个reducer时,需要给一个默认值
// reducer 必须是一个纯函数
function countReducer(state={ num: 0 }, action) {
    let { type } = action;
    switch (type) {
        case 'add': 
            return {
                ...state,
                num: ++state.num
            }
        case 'subtract': 
            return {
                ...state,
                num: --state.num
            }
        default : 
            return {
                ...state,  
            }
    }
}

接着我们把countReducer传入到CreateStore当中进行使用。

const store = new CreateStore(countReducer);
store.dispatch({
        type: 'add'
})
store.getState() // { num: 1 }
store.dispatch({
        type: 'subtract'
})
store.getState() // { num: 0 }
  1. 实现combineReducers。 在实际业务需求中,reducers当然不止一个,但是CreateStore中第一个参数只能接受一个函数。那就需要把多个reducers合并成一个大的reducer,接下来实现combineReducers。
    function combineReducers(reducers) {
        const reducersKey = object.keys(reducers);
        return (state = {}, action) => {
            let nextState = {};
            reducersKey.forEach(key => {
                nextState[key] = reducers[key](state[key], action);
            })
            return nextState;
        }
    }

还可以这样写

    function combineReducers(reducers) {
        const reducersKey = Object.keys(reducers);
        return (state = {}, action) => {
            return reducersKey.reduce((prev, curr) => {
                prev[curr] = reducers[curr](state[curr], action);
                return prev;
            }, {})
        }
    }

combineReducers把单个单个的reducer合成了一颗大树,每当发起action时,从顶部向下执行到每个reducer,也就是说,当我发起一个type为add action时,counterReducer中的type会被匹配,其他的子reducer也会跟着执行,只不过这些reducer中没有匹配的type,返回默认的state。

再实现一个reducer

function ageModifyReducer(state={ name: 'Joe', age: 18 }, action) {
    let { type } = action;
    switch (type) {
        case 'name': 
            return {
                ...state,
                name: action.name
            }
        case 'age': 
            return {
                ...state,
                age: action.age
            }
        default : 
            return {
                ...state,  
            }
    }
}

let reducer = combineReducers({
    ageModifyReducer,
    countReducer
})
let store = CreateStore(reducer);
store.getState(); // { ageModifyReducer: { name: "Joe", age: 18 }, countReducer: { num: 0 }}

// 接下来修改年龄
store.dispatch({
    type: 'name',
    name: 'Mike'
})
store.getState(); // { ageModifyReducer: { name: "Mike", age: 18 }, countReducer: { num: 0 }}

到这一步,能进行状态管理的redux就算完成了,接下来,解析中间件,中间件这里,刚开始时我是傻傻的,后来时隔一段时间再回去细细品味,还是恶心🤢,但是现在大概能掌握了其实现的理念。

  1. 我们先从creatStore改造入手
function CreateStore(reducer, initialState, enhancer) {
    let state;
    let listener = [];
    // 如果传进来的第二个参数是中间件,内部需要做处理,把中间件给到enhancer
    if (initialState && typeof initialState === 'function') {
        enhancer = initialState;
        initialState = undefined;
    }
    // 如果传入了中间件 ,那么重新构造一个store, 并且以下逻辑不走
    if(enhancer && typeof enhancer === 'function') {
        return enhancer(CreateStore)(reducer, initialState);
    }
    // 以下逻辑不走了

    // 发起action,触发reducer
    function dispatch(action) {
        // 通过action去匹配reducer中对应的操作,并且返回state
        state = reducer(state, action)
        return action;
    }
    // 初始化reducer,返回默认state
    dispatch({ type: 'init'})

    function getState() {
        return state;
    }
    function subscrible(listner) {
        listener.push(listner);
    }
    return {
        dispatch,
        getState,
        subscrible
    }
}
  1. 然后实现一个applyMiddlewares
// 首先CreateStore中,我们看见当我们执行applyMiddlewares需要传两次参数
if(enhancer && typeof enhancer === 'function') {
        return enhancer(CreateStore)(reducer, initialState);
}
function applyMiddlewares(...middlerWares) {
    return (store) => (reducer, initialState) {
        const store = store(reducer, initialState);
        // 这里其实在中间件中,用户手动dipatch一个action,是会报错的
        let dispatch = () => {
            throw Error('不能在此执行dispatch')
        }
        const middleAPI = {
            getState: store.getState,
            dispatch : (...agrs) => dispatch(...agrs) 
        }
        const chain = middlerWares.map(middleWare => middleWare(middleAPI));
        dispath = compose(...chain)(store.dispatch);
        return {
            ...store,
            dispatch
        }
    }
}

通过applyMiddlewares返回的dispatch函数,不是真正意义上的dispatch函数,因为在Redux中,发起dispatch之后就会立即执行reducer。但在这里不是,真正触发dispatch函数的是最里层的中间件。所以在组合中间件过程中还需要注意顺序,接着来剖析中间件。 假设我们现在有一个中间件logger。连续使用两次

    function logger() {
        return (next) => (action) => {
            let returnValue = next(action);
            return returnValue;
        }
    }
    applyMiddlewares(logger, logger);
    // 看看 applyMiddlewares之后返回的dispatch长什么样
    disaptch = (action) => {
        // returnValue就是action
        let returnValue = ((action) => {
            let returnValue = store.dispatch(action);
            return returnValue;
        })(action);
        returnValue;
    }

当我们在外部发起store.dispatch()时候,就是执行上面的dispatch函数,层层向里执行,然后有层层向外执行,所谓的洋葱模型。

以上就是我对Redux的学习结果,欢迎大家阅读,如有不对地方,请联系我指出,感谢各位的观看。