手写Vuex原理,保证你看完自己也能动手写

如何简单、快速的理解vuex,花10分钟看看下面这篇文章你就知道了。话不多说,直接进入主题。

一、什么是vuex

我们要了解一门技术是什么,最直接的就是去它的官网看看。我们可以清晰的看到它是一个状态管理模式
那什么是状态管理模式呢?它是以相应的规则保证状态以一种可预测的方式发生变化。听起来很绕对吧,看完下面,你或许能有自己对vuex的理解。

~TD_$R)1S$0GF1VXPQZVO.png

二 、 为什么要用vuex

想象一下,当我们在做项目的时候,我们会创建很多的页面(组件/试图)。这个时候就涉及到了组件通信和页面之间的多级嵌套。此时我们就会遇到两个问题:

  • 多个试图依赖同一个状态
  • 不同的试图行为需要变更为同一个状态

对于页面嵌套我们可以用父子组件通信来解决。但是对于兄弟组件间的通信或其他更复杂的关系,这个就显得捉襟见肘了。那我们可不可以有一个类似仓库的东西,把各个组件都需要依赖的同一个状态抽取出来,在全局使用单例模式进行管理。在这种模式下,任何组件都可以直接访问到这个状态,或者当状态发生改变时,所有的组件都获得更新。

所以vuex诞生了,Vuex 是专门为 Vue 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新。

三 、 如何使用vuex

1. 安装

npm install vuex –save

2. 配置

先在src下创建store文件夹,然后在其下创建index.js,引入vuex,完成配置如下:

import Vue from 'vue'
import Vuex from 'vuex'


Vue.use(Vuex)

const store =  new Vuex.Store({
  state: {
   // 定义一个num,以供全局使用
   num:1,
   // 定义一个name,以供全局使用
   name: '小黄同学', 
   // 定义一个age,以供全局使用
   age: 18, 

  },
  mutations: {
  },
  actions: {
  },
  getters:{
  },
  modules: {
  }
})
export default  store
复制代码

然后在main.js 中引入,并挂载到vue实例上:

import Vue from 'vue'
import App from './App.vue'
import store from './store'  //引入store

Vue.config.productionTip = false

new Vue({
  store,     //挂载到vue实例上
  render: h => h(App)
}).$mount('#app')

复制代码

最后可以在组件中直接使用了

<template>
  <div id="app">
    <p>{{this.$store.state.name }}</p>
  </div>
</template>
复制代码

image.png

那对vuex的基本了解和基本使用我们已经知道了,接下来我们就要对vuex的原理进行剖析,在store文件夹下创建myStore.js.让我们的myStore.js具备和vuex 一样的作用。

  1. 首先我们对myStore.js的结构进行分析,它要new Vuex.Store(),说明Vuex.store是一个类。并且它可以Vue.use(Vuex),说明Vuex上具备inStall方法。我们就可以先写出Vuex的总体框架了。
import Vue from "vue"

class Store{
  constructor(){}
    
}

let Vuex = {
  Store,
  install
}

export default Vuex
复制代码

2. inStall函数

inStall函数的作用就是在Vue.use(Vuex)后,每个组件上都具有store方法,以此来达到任何组件都可以直接访问到这个状态

let install = function(Vue){
  Vue.mixin({//让每个组件都具有store方法
    beforeCreate() {
      if(this.$options && this.$options.store){  // $options获取根组件
        this.$store = this.$options.store
      }else{  //如果是子组件的话
        this.$store = this.$parent && this.$parent.$store  //去父组件上找
      }
    },
  })
}
复制代码

1. state

state作为一个“唯一数据源”而存在,而且它的数据在vuex中是响应式的,那么我们如何实现这种响应式呢,这时候我们就能想到 new Vue({})中的数据也是响应式的,state就可以重写出来了。

import Vue from "vue"

class Store{
  constructor(options){  //options 代表 new Vuex.store({}) 中的整个实例对象
    //state
    this.vm = new Vue({  
      data:{
        state : options.state || {}
      },
    })
  }
    
}

let Vuex = {
  Store,
  install
}

export default Vuex
复制代码

这样我们就能在组件中使用vuex里面的数据了,我们使用用自己写的myVuex.js,在组件中:

image.png

image.png

不过我们一般为了使用方便,会用代理

import Vue from "vue"

class Store{
  constructor(options){  //options 代表 new Vuex.store({}) 中的整个实例对象
    //state
    this.vm = new Vue({  
      data:{
        state : options.state || {}
      },
    })
  }
  
    get state(){  // 函数前面加get,当我们要取函数内部的返回值时,只需要写函数名state就可以
    return this.vm.state
  }    
}

let Vuex = {
  Store,
  install
}

export default Vuex
复制代码

就可以少写一层vm了
image.png

2. Getters

从state派生出一些状态,并进行计算和测量。Getters的实现原理就是先获取 Getters对象内所有的函数名,然后数据劫持forEach遍历每一个函数并调用掉。

  //getters
 let getters = options.getters || {} 
 this.getters = {}
 Object.keys(getters).forEach(getterName =>{//得到了所有函数的函数名
 Object.defineProperty(this.getters,getterName,{ 
      get:()=>{
        return  getters[getterName](this.state)  //返回并调用每一个函数
      }
    })
    })  
复制代码

在Getters里面写入一个函数:

import Vue from 'vue'
import Vuex from './myVuex.js'
Vue.use(Vuex)

const store =  new Vuex.Store({
  state: {
    num:1,
    name:'小黄同学',
    age:18
  },

  getters:{
    getNum(state){    
      return state.num * 10
    }
  },

})
export default  store
复制代码

在组件中使用:

image.png

image.png

3. Mutation

用于更改vuex中的状态,类似于事件 第一个参数为state,第二个参数为自己传入的值。并且想要在组件中调用Mutation的函数,要用commit调用。实现原理和Getters类似,但是可以不用数据劫持,直接重写函数即可:

//mutations
 let mutations = options.mutations || {}
 this.mutations = {}
 
 Object.keys(mutations).forEach(mutationName =>{
 this.mutations[mutationName] = (arg) =>{//将mutations中的函数重写成函数调用
    mutations[mutationName](this.state,arg)
     }
    })
    
  commit = (method,arg) =>{    //用commit开启函数的调用,要写成箭头函数,
   this.mutations[method](arg) // 不然后面Actions中会出现this执向错误
  }
复制代码
import Vue from 'vue'
import Vuex from './myVuex.js'

Vue.use(Vuex)

const store =  new Vuex.Store({
  state: {
    num:1,
    name:'小黄同学',
    age:18
  },
  mutations: {
    incre(state,arg){
      state.num += arg
    }
  },

})
复制代码

在组件中使用:点击后发生变化

image.png

Actions.gif

4.Actions

Action 类似于mutation ,Action 提交的是 mutation,而不是直接变更状态。用法同mutation基一致,并且可以执行异步操作,用dispatch调用:

    //actions
    let actions = options.actions || {}
    this.actions = {}
    Object.keys(actions).forEach(actionName =>{
      this.actions[actionName] = (arg) =>{
        actions[actionName](this,arg)
      }
    })
  }

  dispatch(method,arg ){
      this.actions[method](arg)
  }
复制代码

我们可以在组件中使用,可以看到,俩秒后才出现结果

image.png
555.gif

5. Module

将 store 分割成模块(module) 。每个模块拥有自己的 state、mutation、action、getter,防止store对象太大变得臃肿。
这个作者就不对其复写了。

关于vuex分享到这里就结束了,希望看完这篇文章能对你产生帮助。如果文章有不对或者是任何问题,欢迎点评和指出

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享