Vuex学习路线,上手非常简单

109 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第31天,点击查看活动详情

概括

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

我的理解:将不同组件需要共用的数据集中保存在一起,避免了嵌套组件传参的麻烦,需要使用时可以直接调用或者通过计算得到,并且是响应式的。

什么是“状态管理模式”?

假如现在有一个大型单页面的项目,由很多组件和子组件构成,当某个数据发生变化时,这些页面要做出相应的反应,这就是多个组件共享一个状态,此时就会产生两个问题:

  1. 多个视图依赖同一个状态
  2. 来自不同视图的行为需要变更同一个状态

虽然都有对应的解决办法,但是对于中大型项目来说,嵌套组件的传参过于繁琐,解决起来还是不太优雅,维护也很困难。

所有就想到了把这些共享的状态提取出来,用一个全局单例模式管理,任何组件都能直接访问到这些状态,状态发生变化时,这些组件也能实时更新。

附上一张官方的流程图:

image-20220829185758118.png

安装VueX

先使用脚手架创建一个 Vue 项目,可以通过脚手架直接安装 VueX,也可以通过下面的步骤手动搭建。

终端执行 npm i vuex -s ,然后创建 src/store/index.js 文件,文件内容如下:

import Vue from "vue";
import Vuex from "vuex";
​
Vue.use(Vuex);
export default new Vuex.Store({
    state:{},
    getters:{},
    mutations:{},
    actions:{},
    modules:{}
});

main.js 文件中:

import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store"; //引入的store对象Vue.config.productionTip = false;
​
new Vue({
  router,
  store, //把store对象绑定到Vue实例上,每个实例只能有一个store对象
  render: (h) => h(App),
}).$mount("#app");

到这步就安装配置完成了,接下来就可以使用,在上面的store对象中,有五个对象属性,,这五个对象属性也正是VueX的核心。

State

state就是存放数据的仓库,在state中添加属性

state:{
    price:5,
    //……任何你想共享的状态
}

接下来在组件中获取这个状态,在 AboutView.vue 中:

<template>
  <div class="about">
    {{ price }}
  </div>
</template>
computed:{
    price(){
        return this.$store.state.price;
    }
}

由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态。

Mutations

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation,每个mutation 都有一个字符串的事件类型和一个回调函数。

mutations:{
    //type:‘change_price’ 回调函数
    change_price(state, price) {
      state.price = price;
    },
}

AboutView.vue 中:

<input type="number" v-model="sub_price" @change="change_price" />
data() {
    return {
        sub_price: 0,
    };
},
methods: {
    change_price() {
        this.$store.commit("change_price", this.sub_price);
    },
},

在input框中绑定了一个 sub_price 属性,当它被修改时调用 change_price 方法,在这个方法中提交了一个 mutation,将会调用 mutations 中的 change_price 方法,并在回调函数中接收 sub_price,修改 state 中 price 的值。

Actions

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。
actions: {
    // context:与 store 实例具有相同方法和属性的对象
    dec_price(context, payload) {
        setTimeout(() => {
            //提交 mutations
            context.commit("dec_price", payload);
        }, 1000);
    },
},
mutations: {
    change_price(state, price) {
        state.price = price;
    },
    dec_price(state, price) {
        state.price += price;
    },
},

AboutView.vue 中:

<button @click="dec_price">price减1</button>
dec_price() {
    //通过dispatch调用action中的方法
    this.$store.dispatch("dec_price", -1);
},

每次点击按钮后,会经过大约一秒后把price减1,在 Action 中主要实现一些异步的操作。

Modules

当项目足够大时,在 state 中管理的状态就会变得臃肿,可以把 store 分割成模块,每个模块都有自己的 state、mutation、action、getter,甚至模块中还能嵌套模块。

const moduleA = {
  namespaced: true,
  state: () => ({
    count: 10,
  }),
  mutations: {
    add_count(state, payload) {
      state.count += payload;
    },
  },
  actions: {},
  getters: {},
};
export default new Vuex.Store({
  modules: {
    moduleA: moduleA,
  },
});

AboutView.vue 中:

{{ price }}*{{ count }} =
<button @click="add_count(1)">加数量</button>
<button @click="add_count(-1)">减数量</button>
computed:{
    //返回模块 state 中的状态
    count(){
        return this.$store.state.moduleA.count;
    }
}
methods:{
    add_count(val){
        //通过命名空间提交模块的 mutation
        this.$store.commit("moduleA/add_count",val)
    }
}

接下来就可以在页面中通过数量加减按钮控制模块中 state 的状态了。

Getters

可以理解为 store 中的计算属性,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

const moduleA = {
  getters: {
    /**
     * @description:根据价格数量计算总价
     * @param {*} state 当前模块的state
     * @param {*} getters
     * @param {*} rootState 根模块的state
     * @return {*}
     */
    total(state, getters, rootState) {
      return state.count * rootState.price;
    },
  },
};

AboutView.vue 中:

{{ price }}*{{ count }} = {{ total }}
computed: {
    total() {
        //调用模块中 getters 的 total 方法
        return this.$store.getters["moduleA/total"];
    },
},

小结

对于 vuex 算是入了个门,后面需要更多的实践操作才能理解更加深入,mapStatemapGetters 辅助函数比较简单,就没有展开细说,还有 modules 中的命名空间,只需要添加一个属性 namespaced: true, 即可。vuex 状态持久化也比较简单,可以自己保存到 localStorage 或者 sessionStorage 中,也可以直接使用 vuex-persistedstate 插件。