Vuex探讨第一站-小小重写

首先,Vuex有以下几个特征

一、主要特征

是一个对象

js 复制代码
# 咱们使用的时候 都是通过
new Vuex.Store({xxx})

看到.StoreVuex十有八九就是一个对象了

有两个属性

其一是上面说的Store,其二就是install

总结起来,Vuex是一个对象,包含了两个属性,Store类install方法

二、准备工作

通过vue-cli创建一个项目吧。项目结构反正我的是这样

项目地址已经放到github上了 有什么问题恳请各位大佬不吝赐教

三、分析流程

我们平常在Vue项目中使用,一般三步走。

第一:安装依赖

bash 复制代码
npm i vuex

第二:引入vuex包

js 复制代码
import Vuex from 'vuex'

第三:执行Vue.use()以及实例化

js 复制代码
new Vuex.Store({xxxx})

Vue.use(Vuex)

四、大体框架

首先vuex是一个对象,然后一个两个属性,一个Store,一个install

js 复制代码
class Store{
    
}

let install = function(){

}
let vuex = {
     Store,
     install
}

export defaul vuex

开始install方法

install是为Vue.use(vuex)服务的,不然平常咱们项目中,this.$store.state.xxthis.$store.getters.xx怎么来的

主要作用:咱们vue的每一个组件都可以拥有store实例

话不多说,直接开干

js 复制代码
let install = function(Vue){
    Vue.mixin({
        beforeCreate(){
            if(this.$options.store){
                this.$store = this.$options.store
            }else {
                this.$store = this.$parent && this.$parent.store
            }
        }
    })
}

解释下

首先入参有一个Vue,可能刚开始都不是很明白,这是Vue.use执行时带过来的。就是Vue的实例

其次mixin的作用是将mixin的内容混入到vue的options中

再者生命周期函数为什么选用beforeCreate,因为created的时候,options已经初始化好了,没必要了

最后,如果是根组件,直接将传入的store挂载到根组件上,属性名$store,如果不是,通过$parent来进行获取

五、咱们的state

本次咱们主要实现stategettermutationaction等,有了一个思路,可以举一反三

js 复制代码
class Store{
    constructor(options){
        this.state = options.state || {}
    }
}

定义store文件

js 复制代码
export default {
    state:{
        a:1
    }
}

Vue文件中使用

html 复制代码
<div>{{ this.$store.state.a }}</div>

接下来,启动项目,在浏览器中访问,就可以试试啦,一定要动手,不要觉得自己会了会了。

但是的但是,state是响应式的哦,所以还需要实现下state的响应式。

js 复制代码
class Store{
    constructor(options){
        this.vm = new Vue({
            data:{
                state:options.state || {}
            }
        })
    }
}

这样的话,咱们访问state数据就变成了this.$store.vm.state.xxx,但是我们平常访问的样子都是this.$store.state.xxx。我们需要稍微修改下

js 复制代码
class Store{
    constructor(options){
        this.vm = new Vue({
            data:{
                state:options.state || {}
            }
        })
    }
    
    // 新添加的
    get state(){
        return this.vm.state
    }
}

现在访问this.$store.state.xxx就可以了,也实现了响应式。

六、咱们的getter

话不多说,直接上代码

js 复制代码
class Store{
    constructor(options){
        this.vm = new Vue({
            data:{
                state:options.state || {}
            }
        })
        
        // 新添加的
        let getters = options.getters || {}
        this.getters = {}
        Object.keys(getters).forEach(item => {
            Object.defineProperty(this.getters, item, {
                get: () => {
                    return getters[item](this.state)
                }
            })
        })
    }
    
    get state(){
        return this.vm.state
    }
}

说白了,通过this.$store.getters.xx访问就是执行了getters对应的函数。所以,为什么访问getters时不需要带() ,原因就一行代码return getters[item](this.state),通过Object.definePropertyget属性来实现的

七、咱们的mutation

