如何在Vue里实现一个Redux状态管理?

栏目: IOS · Android · 发布时间: 6年前

内容简介:嗯,我们都知道最近在学习不同的是,我们是在

嗯,我们都知道 redux 通常是 react 项目中 中一种管理数据的手段,它跟我们 vue 项目里的 Vuex 状态管理类似,功能相同,但是使用方法却有不同~

最近在学习 redux 这一块,为了更好的帮助跟我一样的萌新更加深入的了解 redux 的内部原理,我们就来试试,手动实现一个简单的 redux 状态管理。

不同的是,我们是在 vue 项目中来实现的~

代码未动,目标先行

我们要实现 redux ,首先要给自己定个小目标,也就是一个业务场景,我们呀,也别想那么复杂,就以最简单的还是最经典的计数器开始,如图:

如何在Vue里实现一个Redux状态管理?

看图,大家就应该很清楚做什么,当我点击 加号 按钮,希望仓库里的数据能增 1,点击 减号 按钮,希望仓库里的数据能减 1 ,嗯,很明确的需求,我们就来看看怎么实现吧~

具体实现

  • redux创建

首先我们需要在 src 目录下创建一个 redux 目录,用来放我们 redux 的核心代码,其中最核心的代码就是 createStore 方法了,因为我们在初始化 store 的时候就是通过这个方法进行创建的, redux 源码里它会返回 dispatchsubscribegetStatereplaceReducer 四个方法,但是实际上我们通常用到的就是前三个了~

ok,我们接下来慢慢的来实现~

redux 目录下创建一个 index.js ,代码如下:

// index.js

function createStore () {
    let state ;
    
    //分发action
    let dispatch = ()=>{
    
    }
    
    //订阅数据更新
    let subscribe = ()=> {
    
    }
    
    //获取store的状态
    let getState = ()=>{
    
    }
    
    return{
    	dispatch ,
    	subscribe ,
    	getState
    }

}

export {
    createStore
}
复制代码

这样我们就有了一个 redux 雏形,接下来主要是完善里面的方法而已~

  • 初始化store

我们先把这个 store 挂载到 vue 的原型上,方便后面的使用,我们在 src 创建一个 store 文件夹,并新建一个 index.js ,代码如下:

import { createStore } from "../redux"

const store = createStore();

export default store;
复制代码
  • 挂载store

我们在入口文件 main.js 引入,并挂载即可:

import store from './store'

Vue.prototype.$store = store;
复制代码

接着,如果在页面中打印 this.$store ,出现如下结果,则证明呢已经挂载成功了~

如何在Vue里实现一个Redux状态管理?

但是此时,并没有什么实际的功能,因为我们并没有完善我们的 createStore 里方法~

  • 完善createStore

使用过 redux 的同学都知道,我们在初始化 store , 也就是调用 createStore 的时候是需要传递一个 reducers 参数的,用来写入更改状态的处理逻辑,我们这里以计数器为例,将计数器 的 reducer 作为入参~

这里我将计数器相关 actionsconstantsreducer 直接贴代码了,就不在赘述了~

constants counter.js

// 常量定义
    export const ADD = 'add';
    export const MINUS = 'minus'
复制代码

actions counter.js

import * as constants from '../constants/counter'

//增加
export const add = ()=>({
    type : constants.ADD
})

//减少
export const minus = ()=>({
    type : constants.MINUS
})

复制代码

reducers counter.js

import * as constants from '../constants/counter'

const defaultState = {
	count: 0
}

export default (state = defaultState, action) => {
    switch ( action.type ) {
    	case constants.ADD :
            return {
            	...state ,
            	count : state.count + 1
            }
    	case constants.MINUS :
            return {
                ...state ,
                count : state.count - 1
            }
    	default :
            return  state;
    }
}

复制代码

dispatch

我们期望,我们点击加号的时候,会派发一个 addaction ,然后对应的 reducer 接收到 action 做对应的处理,减号按钮同理~

顺着这个思路,我们先来看看 dispatch 方法,需要接受一个参数 actionaction 必须是一个对象,必须包含一个 type 值,表明需要 action 的类型,然后我们可以通过传过来的 recuder 获取到新的状态,因此, dispatch 方法如下:

//分发action
let dispatch = (action)=>{
    //判断 action 的 type 值
    if( typeof action !== 'object') throw Error('Expected the action to be a object.');
    if( action.type === undefined ) throw Error('The action.type is not defined');
    //获取新的 state
    state = reducer(state, action);
}
复制代码

我们打印 state , 通过点击 加号 和 减号 按钮,可以看到 state 确实有变换,跟我们预期的一样~

如何在Vue里实现一个Redux状态管理?

getState

我们可以通过 getState() 方法获取当前 state ,因此该方法相对比较简单,如下~

//获取store的状态
let getState = () => state;
复制代码

这样我们在每次派发 action 的后,通过 getState 方法确实能获取到对应的状态~

