原稿:语雀 · Vuex 使用 · 原目录:Vue › Vue 知识点 › Vuex 使用

记录下自己使用 Vuex 填的坑

引入

  1. 使用 npm install vuex -S 安装 vuex
  2. 创建 /store 目录,其中创建子目录 /modules 和 index.js,子目录为了方便模块化使用 Vuex
import Vue from "vue";
import Vuex from "vuex";
import user from "./modules/user";

Vue.use(Vuex);
export default new Vuex.Store({
  strict: true,
  modules: {
    user,
  },
});
  1. 在 main.js 中引入 Vuex,同时使用 ‘store’ 属性将 store 注册到根组件中,之后可使用 this.$store 访问当前 Vuex 的仓库
import store from "./store";
new Vue({
  router,
  store,
  render: h => h(App),
}).$mount('#app')
  1. 编写 /modules/user.js,即用户模块

Vuex 响应式原理

参考链接:深入vuex源码(四)-响应式store及动态注册 - 龙玥的文章 - 知乎

Vuex 响应式主要在构造函数 Store() 执行过程中调用的 resetStoreVM(this, state): 2. Vuex 创建了一个 vue 实例 store._vm 用于保存单一状态树 4. Vuex 将 store 的 getters 传递给了store._vm 的计算属性,将这些计算属性设为新的 getters,则 store 的 getters 实例属性变成了响应式,同时把 state 作为 store._vm.data.$$state,state 也变成了响应式

function resetStoreVM (store, state, hot) {
  var oldVm = store._vm;
  // bind store public getters
  store.getters = {};
  // reset local getters cache
  store._makeLocalGettersCache = Object.create(null);
  var wrappedGetters = store._wrappedGetters;
  var computed = {};
  forEachValue(wrappedGetters, function (fn, key) {
    computed[key] = partial(fn, store);
    Object.defineProperty(store.getters, key, {
      get: function () { return store._vm[key]; },
      enumerable: true // for local getters
    });
  });

  // use a Vue instance to store the state tree
  var silent = Vue.config.silent;
  Vue.config.silent = true;
  store._vm = new Vue({
    data: {
      $$state: state
    },
    computed: computed
  });
  // ...
}

Modules

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。

const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

Getter 访问

// /modules/user.js
export default {
  state: {user: "{}"},
    getters: {
    getUid(state) { return JSON.parse(state.user).id;},
    getUsername(state) {return JSON.parse(state.user).username;},
  },
  mutations: {
    setUser(state, data) {state.user = data;},
  },
  actions: {
    setUser(context, data) {
      context.commit("setUser", data);
    },
  },
};

getter 可以理解为 state 的计算属性,主要有多种访问方法

通过属性访问

组件可直接通过 this.$store.getters 访问,默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间,则默认访问模块内的 getters 时无需指定路径

this.$store.getters.getUid
// 无需this.$store.getters['user/getUid']

mapGetters

mapGetters 辅助函数会将 store 中的 getter 映射到局部计算属性

// 假设在login.vue组件中
import { mapGetters } from 'vuex';
export default {
  computed: {
    // 将this.getUsername映射为this.$store.getters.getUsername
    ...mapGetters([
      "getUsername",
      "getUid",
    ])
  }
}

也可重新命名,此时传入对象

// 假设在login.vue组件中
import { mapGetters } from 'vuex';
export default {
  computed: {
    ...mapGetters({
      uname:"getUsername",
      uid:"getUid",
    })
  }
}

Action

Action 注册

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象:

  • 通过调用 context.commit 可以提交一个 mutation,如下方写法
  • 通过 context.state 和 context.getters 来获取 state 和 getters
actions: {
  setUser(context, data) {
    context.commit("setUser", data);
  }
}

使用 ES6 的解构写法可以直接获取 context 传入的 commit,上面的 action 函数可简化:

setUser({commit}, data) {
  commit("setUser", data);
}

Action 分发

  1. 通过 dispatch() 分发
this.$store.dispatch('setUser',user)
  1. 使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在根节点注入 store)
// 假设在login.vue组件中
import { mapActions } from 'vuex';
export default {
methods: {
    // 将this.setUser()映射为this.$store.dispatch('setUser')
    ...mapActions(["setUser"]),
    login() {
      //...
      this.setUser(user);
    }
    }
}