用最基础的方法讲解 Redux 实现原理

栏目: 服务器 · 发布时间: 5年前

内容简介:用最基础的方法讲解 Redux 实现原理?说白了其实是我能力有限,只能用最基础的方法来讲解,为了讲的更加清楚,文章可能比较拖沓。不过我相信,不是很了解 Redux 的同学,看完我今天分享的文章一定会有所收获!这不是我今天要说的重点,想知道什么是 Redux点击传送门在开始之前我想先讲一种常用的设计模式:观察者模式。先来说一下我对

用最基础的方法讲解 Redux 实现原理?说白了其实是我能力有限,只能用最基础的方法来讲解,为了讲的更加清楚,文章可能比较拖沓。不过我相信,不是很了解 Redux 的同学,看完我今天分享的文章一定会有所收获!

什么是 Redux ?

这不是我今天要说的重点,想知道什么是 Redux点击传送门

开始

在开始之前我想先讲一种常用的设计模式:观察者模式。先来说一下我对 观察者模式 的个人理解:观察者模式又称“发布-订阅(Publish/Subscribe)模式。对于这种模式很清楚的同学下面这段代码可以跳过。如果你还不清楚,你可以试着手敲一遍下面的代码!!

观察者模式

观察者模式,基于一个主题/事件通道,希望接收通知的对象(称为subscriber)通过自定义事件订 阅主题,通过deliver发布主题事件的方式被通知。就和用户订阅微信公众号道理一样,一个公众号可以被多个用户同时订阅,当公众号有新增内容时候,只要发布就好了,用户就能接收到最新的内容。

/**
 * describe: 实现一个观察者模式
 */
let data = {
    hero: '凤凰',
};
//用来储存 订阅者 的数组
let subscribers = [];
//订阅 添加订阅者 方法
const addSubscriber = function(fn) {
    subscribers.push(fn)
}
//发布
const deliver = function(name) {
    data.hero = name;
    //当数据发生改变,调用(通知)所有方法(订阅者)
    for(let i = 0; i<subscribers.length; i++){
        const fn = subscribers[i]
        fn()
    }
}
//通过 addSubscriber 发起订阅
addSubscriber(() => {
    console.log(data.hero)
})
//改变data,就会自动打印名称
deliver('发条') 
deliver('狐狸')
deliver('卡牌')
复制代码

这个发布订阅通过 addSubscriber 来储存订阅者(方法fn),当通过调用 deliver 来改变数据的时候,就会自动遍历 addSubscriber 来执行里面的 fn 方法 。

为啥要讲这个发布订阅模式呢?因为搞清楚了这个模式那么 Redux 的原理就会水到渠成。

Redux 起步

首先我们把上面那个发布订阅代码优化一下,顺便改一下命名,为什么要改命名?主要是紧跟 Redux 的步伐。让同学们更加眼熟。

let state = {hero: '凤凰'};
let subscribers = [];
//订阅 定义一个 subscribe 
const subscribe = (fn) => {
    subscribers.push(fn)
}
//发布
const dispatch = (name) => {
    state.hero = name;
    //当数据发生改变,调用(通知)所有方法(订阅者)
    subscribers.forEach(fn=>fn())
}
//通过 subscribe 发起订阅
subscribe(() => {
    console.log(state.hero)
})
//改变state状态,就会自动打印名称
//这里要注意的是,state状态不能直接去修改
dispatch('发条') 
dispatch('狐狸')
dispatch('卡牌')
复制代码

现在这样一改是不是很眼熟了,没错这就是一个类似redux改变状态的思路。但是光一个发布订阅还是不够的,不可能改变一个状态需要去定义这么多方法。所以我们把他封装起来。

creatStore 方法

const creatStore = (initState) => {
    let state = initState;
    let subscribers = [];
    //订阅 定义一个 subscribe 
    const subscribe = (fn) => {
        subscribers.push(fn)
    }
    //发布
    const dispatch = (currentState) => {
        state = currentState;
        //当数据发生改变,调用(通知)所有方法(订阅者)
        subscribers.forEach(fn=>fn())
    }
    // 这里需要添加这个获取 state 的方法
    const getState = () => {
        return state;
    }
    return {
        subscribe,
        dispatch,
        getState,
    }
}
复制代码

这样就创建好了一个 createStore 方法。没有什么新东西,就传进去一个初始状态,然后在返回 subscribe, dispatch, getState 三大方法。这里新增了个 getState 方法,代码很简单就是一个 return state 为了获取 state.

creatStore 使用

实现了 createStore 下面我们来试试如何使用他,那就拿那个非常经典的案例--计数器来试试

let initState = {
    num: 0,
}
const store = creatStore(initState);
//订阅
store.subscribe(() => {
    let state = store.getState()
    console.log(state.num)
})
// +1
store.dispatch({
   num: store.getState().num + 1
})
//-1
store.dispatch({
   num: store.getState().num - 1
})
复制代码

这个样子又接近了一点 Redux 的模样。 不过这样有个问题。如果你使用 store.dispatch 方法时,中间万一写错了或者传了个其他东西那就比较麻烦了。就比如下面这样:

用最基础的方法讲解 Redux 实现原理

其实我是想 +1,+1,-1 最后应该是 1 (初始 num 为0)!但是由于写错了一个导致后面的都会错。而且他还有个问题就是可以随便的给一个新的状态。那么就显得不那么单纯了。比如下面这样:

