Vuex(状态管理器)

892 阅读6分钟

定义

Vuex为Vue.js应用程序开发的状态管理模式,采用集中式存储管理所有组件的公共状态,并以相应的规则保证状态以一种可预测的方法发生变化。

原理

Vue组件接收交互行为,调用dispatch方法触发action相关处理,若页面状态需要改变,则调用commit方法提交mutation修改state,通过getters获取到state新值,响应数据或状态给Vue 组件,界面随之更新。

使用场景:

  • 组件会被销毁
  • 组件基于数据而创建
  • 多对多事件--多处触发,影响多处
  • 涉及非父子组件之间跨组件共享数据
  • 多个组件依赖于同一状态时。
  • 来自不同组件的行为需要变更同一状态。

Vuex 核心功能:

State(Vuex的数据仓库)

Mutations(只能在mutations中修改state的数据状态)使用store.commit方法更改state存储的状态。

Actions(异步改变共享数据)

Getters(获取state二次处理的数据)

getters是store的计算属性,对state的加工,是派生出来的数据。就像computed计算属性一样,getter返回的值会根据它的依赖被缓存起来,且只有当它的依赖值发生改变才会被重新计算。

Module Module是store分割的模块,每个模块拥有自己的state、getters、mutations、actions。

Vuex框架的运行流程图:

  • Vue Components:Vue组件。HTML页面上,负责接收用户操作等交互行为,执行dispatch方法触发对应action进行回应。
  • dispatch:操作行为触发方法,是唯一能执行action的方法。
  • actions:操作行为处理模块。负责处理Vue - Components接收到的所有交互行为。包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发。向后台API请求的操作就在这个模块中进行,包括触发其他action以及提交mutation的操作。该模块提供了Promise的封装,以支持action的链式触发。
  • commit:状态改变提交操作方法。对mutation进行提交,是唯一能执行mutation的方法。
  • mutations:状态改变操作方法。是Vuex修改state的唯一推荐方法,其他修改方式在严格模式下将会报错。该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些hook暴露出来,以进行state的监控等。
  • state:页面状态管理容器对象。集中存储Vue components中data对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用Vue的细粒度数据响应机制来进行高效的状态更新。
  • getters:state对象读取方法。图中没有单独列出该模块,应该被包含在了render中,Vue Components通过该方法读取全局state对象。

Vuex中状态储存在哪里,怎么改变它?

存储在state中,改变Vuex中的状态的唯一途径就是显式地提交 (commit) mutation。

如何对mutation中的state改变状态

this.$store.dispatch()

dispatch: 含有异步操作,数据提交至 actions ,可用于向后台提交数据

  • 存储 this.$store.dispatch('getlists',name)
  • 取值 this.$store.getters.getlists
this.$store.commit()

commit: 同步操作,数据提交至 mutations ,可用于读取用户信息写到缓存里

  • 存储 this.$store.commit('changeValue',name)
  • 取值 this.$store.state.changeValue

Vuex中状态是对象时,使用时要注意什么?

因为对象是引用类型,复制后改变属性还是会影响原始数据,这样会改变state里面的状态,是不允许,所以先用深度克隆复制对象,再修改。

怎么在组件中批量使用Vuex的state状态?

使用mapState辅助函数, 利用对象展开运算符将state混入computed对象中

import {mapState} from 'vuex'
export default{
    computed:{
        ...mapState(['price','number'])
    }
}

vuex的store是如何挂载注入到组件中呢?

  • 利用vue的插件机制,使用Vue.use(vuex)时,会调用vuex的install方法,装载vuex
  • applyMixin方法使用vue混入机制,vue的生命周期beforeCreate钩子函数前混入vuexInit方法

vuex与全局变量的区别

  • vuex的状态存储是响应式的,当Vue组件从store中读取状态的时候,若store中的状态发生变化,那么相应的组件也会得到高效更新。

  • 不能直接改变store的变化,改变store中状态的唯一途径是commit mutation。方便于跟踪每一个状态的变化。

如何使用vuex

  • 开始使用vuex,新建一个 sotre文件夹,分开维护 actions mutations getters
  • 在store/index.js文件中新建vuex 的store实例
import Vue from 'vue'
import Vuex from 'vuex'
import * as getters from './getters' // 导入响应的模块,*相当于引入了这个组件下所有导出的事例
import * as actions from './actions'
import * as mutations from './mutations'
 