但其实有个问题,我们初始获取 state 的时候会返回 undefined ,因为我们初始的时候只是定义了 state , 却没有赋值~

为了解决这个问题,我们可以在 初始化的时候 dispatch 一个 initaction ,这样会返回 reducer 中默认的 state ,即:

//初始化state
    dispatch({ type : '@@redux/INIT'});
    
    //获取store的状态
    let getState = () => state;
复制代码

subscribe

虽然我们现在通过按钮,在控制台打印出每次 count 的变化,但是并没有反馈到页面上,因为页面上我们的值并没有做更改~

为了监控数据变化后做对应的处理, redux 提供了一个叫 subscribe ,它的入参是一个函数,作用就是订阅数据变化,做对应的逻辑处理,返回一个函数,用来取消订阅,因此我们可以对 subscribe 函数做如下处理:

//订阅处理函数
let listeners = [];

//订阅数据更新
let subscribe = (fn)=> {
    listeners.push(fn);
    return ()=>{
        listeners = listeners.filter(listener => fn != listener );
    }
}
复制代码

注意,上面只是订阅,当我们数据变化的时候需要发布,即要循环 listeners 中的方法,依次执行,因此需要在 dispatch 方法的最后加上一句:

listeners.forEach(listener => listener());
复制代码

效果

接着我们来试试~

代码如下:

<template>
    <div class='wrapper'>
        <div class="">计数器:{{ number }}</div>
        <div class="btn-box">
            <button class="btn" @click="handleAddBtnClick">+</button>
            <button class="btn" @click="handleMinusBtnClick">-</button>
        </div>
        <div class="btn-box">
            <button class="btn" @click="handleRemoveListenerBtnClick">取消打印监听</button>
        </div>
    </div>
</template>

<script>
    import { add , minus } from '../actions/counter'
    export default {
        data(){
            return{
            	number : this.$store.getState().count ,
                consoleHandler : null
            }
        },
        methods : {
            //计数器加一
            handleAddBtnClick(){
                this.$store.dispatch(add())
            },
            //计数器减一
            handleMinusBtnClick(){
            	this.$store.dispatch(minus())
            },
            //取消打印事件监听
    		handleRemoveListenerBtnClick(){
                this.consoleHandler();
            }
        },
        mounted () {
            //数据更新事件
            this.$store.subscribe(()=>{
            	this.number = this.$store.getState().count;
            })
            //打印事件
            this.consoleHandler = this.$store.subscribe(()=>{
            	console.log('我的值发生了更改~')
            })
    	}
    }
</script>

<style scoped>
    .wrapper{
        padding: 30px 15px;
        text-align: center;
    }
    .btn-box{
        margin-top: 20px;
    }
    .btn{
        display: inline-block;
        font-size: 20px;
        min-width: 50px;
        text-align: center;
        margin: 0 10px;
    }
</style>

复制代码

我们在初始的时候 添加了两个 事件监听,一个用来更新 number 的值,另一个用来打印,我们每次数据变更都会触发这两个方法,当我们点击取消打印监听按钮的时候,之后的数据变化不会在触发打印操作~

自己动手试试~

combineReducers

到此,我们已经基本实现了一个 简单版 的 redux ,但是还不够完美,问题在于我们还差一个函数,叫 combineReducers , 它是用来合并多个 reducer 返回一个新的 reducer ,以此区分不同的状态~

我们在刚才的 redux 文件夹下的 index.js 添加这个方法,如下:

// 合并reducer
// key是新状态的命名空间 值是reducer,执行后会返回一个新的reducer
function combineReducers (reducers) {
    // 第二次调用reducer ,内部会自动的把第一次的状态传递给reducer
    return (state = {}, action) => {
    	// reducer默认要返回一个状态
    	let newState = {}
    	for (let key in reducers) {
            // 默认reducer俩参数 一个叫state,一个叫action
            let s = reducers[key](state[key], action);
            newState[key] = s;
    	}
    	return newState;
    }
}
复制代码

接着,将我们的 reducer 更换一下即可,这里就不在赘述了~

结语

通过上面的学习,我们手动实现了一个 redux ,虽然比较简单,核心代码很少,但起码功能是完善的,主要是学习一种编码的思想吧,我们可以在学习一些框架或者库的同时可以多去关注,它的内部实现,然后我们开始可以自己一步一步模仿一个简单的版本,后面在不停的拓展,希望和大家一起继续加油~

代码我也上传到 github 上了,有需要的小伙伴可以参考参考~


以上所述就是小编给大家介绍的《如何在Vue里实现一个Redux状态管理?》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

Beginning ARKit for iPhone and iPad

Beginning ARKit for iPhone and iPad

Wallace Wang / Apress / 2018-11-5 / USD 39.99

Explore how to use ARKit to create iOS apps and learn the basics of augmented reality while diving into ARKit specific topics. This book reveals how augmented reality allows you to view the screen on ......一起来看看 《Beginning ARKit for iPhone and iPad》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

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

RGB CMYK 互转工具