用最基础的方法讲解 Redux 实现原理

因为恶意修改 num 为 String 类型,导致后面在使用 dispatch 由于 num 不再是 Number 类型,导致打印出 NaN,这就不是我们想要的啦。所以我们要在改造一下,让 dispatch 变得单纯一些。那要怎么做呢?我们请一个管理者来帮我们管理,暂且给他命名 reducer

什么是 reducer

reducer 相当于是个管理者,然后我们每次想做什么就去通知管理者,让他在来根据我们说的去做。如果我们不小心说错了,那么他就不会去做。直接按默认的事情来。噔噔蹬蹬 reducer 登场!!

function reducer(state, action) {
    //通过传进来的 action.type 让管理者去匹配要做的事情
    switch (action.type){
        case 'add':
            return {
                ...state,
                count: state.count + 1
            }
        case 'minus':
            return {
                ...state,
                count: state.count - 1
            }
        // 没有匹配到的方法 就返回默认的值
        default:
            return state;
    }
}
复制代码

增加了这个管理者,那么我们就要重新来写一下之前的 createStroe 方法了:把 reducer 放进去

const creatStore = (reducer,initState) => {
    let state = initState;
    let subscribers = [];
    //订阅 定义一个 subscribe 
    const subscribe = (fn) => {
        subscribers.push(fn)
    }
    //发布
    const dispatch = (action) => {
        state = reducer(state,action);
        subscribers.forEach(fn=>fn())
    }
    const getState = () => {
        return state;
    }
    return {
        subscribe,
        dispatch,
        getState,
    }
}
复制代码

很简单的一个修改,为了让你们方便看出修改的地方,和区别,我特意重新码了这两个前后的方法对比,如下图

用最基础的方法讲解 Redux 实现原理

好,接下来我们试试添加了管理者的 creatStore 效果如何。

function reducer(state, action) {
    //通过传进来的 action.type 让管理者去匹配要做的事情
    switch (action.type){
        case 'add':
            return {
                ...state,
                num: state.num + 1
            }
        case 'minus':
            return {
                ...state,
                num: state.num - 1
            }
        // 没有匹配到的方法 就返回默认的值
        default:
            return state;
    }
}

let initState = {
    num: 0,
}
const store = creatStore(reducer,initState);
//订阅
store.subscribe(() => {
    let state = store.getState()
    console.log(state.num)
})
复制代码

为了看清楚结果,dispatch(订阅)我直接再控制台输出,如下图:

用最基础的方法讲解 Redux 实现原理

效果很好,我们不会再因为写错,而出现 NaN 或者其他不可描述的问题。现在这个 dispatch 比较纯粹了一点。

我们只是给他一个 type ,然后让管理者自己去帮我们处理如何更改状态。如果不小心写错,或者随便给个 type 那么管理者匹配不到那么这个动作那么我们这次 dispatch 就是无效的,会返回我们自己的默认 state。

好叻,现在这个样子基本上就是我脑海中第一次使用 redux 看到的样子。那个时候我使用起来都非常困难。当时勉强实现了一下这个计数器 demo 我就默默的关闭了 vs code。

接下来我们再完善一下这个 reducer,给他再添加一个方法。并且这次我们再给 state 一个

function reducer(state, action) {
    //通过传进来的 action.type 让管理者去匹配要做的事情
    switch (action.type){
        case 'add':
            return {
                ...state,
                num: state.num + 1
            }
        case 'minus':
            return {
                ...state,
                num: state.num - 1
            }
        // 增加一个可以传参的方法,让他更加灵活
        case 'changeNum':
            return {
                ...state,
                num: state.num + action.val
            }
        // 没有匹配到的方法 就返回默认的值
        default:
            return state;
    }
}

let initState = {
    num: 0,
}
const store = creatStore(reducer,initState);
//订阅
store.subscribe(() => {
    let state = store.getState()
    console.log(state.num)
})
复制代码

控制台再使用一次新的方法:

用最基础的方法讲解 Redux 实现原理

好叻,这样是不是就让 dispatch 更加灵活了。

现在我们再 reducer 中就写了 3 个方法,但是实际项目中,方法一定是很多的,那么都这样写下去,一定是不利于开发和维护的。那么这个问题就留给大家去思考一下。

提示:Redux 也知道这一点,所以他提供了 combineReducers 去实现这个模式。这是一个高阶 Reducer 的示例,他接收一个拆分后 reducer 函数组成的对象,返回一个新的 Reducer 函数。

思考完之后可以参考redux 中文文档 的combineReducers介绍

具体实现原理我将会在下次分享。

总结

Redux 这个项目里,有很多非常巧妙的方法,很多地方可以借鉴。毕竟这可是在 github 上又 47W+ 的 Star。

今天也只是讲了他的一小部分。自己也在努力学习中,希望今后能分享更多的看法,并和大家深入探讨。


以上所述就是小编给大家介绍的《用最基础的方法讲解 Redux 实现原理》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Pro CSS and HTML Design Patterns

Pro CSS and HTML Design Patterns

Michael Bowers / Apress / April 23, 2007 / $44.99

Design patterns have been used with great success in software programming. They improve productivity, creativity, and efficiency in web design and development, and they reduce code bloat and complexit......一起来看看 《Pro CSS and HTML Design Patterns》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具