js 复制代码
class Store{
    constructor(options){
        this.vm = new Vue({
            data:{
                state:options.state || {}
            }
        })
        
        
        let getters = options.getters || {}
        this.getters = {}
        Object.keys(getters).forEach(item => {
            Object.defineProperty(this.getters, item, {
                get: () => {
                    return getters[item](this.state)
                }
            })
        })
        
        // 新添加的
        let mutations = options.mutations || {}
        this.mutations = {}
        Object.keys(mutations).forEach(item => {
            this.mutations[item] = (args) => {
                mutations[item](this.state, args)
            }
        })
    }
    
    get state(){
        return this.vm.state
    }
    
   // 新添加的
   commit(type,payload){
       this.mutations[type](payload)
   }
}

我们在平时的项目中通过什么来触发mutations中的方法

答案当然是commit。实现也比较简单,初始化时遍历传入进来的mutations,遍历给this.mutations赋值,存储类型以及对应的回调函数。通过this.$store.commit('xx',xx)来触发时,调用对应的函数即可。这里没有做类型不存在的处理。

八、咱们的action

js 复制代码
class Store{
    constructor(options){
        this.vm = new Vue({
            data:{
                state:options.state || {}
            }
        })
        
        
        let getters = options.getters || {}
        this.getters = {}
        Object.keys(getters).forEach(item => {
            Object.defineProperty(this.getters, item, {
                get: () => {
                    return getters[item](this.state)
                }
            })
        })
        
        
        let mutations = options.mutations || {}
        this.mutations = {}
        Object.keys(mutations).forEach(item => {
            this.mutations[item] = (args) => {
                mutations[item](this.state, args)
            }
        })
        
        // 新添加的
        let actions = options.actions || {}
        this.actions = {}
        Object.keys(actions).forEach(item => {
            this.actions[item] = (args) => {
                // 此处传的是this
                actions[item](this, args)
            }
        })
    }
    
    get state(){
        return this.vm.state
    }
    
    // 新添加的
    dispatch(type, payload) {
        this.actions[type](payload)
    }
   
    commit(type,payload){
       this.mutations[type](payload)
    }
}

还记得项目中的写法吗?

js 复制代码
actions:{

// { commit } 就是 store实例 所以上面要用this
// 反过来想 commit 是store的方法 所以你说自定义vuex是不是要传入this
incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

调用的话跟平常项目一样,通过dispatch方法来触发,this.$store.dispacth('xxx',xxx)

但是此时启动项目,调用的话会报错

即找不到mutations这个属性,那么问题来了,通过commit方法来进行调用是正常的,dispatch方法就不行了吗?十有八九是this指向问题。

有两种方法

第一种:改commit为箭头函数

js 复制代码
commit:(type,payload)=>{
    this.mutations[type](payload)
}

箭头函数想必大家都太清楚了,不清楚可以自行查阅。

第二种:绑定this

js 复制代码
constructor(){
    // ....
    
    const store = this
    const { commit } = this
    this.commit = function (type, payload) {
        // this指向绑定住了
        commit.call(store, type, payload)
    }
}

通过call方法把commit的this指向牢牢的绑定在store上,这样就不存在mutations报错的情况了。

九、总结

以上就是个人重写vuex的一些小小见解,有不足之处,请各位大佬及时指正。

相关推荐
萧大侠jdeps1 小时前
Vue 3 与 Tauri 集成开发跨端APP
前端·javascript·vue.js·tauri
JYeontu2 小时前
实现一个动态脱敏指令,输入时候显示真实数据,展示的时候进行脱敏
前端·javascript·vue.js
发呆的薇薇°2 小时前
react里使用Day.js显示时间
前端·javascript·react.js
嘤嘤嘤2 小时前
基于大模型技术构建的 GitHub Assistant
前端·github
KeepCatch2 小时前
CSS 动画与过渡效果
前端
跑跑快跑2 小时前
React vite + less
前端·react.js·less
web136885658712 小时前
ctfshow_web入门_命令执行_web29-web39
前端
GISer_Jing2 小时前
前端面试题合集(一)——HTML/CSS/Javascript/ES6
前端·javascript·html
清岚_lxn2 小时前
es6 字符串每隔几个中间插入一个逗号
前端·javascript·算法
胡西风_foxww2 小时前
【ES6复习笔记】Map(14)
前端·笔记·es6·map