vuex你真的学会了吗?

502 阅读3分钟

前言:

在SPA单页面组件的开发中 Vue的vuex和React的Redux都统称为同一状态管理,个人的理解是全局状态管理更合适;简单的理解就是你在state中定义了一个数据之后,你可以在所在项目中的任何一个组件里进行获取、进行修改,并且你的修改可以得到全局的响应变更。

一、安装

`npm install vuex --save`  or  `yarn add vuex`

二、使用

创建文件src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

const store = new Vuex.Store();

export default store

main.js中引入store,全局注入store,这样就可以在每个组件里面使用this.$store

import store from './store'

new Vue({
    el: "#app",
    store, // 注入store
    router, // 路由
    templat:"<App/>",
    component:{
        App
    }
})

store/index.js里面,我们先声明一个state变量,并赋值一个空对象给它,里面随便定义两个初始属性值;然后再在实例化的Vuex.Store里面传入一个空对象,并把刚声明的变量stater放到里面:

1. state

import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
 const state = { 
    //要设置的全局访问的state对象
     showFooter: true,
     changableNum:0
     //要设置的初始属性值
   };
 const store = new Vuex.Store({
       state
    });
 
export default store;

这时候可以用this.$store.state.showFooterthis.$store.state.changebleNum在任何一个组件里面获取showfooterchangebleNum定义的值,但这不是理想的获取方式;vuex官方API提供了一个getters,和vue计算属性computed一样,来实时监听state值的变化(最新状态),并把它也放进Vuex.Store里面。

2. getters

import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
 const state = {   
 //要设置的全局访问的state对象
     showFooter: true,
     changableNum:0
     //要设置的初始属性值
   };
const getters = {   
    //实时监听state值的变化(最新状态)
    isShow(state) {  
    //方法名随意,主要是来承载变化的showFooter的值
       return state.showFooter
    },
    getChangedNum(){  
    //方法名随意,主要是用来承载变化的changableNum的值
       return state.changebleNum
    }
};
const store = new Vuex.Store({
       state,
       getters
});
export default store;

定义的state的初始值,想要改变他的值,接下来要说的就是mutationsmutattions也是一个对象,这个对象里面可以改变state的初始值的方法,具体的用法就是给里面的方法传入参数state或额外的参数,然后利用vue的双向数据驱动进行值的改变。

3. mutations

import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
 const state = {   
 //要设置的全局访问的state对象
     showFooter: true,
     changableNum:0
     //要设置的初始属性值
   };
const getters = {   
//实时监听state值的变化(最新状态)
    isShow(state) {  
    //承载变化的showFooter的值
       return state.showFooter
    },
    getChangedNum(){  
    //承载变化的changebleNum的值
       return state.changableNum
    }
};
const mutations = {
    show(state) {   
    //自定义改变state初始值的方法,这里面的参数除了state之外还可以再传额外的参数(变量或对象);
        state.showFooter = true;
    },
    hide(state) {  
    //同上
        state.showFooter = false;
    },
    newNum(state,sum){ 
    //同上,这里面的参数除了state之外还传了需要增加的值sum
       state.changableNum+=sum;
    }
};
 const store = new Vuex.Store({
       state,
       getters,
       mutations
});
export default store;

通过this.$store.commit('show')this.$store.commit('hide')以及this.$store.commit('newNum',6) 在别的组件里面进行改变showfooterchangebleNum的值了,但这不是理想的改变值的方式;因为在 Vuex 中,mutations里面的方法都是同步事务,意思就是说:比如这里的一个this.$store.commit('newNum',sum)方法,两个组件里用执行得到的值,每次都是一样的,这样肯定不是理想的需求

vuex官方API还提供了一个actions,这个actions也是个对象变量,最大的作用就是里面的Action方法 可以包含任意异步操作,这里面的方法是用来异步触发mutations里的方法,actions里自定义的函数接收一个context参数和要变化的形参,context与store实例具有相同的方法和属性,所以它可以执行context.commit('')

4. actions

import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
 const state = {   
 //要设置的全局访问的state对象
     showFooter: true,
     changableNum:0
     //要设置的初始属性值
   };
