vuex的使用

8,254 阅读3分钟

vuex.png

第一步:想要了解一个技术,先去查看他的官网,如(Vuex官网),映入眼帘的就是“vuex是什么”:

vuex是什么.png

如图所示,它是一个程序里面的状态管理模式,它是集中式存储所有组件的状态的仓库,并且保持我们存储的状态以一种可以预测的方式发生变化。

第一步,了解Vuex

想象一下

如果你的项目里有很多页面,页面之间存在多级的嵌套关系,此时,这些页面假如都需要共享一个状态的时候,此时就会产生以下两个问题:

  • 多个视图依赖同一个状态
  • 来自不同视图的行为需要变更同一个状态
我们简单的动动聪明的小脑袋就会有解决的方案(如下):
  • 对于第一个问题,假如是多级嵌套关系,可以使用父子组件传参进行解决,虽然可以解决,但是组件一旦多起来,会非常麻烦;对于兄弟组件或者关系更复杂组件之间,就很难办了,虽然可以通过各种各样的办法解决,可实在很不优雅,而且等项目做大了,代码就会堆成垃圾山,实在令人心烦。
  • 对于第二个问题,你可以通过父子组件直接引用,或者通过事件来变更或者同步状态的多份拷贝,这种模式很脆弱,往往使得代码难以维护,而且同样会让代码堆成垃圾山。
这时候,Vuex诞生了!

这就是 Vuex 背后的基本思想,借鉴了 Flux、Redux。与其他模式不同的是,Vuex 是专门为 Vue 设计的状态管理库,以利用 Vue.js 的细粒度数据响应机制来进行高效的状态更新

接着,你就会看到下面这张官网的vuex使用周期图

周期图.png

第二步,安装

可以通过 npm install -g @vue/cli -g 创建vue 项目的脚手架,在配置项目时加上Vuex,项目配置完后打开src路径下store文件夹里的index.js文件,文件内容如下:

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

Vue.use(Vuex); 
const store = new Vuex.Store({ 
    state: { 
    // 定义一个name,以供全局使用
        name: '张三', // 定义一个number,以供全局使用 number: 0, 
        // 定义一个list,以供全局使用
        list: [ 
            { id: 1, name: '111' }, 
            { id: 2, name: '222' }, 
            { id: 3, name: '333' },
        ], 
     },
});

export default store;

如果你想在项目的任意地方访问到仓库里的状态

<template> 
    <div></div>
</template>
<script> 
export default {
    mounted() {
        // 使用this.$store.state.XXX可以直接访问到仓库中的状态 
        console.log(this.$store.state.name); 
    }, 
}; 
</script>
  • 建议: 每次都写this.$store.state.XXX让你感到厌烦,实在不想写这个东西怎么办,解决方案就像下面这样:
<script> 
import { mapState } from 'vuex'; // 从vuex中导入mapState
export default {
    mounted() {
        console.log(this.name); 
    }, 
    computed: { 
        ...mapState(['name']), // 经过解构后,自动就添加到了计算属性中,此时就可以直接像访问计算属性一样访问它 
    }, 
};
</script>

第三步,了解如何修改值:Mutation

Mutation是vuex自带的修改方法,他修改的是store也就是自己的状态和数据。

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

Vue.use(Vuex); 
const store = new Vuex.Store({ 
    state: { 
        // 定义一个name,以供全局使用
        name: '张三',
        // 定义一个number,以供全局使用 
        number: 0, 
     },
     mutations: {
        setNumber(state) {
          // 增加一个mutations的方法,方法的作用是让num从0变成5,state是必须默认参数
          state.number = 5;
         }
    },
  },
});

以上是简单实现mutations的方法,是没有传参的,如果我们想传不固定的参数怎么办?如下:

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

Vue.use(Vuex); 
const store = new Vuex.Store({ 
    state: { 
        // 定义一个name,以供全局使用
        name: '张三',
        // 定义一个number,以供全局使用 
        number: 0, 
     },
     mutations: {
        setNumber(state,val) {
          // 增加一个mutations的方法,方法的作用是让num从0变成5,state是必须默认参数
          state.number += val;
         }
    },
  },
});

