原稿:语雀 · Vuex 使用 · 原目录:Vue › Vue 知识点 › Vuex 使用
记录下自己使用 Vuex 填的坑
引入
- 使用 npm install vuex -S 安装 vuex
- 创建 /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,
},
});
- 在 main.js 中引入 Vuex,同时使用 ‘store’ 属性将 store 注册到根组件中,之后可使用 this.$store 访问当前 Vuex 的仓库
import store from "./store";
new Vue({
router,
store,
render: h => h(App),
}).$mount('#app')
- 编写 /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 分发
- 通过 dispatch() 分发
this.$store.dispatch('setUser',user)
- 使用 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);
}
}
}