状态管理工具
状态管理工具出现的背景
- 组件间数据共享的问题
- 单向数据流:
- 多个视图依赖于同一个状态。传参方法:
props,emit。对于多层嵌套的组件会非常繁琐,同时无法解决兄弟组件间的状态共享问题 - 来自不同视图的行为需要变更同一个状态。事件方法:
emit, on。可以采用父子组件直接引用或者通过事件来变更和同步状态的拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
- 多个视图依赖于同一个状态。传参方法:
- 全局对象
- 比如 window,也可以全局获取和修改,但是全局对象会污染全局环境,且多个组件可能都需要修改它,这样不利于维护。而且没办法让组件知道数据的变化,导致需要手动刷新组件。
// 在所有组件外层套一个父组件,所有子组件都可以共享子组件内的状态
const Count = {
// 状态
data() {
return {
count: 0,
};
},
// 视图
template: `
<div>
<button @click="count++">+1</button>
{{count}}
</div>
`,
methods: {
increment() {},
},
};
createApp(Count).mount("#app");
- 状态管理工具(vuex, redux, mobx)
Vuex
什么是状态管理工具
- 状态管理工具是一种集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
什么是 Vuex
一个专为Vue.js应用程序开发的状态管理模式 + 库。采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
- Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
- Vuex 是集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
- Vuex 是响应式的,当数据变化时,视图会重新渲染。
核心思想
-
集中式状态管理 Vuex 将所有组件的共享状态抽取出来,集中存储在一个 store 中,使状态的变更变得可追踪,可预测,并可统一管理
-
状态响应式的实现原理 Vuex 内部使用 Vue 的响应式系统(Vue2 使用
Object.defineProperty,Vue3 使用Proxy)来让state中的对象变为响应式的。组件从store中获取状态后,一旦state中的数据发生变化,那么组件也会自动更新。
Vuex 的基本使用
状态自管理应用包含以下几个部分:
- state,驱动应用的数据源;
- view,以声明方式将 state 映射到视图;
- actions,响应在 view 上的用户输入导致的状态变化。
在 vuex 中,数据的流动是单向的,遵循State -> View → Action/Mutations → State 的循环
组件触发事件 -→ Action(处理异步逻辑) -→ Mutation(修改状态) --> State(存储数据) --> View(渲染 UI)
View(组件) │ dispatch(异步)/commit(同步) ↓ actions → mutations → state(被更新) ↑ getters(派生状态) │ View(再次获取新状态)
组件 dispatch → actions → commit → mutations → 修改 state(响应式)→ 组件自动更新
- State: 存放共享数据的地方
- Getters: 类似于计算属性,用于获取 state 中的数据
- Mutations: 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。每个 mutation 都有一个字符串的事件类型 (type) 和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。
- Actions: 类似于 mutation,不同在于:Action 提交的是 mutation,而不是直接变更状态;Action 可以包含任意异步操作。
VueX 的核心概念
- Vuex 的核心概念包括:state、getters、mutations、actions、modules。
- Vuex 的状态存储是响应式的。当 vue 组件从 store 中读取状态的时候,如果 store 中的状态发生变化,那么响应的组件也会触发更更新。
- Vuex 的状态存储是响应式的。当 vue 组件从 store 中读取状态的时候,如果 store 中的状态发生变化,那么响应的组件也会触发更更新。
- 不能直接修改 store 中的状态,而是需要通过 mutation 来进行状态的修改,这是改变 store 状态的唯一方式(
commit)。方便跟踪每一个状态的变化,从而让我们能够更好地理解我们的应用发生了什么变化,并且更好地进行调试。
import { createApp } from "vue";
import { createStore } from "vuex";
// 创建一个 store 实例
const store = createStore({
state() {
return {
count: 0,
};
},
mutations: {
increment(state) {
state.count++;
},
},
});
const app = createApp("#app");
// 将 store 实例作为插件注入到根组件
app.use(store);
- 接下来,可以在任何组建中通过
store.xxx获取状态对象,并通过commit触发变更
store.commit("increment");
console.log(store.state.count); // -> 1
// 访问 store 实例
methods:{
increment(){
this.$store.commit("increment")
console.info(this.$store.state.count) // -> 1
}
}
- 我们可以直接从
store中获取状态,但是,这种模式导致组件依赖全局状态单例。在模块化的构建系统中,以一个全局 store 对象管理应用的所有状态,而不管状态是定义在哪个模块,这会导致代码变得难以维护。
// 一个Vue组件
const Counter = {
template: `<span>Count is: {{ count }}</span>`,
computed: {
count() {
return store.state.count;
},
},
};
- vuex 通过 Vue 的插件系统将
store实例从根组件注入到每一个组件,实现组件与store的通信,从而实现组件与组件之间的状态共享。
const Counter = {
template: `<span>Count is: {{ count }}</span>`,
computed: {
count() {
// 子组件中直接访问 store 实例
return this.$store.state.count;
},
},
};
- 当一个组件需要获取多个状态的时候,我们可以使用
mapState辅助函数帮助我们生成计算属性。
import { mapState } from "vuex";
export default {
computed: {
...mapState({
count: (state) => state.count,
// 可以直接传递一个字符串参数
countAlias: "count", // 等价于 count: state => state.count
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
countPlusLocalState(state) {
return state.count + this.localCount; // this.localCount 是组件的局部状态
},
}),
},
};
// 当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。
computed: {
...mapState(["count"]),
},
// mapState 函数返回一个对象,因此可以与局部计算属性混合使用
computed: {
localComputed() {},
...mapState({
count: (state) => state.count,
}),
},
State: 定义了应用状态的数据结构,可以在这里设置默认的初始状态。
Getters: store 的计算属性
- 允许组件从 Store 中获取状态,mapGetters 辅助函数可以将 store 中的 getter 映射到局部计算属性。
// getter 可以认为是 store 的计算属性,接受store为第一个参数
const store = createStore({
state: {
todos: [
{ id: 1, text: "...", done: true },
{ id: 2, text: "...", done: false },
],
},
getters: {
filterList(state) {
return state.todos.filer((todo) => todo.done);
},
},
});
// 调用 getters
store.getters.filterList; // -> [{ id: 1, text: "...", done: true }]
/* ********** */
// getters 可以接受其他 getter 作为第二个参数
getters: {
// ...
todosCount(state, getters){
return getters.filterList.length
}
}
// 代码中访问
store.getters.todosCount; // -> 1
// 组件中访问
computed: {
// ...
todosCount() {
return this.$store.getters.todosCount; // -> 1
}
}
- 通过方法访问
// 定义 getters
getters:{
// ...
getListById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
// 使用 getters
store.getters.getListById(1); // -> { id: 1, text: "...", done: true }
// 组件中访问
computed: {
// ...
getGetterListById() {
return this.$store.getters.getListById(1);
}
}
- mapGetters 辅助函数,仅仅是将 store 中的 getter 映射到局部计算属性
import { mapGetters } from "vuex";
export default {
// ...
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
// 调用前面写过的 getter 方法
...mapGetters([
"todosCount",
"filterList",
"getListById"
// ...
]),
},
};
// 给 getter属性另取名字
...mapGetters({
// 把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount`
doneCount: "doneTodosCount",
})
Mutations: 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation 是唯一允许更新应用状态的地方,它接受 state 作为第一个参数,接受额外参数作为第二个参数。
const store = createStore({
state: {
count: 0,
},
mutations: {
increment(state) {
// 变更状态
state.count++;
},
},
});
// 调用 mutation
store.commit("increment"); // -> state.count + 1
- 我们不能直接调用一个
mutation处理函数,需要通过commit来触发一个mutation处理函数,mutation处理函数必须是同步函数。(这个选项更像事件注册) - 提交参数(payload):
mutations: {
increment(state, n) {
state.count += n;
},
}
store.commit("increment", 233)
/* ******* */
// payload 应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读
mutations: {
increment(state, payload) {
state.count += payload.amount;
},
}
store.commit("increment", {
amount: 10,
})
// 对象风格的提交方式
store.commit({
type: "increment",
amount: 10,
})
- 作为代码规范,我们需要使用常量替代
mutation事件类型
// mutation-type.js
export const SOME_MUTATION = "SOME_MUTATION";
// store.js
import Vuex from "vuex";
import { SOME_MUTATION } from "./mutation-type";
const store = new Vuex.Store({
state: {
//...
},
mutations: {
// 使用 es2015 风格的计算属性命名功能来使用一个常量作为函数名
[SOME_MUTATION](state, payload) {
// mutate state
state.count += payload.amount;
},
},
});
Mutation必须是同步函数,这是为了调试和跟踪状态变化。
mutations: {
increment(state) {
setTimeout(() => {
state.count++;
}, 1000); // 异步操作不行
}
someMutation(state) {
api.callAsyncData(()=>{
state.count++; // 异步操作不行
})
}
}
mapMutations辅助函数,在组件中提交mutation:
import { mapMutations } from "vuex";
export default {
// ...
methods: {
// 将 `this.increment()` 映射为 `this.$store.commit('increment')`
...mapMutations(["increment"]),
// 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
...mapMutations({
incrementBy: "increment",
}),
},
};
Actions:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。
- Actions 提交的是 mutation,而不是直接变更状态。
- Actions 可以包含任意异步操作。
const store = createStore({
state: {
count: 0,
},
mutations: {
increment(state) {
state.count++;
},
},
actions: {
increment(context) {
// context 对象具有和 store 实例相同的方法属性,但并不是 store 实例本身
context.commit("increment");
},
},
});
// 实际写法
actions: {
increment({ commit }) {
commit("increment");
},
},
- 使用 Action:
store.dispatch("increment"); - 可以在 Action 内部执行异步操作
// 使用 Action
store.dispatch("increment");
// Action 内部执行异步操作
actions: {
incrementAsync({commit}){
setTimeout(() => {
commit("increment");
}, 100);
}
}
// 以 payload 的形式提交
store.dispatch("incrementAsync", {
amount: 10,
})
// 以对象形式提交
store.dispatch({
type: "incrementAsync",
amount: 10,
})
/* ********* */
// e.g.:
actions:{
checkout({ commit, state }, products) {
// 将当前物品备份
const savedCartItems = [...state.cart.added];
// 发起 checkout 请求,然后使用备份的物品,清空购物车
commit(types.CHECKOUT_REQUEST);
shop.buyProducts(
products,
() => {
// 请求成功
commit(types.CHECKOUT_SUCCESS);
},
() => {
// 请求失败,恢复 cart 状态
commit(types.CHECKOUT_FAILURE, savedCartItems);
}
);
},
};
- 在组件中使用 actions
import { mapActions } from "vuex";
export default {
methods: {
...mapActions(["increment"]),
// 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
...mapActions({
incrementBy: "increment",
}),
},
};
- 组合多个 actions
// 如何组合多个 actions
actions:{
actionA({ commit }) {
return new Promise((res, rej) => {
setTimeout(() => {
commit("someMutation");
res();
}, 100);
});
},
};
store.dispatch("actionA").then(() => {
// 做一些actionA执行完之后的事情...
});
// 在 actions 中分发其他 actions
actions: {
// ...
actionB({ dispatch, commit }) {
return dispatch("actionA").then(() => {
commit("someOtherMutation");
});
},
};
/* ****** */
// 更推荐结合 async/await
actions: {
async actionA({commit}){
commit('gotData',await getData())
}
async actionB({dispatch,commit}){
await dispatch('actionA')
// 等待 actionA 完成之后再执行其他操作
commit('gotOtherData',await getOtherData())
}
}
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 的状态
- 模块的局部状态
const moduleA = {
state:()=>{
return{
count:0,
id:2333
}
}
mutations:{
// 这里 state 是模块的局部状态
increment(state){
state.count++
}
}
actions:{
// 对于模块内部的 action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState
async increment({ state, commit, rootState }){
await { data } = api.fetchData({id:state.id})
// ...
if(state.count < 10){
commit('doubleCount')
}else{
rootState.count++
}
}
}
// 模块内部的 getter,根节点状态会作为第三个参数暴露出来:
getters:{
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
doubleCount(state){
return state.count * 2
}
}
}
namespace命名空间: 默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的 —— 这样使得多个模块能够对同一 mutation 或 action 作出响应。 可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
// 命名空间
const store = createStore({
modules: {
account: {
namespaced: true, // 开启命名空间
state: () => {},
getters: {
isAdmin() {
// 调用方法: getters['account/isAdmin']
// getters['account/isAdmin']
},
},
actions: {
logIn() {
// 调用方法: dispatch('account/logIn')
// dispatch('account/logIn')
},
},
mutations: {
logOut() {
// 调用方法: commit('account/logOut')
// commit('account/logOut')
},
},
},
},
});
// 嵌套模块的命名空间
const store = new Vuex.Store({
modules: {
account: {
namespaced: true,
// 模块内容(module assets)
state: () => ({ ... }), // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响
getters: {
isAdmin () { ... } // -> getters['account/isAdmin']
},
actions: {
login () { ... } // -> dispatch('account/login')
},
mutations: {
login () { ... } // -> commit('account/login')
},
// 嵌套模块
modules: {
// 继承父模块的命名空间
myPage: {
state: () => ({ ... }),
getters: {
profile () { ... } // -> getters['account/profile']
}
},
// 进一步嵌套命名空间
posts: {
namespaced: true,
state: () => ({ ... }),
getters: {
popular () { ... } // -> getters['account/posts/popular']
}
}
}
}
}
})
结构
--- main.js
--- api
--- userData.js
--- HomeData.js
--- App.vue
--- User.vue
--- store
--- index.js // 组装模板并导出 store 的地方
Vuex 对比组件通信
| 特性 | Vuex | 父子通信 / Event Bus / Props 等 |
|---|---|---|
| 适合场景 | 跨组件复杂状态共享 | 简单数据传递或局部状态管理 |
| 状态追踪 | 清晰,支持时间旅行调试 | 难以跟踪 |
| 数据流向 | 单向 | 可变(可能出现回调地狱) |
Vue3 ComposeApi中使用
我们可以通过useStore函数,在 setup 函数中获取到 store 实例,从而使用 store 中的数据和方法。
import { useStore } from "vuex";
export default {
setup() {
const store = useStore(); // 获取 store 实例
},
};
- 访问 State 和 Getter
import { computed } from "vue";
import { useStore } from "vuex";
export default {
setup() {
const store = useStore();
return {
// 在 computed 函数中访问 state
count: computed(() => store.state.count),
double: computed(() => store.getters.double),
};
},
};
- 访问 Mutation 和 Action
import { useStore } from "vuex";
export default {
setup() {
const store = useStore();
return {
// 在 methods 中调用 mutation
increment: () => store.commit("increment"),
// 在 methods 中调用 action
async fetchData: await store.dispatch("fetchData"),
};
},
};
插件 Plugin
import { createLogger } from "vuex";
const store = createStore({
plugins: [createLogger()],
});
const myPlugin = (store) => {
// 当 store 初始化后调用
store.subscribe((mutation, state) => {
// 每次 mutation 之后调用
// mutation 的格式为 { type, payload }
});
};
const store = new Vuex.Store({
// ...
plugins: [myPlugin],
});
2025年5月30日 补充
Vuex 面试题
Vuex 原理
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
- Vuex 的状态存储是响应式的,当 Vue 组件从 store 中读取状态的时候,如果 store 中的状态发生变化,那么相应的组件也会得到高效更新。
- 改变 store 中的状态的唯一途径就是显式地提交(
commit)mutation,这样使得我们可以方便地跟踪每一个状态的变化。 - Vuex 为 Vue components 建立起了一个完整的生态圈,包括开发中的 api 调用一环
- 核心流程中的主要功能
Vue Components是 Vue 组件,组件会触发(dispatch)一些事件或动作,也就是图中的Actions- 在组件中发出的动作,肯定是想获取或者改变数据的,但是在 vuex 中,数据是集中管理的,不能直接去更改数据,所以会把这个动作提交(Commit)到 Mutations 中;
- 然后 Mutations 就去改变(Mutate)State 中的数据;
- 当 State 中的数据被改变之后,就会重新渲染(Render)到 Vue Components 中去,组件展示更新后的数据,完成一个流程。
- 各模块在核心流程中的主要功能
Vue Components∶ Vue 组件。HTML 页面上,负责接收用户操作等交互行为,执行dispatch方法触发对应action进行回应。dispatch∶ 操作行为触发方法,是唯一能执行action的方法。actions: 操作行为处理模块。负责处理 Vue Components 接收到的所有交互行为。包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发。向后台 API 请求的操作就在这个模块中进行,包括触发其他action以及提交mutation的操作。该模块提供了 Promise 的封装,以支持action的链式触发。commit: 状态改变提交操作方法。对mutation进行提交,是唯一能执行mutation的方法。mutations∶ 状态改变操作方法。是 Vuex 修改 state 的唯一推荐方法,其他修改方式在严格模式下将会报错。该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些 hook 暴露出来,以进行 state 的监控等。state∶ 页面状态管理容器对象。集中存储Vuecomponents中 data 对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用 Vue 的细粒度数据响应机制来进行高效的状态更新。getters∶ state 对象读取方法。图中没有单独列出该模块,应该被包含在了 render 中,Vue Components 通过该方法读取全局 state 对象。
总结
Vuex 实现了一个单向数据流,在全局拥有一个 State 存放数据,当组件要更改 State 中的数据时,必须通过 Mutation 提交修改信息, Mutation 同时提供了订阅者模式供外部插件调用获取 State 数据的更新。而当所有异步操作(常见于调用后端接口异步获取更新数据)或批量的同步操作需要走 Action ,但 Action 也是无法直接修改 State 的,还是需要通过 Mutation 来修改 State 的数据。最后,根据 State 的变化,渲染到视图上。
Vuex 中 action 和 mutation 的区别
- mutation 必须是同步函数
mutation中的操作是一系列的同步函数,用于修改state中的变量的的状态。当使用 vuex 时需要通过commit来提交需要操作的内容。mutation非常类似于事件:每个mutation都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是实际进行状态更改的地方,并且它会接受state作为第一个参数:
const store = new Vuex.Store({
state: {
count: 1,
},
mutations: {
increment(state) {
// 变更状态
state.count++;
},
},
});
当触发一个类型为 increment 的 mutation 时,需要调用此函数:store.commit('increment')
- action 类似于 mutation,不同在于:
- Action 可以包含任意异步操作
- Action 提交的是 mutation,而不是直接变更状态
const store = new Vuex.Store({
state: {
count: 0,
},
mutations: {
increment(state) {
state.count++;
},
},
actions: {
increment(context) {
context.commit("increment"
}
});
Action 函数接受一个与 store 实例具有相同方法和属性的
context对象,因此你可以调用context.commit提交一个 mutation,或者通过context.state和context.getters来获取 state 和 getters。
二者的不同点:
Mutation专注于修改 State,理论上是修改 State 的唯一途径;Action 主要执行业务代码、异步请求。Mutation:必须同步执行;Action:可以异步,但不能直接操作 State。- 在视图更新时,先触发
actions,actions再触发mutation mutation的参数是state,它包含store中的数据;store的参数是context,它是state的父级,包含state、getters
Vuex 和 localStorage 的区别
- 最重要的区别
Vuex存储在内中,支持响应式localstorage以文件的方式存储在本地,只能存储字符串类型的数据,存储对象的话,需要将对象JSON.stringify()编码为字符串后进行存储。不支持响应式,需要手动去触发更新。读取内存比读取本地缓存要快很多
- 应用场景
- Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。vuex 用于组件之间的传值。
- localstorage 是本地存储,是将数据存储到浏览器的方法,一般是在跨页面传递数据时使用 。
- Vuex 能做到数据的响应式,localstorage 不能
- 永久性
- 刷新页面时 vuex 存储的值会丢失,localstorage 不会。
注意:对于不变的数据确实可以用 localstorage 可以代替 vuex,但是当两个组件共用一个数据源(对象或数组)时,如果其中一个组件改变了该数据源,希望另一个组件响应该变化时,localstorage 无法做到,原因就是 区别 1 。
Redux 和 Vuex 的区别,有什么共同思想
- Redux 和 Vuex 区别
- Vuex 改进了 Redux 中的 Action 和 Reducer 函数,以
mutations变化函数取代Reducer,无需 switch,只需在对应的mutation函数里改变state值即可 - Vuex 由于 Vue 自动重新渲染的特性,无需订阅重新渲染函数,只要生成新的 State 即可
- Vuex 数据流的顺序是 ∶ View 调用 store.commit 提交对应的请求到 Store 中对应的 mutation 函数->store 改变(vue 检测到数据变化自动渲染)
- 通俗点理解就是:vuex 弱化 dispatch,通过 commit 进行 store 状态的一次更变;取消了 action 概念,不必传入特定的 action 形式进行指定变更;弱化 reducer,基于 commit 参数直接对数据进行转变,使得框架更加简易;
- Vuex 改进了 Redux 中的 Action 和 Reducer 函数,以
- 共同思想
- 单—的数据源
- 变化可以预测
本质上:redux 与 vuex 都是对 mvvm 思想的服务,将数据从视图中抽离的一种方案; 形式上:vuex 借鉴了 redux,将 store 作为全局的数据中心,进行 mode 管理;
为什么要用 Vuex 或者 Redux
由于传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致代码无法维护。 所以需要把组件的共享状态抽取出来,以一个全局单例模式管理。在这种模式下,组件树构成了一个巨大的"视图",不管在树的哪个位置,任何组件都能获取状态或者触发行为。 通过定义和隔离状态管理中的各种概念并强制遵守一定的规则,代码将会变得更结构化且易维护。
Vuex 有哪几种属性
有五种,分别是 State、 Getter、Mutation 、Action、 Module
- state => 基本数据(数据源存放地)
- getters => 从基本数据派生出来的数据
- mutations => 提交更改数据的方法,同步
- actions => 像一个装饰器,包裹 mutations,使之可以异步。
- modules => 模块化 Vuex
Vuex 和单纯的全局对象有什么区别
- Vuex 的状态存储是响应式的。当 Vue 组件从
store中读取状态的时候,若store中的状态发生变化,那么相应的组件也会相应地得到高效更新。 - 不能直接改变
store中的状态。改变store中的状态的唯一途径就是显式地提交 (commit)mutation。这样可以方便地跟踪每一个状态的变化,从而能够实现一些工具帮助更好地了解我们的应用。
为什么 Vuex 的 mutation 中不能做异步操作
Vuex中所有的状态更新的唯一途径都是mutation,异步操作通过Action来提交mutation实现,这样可以方便地跟踪每一个状态的变化,从而能够实现一些工具帮助更好地了解我们的应用。- 每个
mutation执行完成后都会对应到一个新的状态变更,这样devtools就可以打个快照存下来,然后就可以实现time-travel了。如果mutation支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。
Vuex 的严格模式是什么,开启有什么用,怎么开启
在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。
- 开启严格模式:
在 store 的创建中,添加 strict: process.env.NODE_ENV !== 'production',在开发环境时开启严格模式,生产环境时关闭严格模式。
const store = new Vuex.Store({
// ...
strict: process.env.NODE_ENV !== "production",
});
在组件中批量使用 Vuex 的 getter 属性
使用 mapGetters 辅助函数, 利用对象展开运算符将 getter 混入 computed 对象中
import { mapGetters } from "vuex";
export default {
computed: {
...mapGetters(["total", "discount", "activeCount"]),
},
};
在组件中重复使用 Vuex 的 mutation
使用mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。
import { mapMutations } from "vuex";
export default {
methods: {
...mapMutations({
setNumber: "SET_NUMBER", // 将 this.setNumber 映射为 this.$store.commit('SET_NUMBER')
}),
},
};
// 使用
this.setNumber(10);
// 相当调用this.$store.commit('SET_NUMBER',10)