修改App.vue

<script>
export default {
  mounted() {
    console.log(`旧值:${this.$store.state.number}`);
    this.$store.commit('setNumberIsWhat', 666);
    console.log(`新值:${this.$store.state.number}`);
  },
};
</script>

就像最开始的mapState一样,我们在组件中可以使用mapMutations以代替this.$store.commit('XXX'),很方便吧?

<script>
import { mapMutations } from 'vuex';
export default {
  mounted() {
    this.setNumberIsWhat({ number: 999 });
  },
  methods: {
    // 注意,mapMutations是解构到methods里面的,而不是计算属性了
    ...mapMutations(['setNumberIsWhat']),
  },
};
</script>

此时可以得到和之前一样的效果,并且代码又美观了一点!

这里说一条重要原则:Mutations里面的函数必须是同步操作,不能包含异步操作!

这里说一条重要原则:Mutations里面的函数必须是同步操作,不能包含异步操作!

这里说一条重要原则:Mutations里面的函数必须是同步操作,不能包含异步操作!

第四步,了解异步操作:Actions。

既然有同步操作那异步操作也是不可缺的,同步操作放在有Mutation中,那谁来放异步操作呢,没错就是Actions。

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

Vue.use(Vuex); 
const store = new Vuex.Store({ 
    state: { 
        // 定义一个name,以供全局使用
        name: '张三',
        // 定义一个number,以供全局使用 
        number: 0, 
     },
     mutations: {
        setNumber(state,val) {
          // 增加一个mutations的方法,方法的作用是让num从0变成5,state是必须默认参数
          state.number += val;
         }
    },
      actions: {
    setNumberAction({ commit }, val) {
      return new Promise((res, rej) => {
        setTimeout(() => {
        // 我们模拟一个异步操作,1秒后修改number为888
        commit('setNumber', { number: 888 });
        }, 3000);
      });
    },
  },
  },
});

看了例子,是不是明白了,action就是去提交mutation的,什么异步操作都在action中消化了,最后再去提交mutation的。

  actions: {
    setNum({ commit }, val) {
      // 增加payload参数
      return new Promise(resolve => {
        setTimeout(() => {
          commit('setNumber', { number: val.number });
          resolve();
        }, 1000);
      });
    },
  },
    

修改App.vue

<script>
import { mapActions } from 'vuex';
export default {
  methods: {
    ...mapActions(['setNum']), // 就像这样,解构到methods中
  },

};
</script>

第五步,按功能进行拆分 - Module

接下来,我们介绍的是按功能拆分我们的store’,按功能拆分的话,就是我们的标题 Module(模块)  。

我们先来看下官方文档是怎么介绍Module的:

module.png

  1. 我们在之前的store上,增加一个新的仓库store2,主要代码如下:
// store2.js

const store2 = {
  state: {
    name: '我是store2',
  },
  mutations: {},
  getters: {},
  actions: {},
};

export default store2;
  1. 然后在store中引入我们新创建的store2模块:
import Vue from 'vue';
import Vuex from 'vuex';
import { state } from './state';
import { getters } from './getters';
import { mutations } from './mutations';
import { actions } from './actions';
import store2 from './store2'; // 引入store2模块

Vue.use(Vuex);

const store = new Vuex.Store({
  modules: { store2 }, // 把store2模块挂载到store里面
  state: state,
  getters: getters,
  mutations: mutations,
  actions: actions,
});

export default store;
  1. 访问state - 我们在App.vue测试访问store2模块中的state中的name,结果如下:
<template>
  <div></div>
</template>

<script>
export default {
  mounted() {
    console.log(this.$store.state.store2.name); // 访问store2里面的name属性
  },
};
</script>

什么时候应该用vuex呢?

  • 这个问题因人而异,如果你不需要开发大型的单页应用,此时你完全没有必要使用vuex,比如你的页面就两三个,使用vuex后增加的文件比你现在的页面还要多,那就没这个必要了。
  • 假如你的项目达到了中大型应用的规模,此时您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。