Vue.use(Vuex)
// 首先声明一个需要全局维护的状态 state,比如 我这里举例的resturantName
const state = {
    resturantName: '飞歌餐馆' // 默认值
    // id: xxx  如果还有全局状态也可以在这里添加
    // name:xxx
}
 
// 注册上面引入的各大模块
const store = new Vuex.Store({
    state,    // 共同维护的一个状态,state里面可以是很多个全局状态
    getters,  // 获取数据并渲染
    actions,  // 数据的异步操作
    mutations  // 处理数据的唯一途径,state的改变或赋值只能在这里
})
 
export default store  // 导出store并在 main.js中引用注册。
  • actions
// 给action注册事件处理函数。当这个函数被触发时候,将状态提交到mutations中处理
export function modifyAName({commit}, name) { // commit 提交;name即为点击后传递过来的参数,此时是 'A餐馆'
    return commit ('modifyAName', name)
}
export function modifyBName({commit}, name) {
    return commit ('modifyBName', name)
}
 
// ES6精简写法
// export const modifyAName = ({commit},name) => commit('modifyAName', name)
  • mutations
// 提交 mutations是更改Vuex状态的唯一合法方法
export const modifyAName = (state, name) => { // A组件点击更改餐馆名称为 A餐馆
    state.resturantName = name // 把方法传递过来的参数,赋值给state中的resturantName
}
export const modifyBName = (state, name) => { // B组件点击更改餐馆名称为 B餐馆
    state.resturantName = name
}
  • getters
// 获取最终的状态信息
export const resturantName = state => state.resturantName
  • 在main.js中导入 store实例
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
 
Vue.config.productionTip = false
 
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,  // 这样就能全局使用vuex了
  components: { App },
  template: '<App/>'
})

额外补充

如何实现vuex与组件data之间的数据同步更新
  • 使用computed属性去获取state中的数据

用 computed 去返回 state 中的数据,当 state 中的数据发生改变后,computed 会感知到,并重新获取 state 中的数据,并返回新的值。

<template>
 <div>{{userInfo}}</div> 
</template>
 
<script>
export default {
 computed: {
 userInfo(){
  return this.$store.state.userInfo;
 }
 }
};
</script>
  • 使用watch监听state中的数据

通过组件的 watch 属性,为 state 中的某一项数据添加一个监听,当数据发生改变的时候触发监听事件,在监听事件内部中去更改 data 中对应的数据,即可变相的让 data 中的数据去根据 state 中的数据发生改变而改变。

<template>
   <div>{{userInfo}}</div> 
</template>
 
<script>
export default {
  data() {
    return {
      userInfo: this.$store.state.userInfo;
   };
 },
 watch: {
    "this.$store.state.userInfo"() {
       this.userInfo = this.$store.getters.getUserInfo; // 使用getters来获取数据
 }
 }
};
</script>

Vuex的严格模式是什么,有什么作用,怎么开启?

在严格模式下,无论何时发生了状态变更且不是由mutation函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。

在Vuex.Store 构造器选项中开启,如下

const store = new Vuex.Store({
    strict:true,
})

手写vuex的原理

前提解析:

vue项目怎么引入vuex:

  • 安装vuex,通过import vuex from 'vuex'
  • 先 var store = new Vuex.Store({...}),再把store作为参数的一个属性值,new Vue({store})
  • 通过Vue.use(vuex)使得每个组件都可以拥有store实例
class Store {
    constructor(options){
       this.vm = new Vue ({
          data:{
             state:optiosn.state
          }
       })
       let getters = options.getter || {}
       this.getters = {}
       Object.keys(getters).forEach(getterName=>{
          Object.defineProperty(this.getters,getterName,{
            get:()=>{
               return getters[getterName](this.state)
            }
          })
       })
       let mutations = options.mutations || {}
       let mutations = {}
       Object.keys(mutations).forEach(mutationName=>{
          this.mutations[mutationName] = (arg)=>{
            mutations[mutationName](this.state,arg)
          }
       })
       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) 
       } 
// 修改代码 
       commit=(method,arg)=>{ 
          console.log(method); 
          console.log(this.mutations); 
          this.mutations[method](arg) 
       } 
       get state(){ 
          return this.vm.state 
       }       
    }

}
let install = function (){
     Vue.mixin({
         beforeCreate(){
            if(this.$options && this.$options.store){
               this.$store = this.$options.store
            }else {
               this.$store = this.$parent && this.$parent.$store
            }
         }
     })
}
let Vuex = {
    Store,
    install
}
export default Vuex