const getters = {   
//实时监听state值的变化(最新状态)
    isShow(state) {  
    //承载变化的showFooter的值
       return state.showFooter
    },
    getChangedNum(){  
    //承载变化的changebleNum的值
       return state.changableNum
    }
};
const mutations = {
    show(state) {   
    //自定义改变state初始值的方法,这里面的参数除了state之外还可以再传额外的参数(变量或对象);
        state.showFooter = true;
    },
    hide(state) {  
    //同上
        state.showFooter = false;
    },
    newNum(state,sum){ 
    //同上,这里面的参数除了state之外还传了需要增加的值sum
       state.changableNum+=sum;
    }
};
const actions = {
    hideFooter(context) {  
    //自定义触发mutations里函数的方法,context与store 实例具有相同方法和属性
        context.commit('hide');
    },
    showFooter(context) {  
    //同上注释
        context.commit('show');
    },
    getNewNum(context,num){   
    //同上注释,num为要变化的形参
        context.commit('newNum',num)
     }
};
 const store = new Vuex.Store({
       state,
       getters,
       mutations,
       actions
});
export default store;

其他组件里进行全局执行actions里面方法的时候,你只需要用执行

this.$store.dispatch('hideFooter')this.$store.dispatch('showFooter')以及this.$store.dispatch('getNewNum',6) //6要变化的实参

// 例:
<template>
  <div id="app">
    <router-view/>
    <FooterBar v-if="isShow" />
  </div>
</template>
<script>
import FooterBar from '@/components/common/FooterBar'
import config from './config/index'
export default {
  name: 'App',
  components:{
    FooterBar
  },
  data(){
    return {
    }
  },
  computed:{
     isShow(){
       return this.$store.getters.isShow;
     }
  },
  watch:{
      $route(to,from){ 
          //跳转组件页面后,监听路由参数中对应的当前页面以及上一个页面
          console.log(to)
        if(to.name == 'book'|| to.name =='my'){ 
            //  to.name来获取当前所显示的页面,从而控制该显示或隐藏footerBar组件
           this.$store.dispatch('showFooter') 
           // 利用派发全局state.showFooter的值来控制        
           }else{
           this.$store.dispatch('hideFooter')
        }
      }
  }
}
</script>

5. module

modules 模块化 以及 组件中引入 mapGetters、mapActions 和 mapStates的使用

因为在大多数的项目中,我们对于全局状态的管理并不仅仅一种情况的需求,有时有多方面的需求,比如写一个商城项目,你所用到的全局state可能是关于购物车这一块儿的也有可能是关于商品价格这一块儿的;像这样的情况我们就要考虑使用vuex中的 modules 模块化了。

// 文件可以改写
import Vue from 'vue';
import Vuex from 'vuex';
import footerStatus from './modules/footerStatus'
import collection from './modules/collection'
Vue.use(Vuex);

export default new Vuex.Store({
    modules:{
         footerStatus,
         collection
    }
});
<template>
  <div id="app">
    <router-view/>
    <FooterBar v-if="isShow" />
    <p>{{this.$store.state.footerStatus.showFooter}}</p>
  </div>
</template>
 
<script>
import {mapState,mapGetters,mapActions} from 'vuex'; //先要引入
import FooterBar from '@/components/common/FooterBar'
import config from './config/index'
export default {
  name: 'App',
  components:{
    FooterBar:FooterBar
  },
  data(){
    return {
    }
  },
  computed:{
    ...mapState({  
        //这里的...是超引用,ES6的语法,意思是state里有多少属性值我可以在这里放多少属性值
         isShow:state => state.footerStatus.showFooter
         //注意这些与上面的区别就是state.footerStatus,
        //里面定义的showFooter是指footerStatus.js里state的showFooter
      }),
  },
  watch:{
      $route(to,from){
        if(to.name=='book'||to.name=='my'){
           this.$store.dispatch('footerStatus/showFooter') 
           // 这里改为'footerStatus/showFooter',
           // 意思是指footerStatus.js里actions里的showFooter方法
        }else{
           this.$store.dispatch('footerStatus/hideFooter') 
           //同上注释
        }
      }
  }
}
</script>

总结:

自我实践下来,如果要用好vuex,需要掌握以下的几点:

  1. 组件数据共享,跨页面数据共享,可以统一管理数据的存储,操作,分发。比如用户数据,比如固定的某些数据是固定某些api获取的且不止用于一个组件或者一个场景;
  2. 不要形式主义,每个页面都用vuex;每个模块都写getters,actions,常量方法,当你的工程量、数据量达到使用某技术场景的时候,采用某方案会觉得恰如其分;
  3. 处理基于数据的业务逻辑,一般是跨页面跨组件的,比如购买流程对用户余额,购物车,订单的联动影响;
  4. vuex核心的index做一些模块公用的存储工具,可以配置一些需要的插件或者工具类;
  5. 拓展:数据通讯不止vuex,简单的也可以用event bus,甚至页面内的已经能符合你的需求了;