Vuex
Vuex 是 Vue.js 的官方状态管理库,用于集中管理组件间共享的状态(数据)。
它通过单一数据源(store)实现数据的响应式管理和可预测的状态变更。
核心概念
概念 | 作用 | 特点 |
---|---|---|
State | 存储应用的状态数据(类似组件的 data ) | 唯一数据源,响应式 |
Getters | 从 state 派生的计算属性(类似组件的 computed ) | 缓存结果,避免重复计算 |
Mutations | 唯一修改 state 的方法(同步操作) | 通过 commit 触发 |
Actions | 处理异步操作或复杂逻辑,提交 mutation 间接修改 state | 通过 dispatch 触发 |
Modules | 将 store 拆分为多个模块,便于管理大型应用的状态 | 支持嵌套和命名空间(namespaced) |
基本使用
安装与配置
npm install vuex@next # Vue 3 使用 vuex@4
npm install vuex@3 # Vue 2 使用 vuex@3
创建 Store
// store/index.js
import { createStore } from 'vuex';const store = createStore({state: {count: 0,user: { name: 'Alice' }},getters: {doubleCount: (state) => state.count * 2,},mutations: {increment(state) {state.count++;},setUser(state, payload) {state.user = payload;}},actions: {async fetchUser({ commit }, userId) {const user = await api.getUser(userId);commit('setUser', user);}},modules: {// 模块化示例见下文}
});export default store;
挂载到 Vue 实例
// main.js
import { createApp } from 'vue';
import App from './App.vue';
import store from './store';const app = createApp(App);
app.use(store); // 注入全局 $store
app.mount('#app');
组件中使用 Vuex
State
存储数据
<template><div>{{ $store.state.count }}</div><div>{{ userName }}</div>
</template><script setup>
import { computed } from 'vue';
import { useStore } from 'vuex';const store = useStore();
const userName = computed(() => store.state.user.name);
</script>
Vuex 的 state 是 Vuex 存储数据的地方,它提供了一种集中式的存储机制,让 Vue 组件能够方便地访问和共享状态。以下是 Vuex 的 state 的使用方法:
定义 state
在 Vuex store 中定义 state,state 是一个对象,用于存储应用的状态数据。
const store = new Vuex.Store({state: {count: 0,todos: [{ id: 1, text: 'Learn JavaScript', done: false },{ id: 2, text: 'Learn Vue', done: false },{ id: 3, text: 'Build a Vue App', done: false }]}
})
在组件中访问 state
可以通过 this.$store.state
或者 mapState
辅助函数在组件中访问 state 中的数据。
直接访问
export default {computed: {count() {return this.$store.state.count;},todos() {return this.$store.state.todos;}}
}
使用 mapState 辅助函数
import { mapState } from 'vuex';export default {computed: {...mapState(['count','todos'])}
}
修改 state
Vuex 的 state 是响应式的,但只能通过提交 mutation 来修改 state,直接修改 state 不会触发视图更新。
// 提交 mutation
this.$store.commit('increment');// 或者在组件中使用 methods
methods: {increment() {this.$store.commit('increment');}
}
对应的 mutation 定义如下:
const store = new Vuex.Store({state: {count: 0},mutations: {increment(state) {state.count++;}}
})
使用模块化 Vuex
在大型应用中,可以将 Vuex store 拆分成多个模块,每个模块有自己的 state、mutations、actions 等。
const moduleA = {namespaced: true,state: {count: 0},mutations: {increment(state) {state.count++;}}
}const store = new Vuex.Store({modules: {a: moduleA}
})
在组件中访问模块的 state:
computed: {count() {return this.$store.state.a.count;}
}
或者使用 mapState
辅助函数:
import { mapState } from 'vuex';export default {computed: {...mapState('a', ['count'])}
}
动态注册模块
可以在应用运行时动态注册模块,方便在需要时添加新的功能。
// 动态注册模块
store.registerModule('b', {state: {name: 'Module B'}
})// 在组件中访问
computed: {name() {return this.$store.state.b.name;}
}
通过以上方法,可以在 Vue 组件中方便地使用 Vuex 的 state,实现数据的集中管理和共享。
Getters
Vuex 中的 Getters 是用于从 Store 的 state 中派生出状态的计算属性,类似于组件内的 computed
属性。
基本用法
-
定义 Getter:在 Store 的
getters
对象中定义函数,接收state
作为第一个参数。const store = new Vuex.Store({state: {todos: [{ id: 1, done: true },{ id: 2, done: false }]},getters: {doneTodos: state => state.todos.filter(todo => todo.done)} });
-
访问 Getter:通过
this.$store.getters.doneTodos
或mapGetters
辅助函数映射到组件计算属性。// 组件中 computed: {...mapGetters(['doneTodos']) }
参数与依赖
-
访问其他 Getter:通过第二个参数
getters
获取其他 Getter。getters: {doneCount: (state, getters) => getters.doneTodos.length }
-
模块中的 Getter:在模块内,可访问模块的
state
、根节点的rootState
和rootGetters
。getters: {combinedData(state, getters, rootState, rootGetters) {// 访问模块和根状态} }
动态参数
-
返回函数:通过返回函数实现传参,例如根据 ID 查找特定项。
getters: {getTodoById: state => id => state.todos.find(todo => todo.id === id) } // 使用:store.getters.getTodoById(2)
响应式与缓存
- 响应式更新:当依赖的
state
变化时,Getter 自动重新计算。 - 缓存机制:结果会被缓存,除非依赖的
state
发生变化,避免重复计算。
Mutation
在 Vuex 中,Mutations 是更改 store 中状态的唯一方式。它们是同步的事务,用于修改 state。以下是如何定义和使用 Mutations 的详细步骤:
定义和使用 Mutations
1. 定义 Mutations
在 Vuex store 中定义 Mutations。Mutations 是一个对象,其中的每个属性是一个函数,这些函数接收 state 作为第一个参数,或者通过参数解构来获取 state 和其他上下文信息。
const store = new Vuex.Store({state: {count: 0,todos: []},mutations: {// 简单的 mutationincrement(state) {state.count++;},// 带参数的 mutationincrementBy(state, amount) {state.count += amount;},// 修改 todossetTodos(state, todos) {state.todos = todos;}}
});
2. 在组件中提交 Mutations
可以在组件中通过 this.$store.commit
提交 Mutations。
<template><div><p>Count: {{ count }}</p><button @click="increment">Increment</button><button @click="incrementBy">Increment By 5</button><ul><li v-for="todo in todos" :key="todo.id">{{ todo.text }}</li></ul></div>
</template><script>
export default {computed: {count() {return this.$store.state.count;},todos() {return this.$store.state.todos;}},methods: {increment() {this.$store.commit('increment');},incrementBy() {this.$store.commit('incrementBy', 5);},fetchTodos() {// 模拟异步操作setTimeout(() => {this.$store.commit('setTodos', [{ id: 1, text: 'Learn JavaScript' },{ id: 2, text: 'Learn Vue' }]);}, 1000);}},created() {this.fetchTodos();}
};
</script>
使用 mapMutations
辅助函数
为了简化代码,Vuex 提供了 mapMutations
辅助函数,可以将 store 中的 Mutations 映射到组件的方法中。
import { mapMutations } from 'vuex';export default {methods: {...mapMutations(['increment','incrementBy','setTodos'])}
};
这样,你就可以直接在组件中使用 increment
、incrementBy
和 setTodos
方法,而不需要显式地写 this.$store.commit
。
Mutations 的参数传递
如果需要向 Mutation 传递参数,可以在 commit
中作为第二个参数传入。
// 在组件中
this.$store.commit('incrementBy', 5);// 在 store 的 Mutations 中
incrementBy(state, amount) {state.count += amount;
}
Mutations 的返回值
Mutations 本身没有返回值,但你可以在提交 Mutation 后执行其他操作。
在 Mutations 中访问 state 和 getters
在 Mutations 中,可以通过参数解构来访问 state 和 getters。
mutations: {someMutation({ state, getters }) {console.log(state.count);console.log(getters.doubleCount);}
}
模块化 Vuex 中的 Mutations
在模块化 Vuex 中,Mutations 可以定义在模块内部。
const moduleA = {namespaced: true,state: {count: 0},mutations: {increment(state) {state.count++;}}
};const store = new Vuex.Store({modules: {a: moduleA}
});
在组件中提交模块的 Mutation:
this.$store.commit('a/increment');
或者使用 mapMutations
:
import { mapMutations } from 'vuex';export default {methods: {...mapMutations('a', ['increment'])}
};
总结
- Mutations 是更改 Vuex store 中状态的唯一方式。
- 在组件中通过
this.$store.commit
提交 Mutations。 - 可以使用
mapMutations
辅助函数简化代码。 - Mutations 可以接收参数,并在模块化 Vuex 中定义在模块内部。
Action
在 Vuex 中,actions 用于处理异步操作,并可以包含逻辑来提交 mutations,从而更新 state。actions 是 Vuex 中处理业务逻辑的主要场所。
定义和使用 actions
1. 定义 actions
在 Vuex store 中定义 actions。actions 是一个对象,其中的每个属性是一个函数,这些函数接收一个上下文对象作为参数,或者通过参数解构来获取 commit
、getters
等。
const store = new Vuex.Store({state: {count: 0,todos: []},mutations: {increment(state) {state.count++;},setTodos(state, todos) {state.todos = todos;}},actions: {// 简单的 actionincrement({ commit }) {commit('increment');},// 带参数的 actionincrementBy({ commit }, amount) {commit('incrementBy', amount);},// 异步 actionfetchTodos({ commit }) {// 模拟异步操作return new Promise((resolve) => {setTimeout(() => {commit('setTodos', [{ id: 1, text: 'Learn JavaScript' },{ id: 2, text: 'Learn Vue' }]);resolve();}, 1000);});}}
});
2. 在组件中使用 actions
可以在组件中通过 this.$store.dispatch
触发 actions。
<template><div><p>Count: {{ count }}</p><button @click="increment">Increment</button><button @click="fetchTodos">Fetch Todos</button><ul><li v-for="todo in todos" :key="todo.id">{{ todo.text }}</li></ul></div>
</template><script>
export default {computed: {count() {return this.$store.state.count;},todos() {return this.$store.state.todos;}},methods: {increment() {this.$store.dispatch('increment');},fetchTodos() {this.$store.dispatch('fetchTodos').then(() => {console.log('Todos fetched!');});}}
};
</script>
使用 mapActions
辅助函数
为了简化代码,Vuex 提供了 mapActions
辅助函数,可以将 store 中的 actions 映射到组件的方法中。
import { mapActions } from 'vuex';export default {methods: {...mapActions(['increment','fetchTodos'])}
};
这样,你就可以直接在组件中使用 increment
和 fetchTodos
方法,而不需要显式地写 this.$store.dispatch
。
actions 的参数传递
如果需要向 action 传递参数,可以在 dispatch
中作为第二个参数传入。
// 在组件中
this.$store.dispatch('incrementBy', 5);// 在 store 的 actions 中
incrementBy({ commit }, amount) {commit('incrementBy', amount);
}
actions 的返回值
dispatch
会返回一个 Promise,因此可以在触发 action 后使用 .then()
或 async/await
来处理异步操作的结果。
this.$store.dispatch('fetchTodos').then(() => {console.log('Todos fetched!');
});
在 actions 中访问 state 和 getters
在 actions 中,可以通过上下文对象访问 state 和 getters。
actions: {someAction({ state, getters }) {console.log(state.count);console.log(getters.doubleCount);}
}
模块化 Vuex 中的 actions
在模块化 Vuex 中,actions 可以定义在模块内部。
const moduleA = {namespaced: true,state: {count: 0},mutations: {increment(state) {state.count++;}},actions: {increment({ commit }) {commit('increment');}}
};const store = new Vuex.Store({modules: {a: moduleA}
});
在组件中触发模块的 action:
this.$store.dispatch('a/increment');
或者使用 mapActions
:
import { mapActions } from 'vuex';export default {methods: {...mapActions('a', ['increment'])}
};
总结
- actions 用于处理异步操作和业务逻辑。
- 在组件中通过
this.$store.dispatch
触发 actions。 - 可以使用
mapActions
辅助函数简化代码。 - actions 可以接收参数,并返回 Promise。
- 在模块化 Vuex 中,actions 可以定义在模块内部,并通过命名空间访问。
辅助函数(简化代码)
Vuex 提供 mapState
, mapGetters
, mapMutations
, mapActions
辅助函数。
<script>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex';export default {computed: {...mapState(['count']),...mapGetters(['doubleCount']),},methods: {...mapMutations(['increment']),...mapActions(['fetchUser']),}
};
</script>
模块化(Modules)
定义模块
// store/modules/user.js
const userModule = {namespaced: true, // 启用命名空间state: () => ({ name: 'Guest' }),mutations: {setName(state, name) {state.name = name;}},actions: {login({ commit }, name) {commit('setName', name);}}
};export default userModule;
注册模块
// store/index.js
import userModule from './modules/user';const store = createStore({modules: {user: userModule}
});
访问模块内容
// 组件中使用命名空间访问
store.commit('user/setName', 'Charlie');
store.dispatch('user/login', 'David');// 辅助函数指定命名空间
...mapMutations('user', ['setName']),
...mapActions('user', ['login']),
核心原则
- 单一数据源:所有共享状态集中存储在
state
中。 - 状态只读:不允许直接修改
state
,必须通过mutations
。 - 同步修改:
mutations
必须是同步函数,确保状态变更可追踪。 - 异步操作:异步逻辑放在
actions
中,通过提交mutations
修改状态。