一、为什么需要 Vuex?
1.1 状态管理痛点
在大型 Vue 应用中,组件间共享状态变得复杂:
-
多层嵌套组件传值繁琐
-
兄弟组件通信困难
-
多个组件依赖同一状态
-
不同视图需要变更同一状态
1.2 Vuex 是什么?
Vuex 是 Vue.js 的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
二、核心概念详解
2.1 Store(仓库)
javascript
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: { // 状态数据
count: 0,
user: null
},
mutations: { // 同步修改状态
increment(state) {
state.count++
}
},
actions: { // 异步操作
incrementAsync({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
},
getters: { // 计算属性
doubleCount: state => state.count * 2
},
modules: { // 模块分割
user: userModule
}
})
2.2 State(状态)
定义: 存储应用状态的数据
javascript
state: {
todos: [
{ id: 1, text: '学习 Vuex', done: true },
{ id: 2, text: '项目实战', done: false }
],
visibilityFilter: 'SHOW_ALL',
count: 0,
user: null
}
组件中访问:
javascript
// 方式1:直接访问
computed: {
count() {
return this.$store.state.count
}
}
// 方式2:mapState 辅助函数
import { mapState } from 'vuex'
export default {
computed: {
// 映射 this.count 为 store.state.count
...mapState(['count']),
// 重命名
...mapState({
storeCount: 'count',
// 使用函数
todosCount: state => state.todos.length
})
}
}
2.3 Getters(派生状态)
定义: 基于 state 的计算属性
javascript
getters: {
// 基本 getter
doneTodos: state => {
return state.todos.filter(todo => todo.done)
},
// 接收其他 getter 作为第二个参数
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
},
// 返回函数实现传参
getTodoById: state => id => {
return state.todos.find(todo => todo.id === id)
}
}
组件中使用:
javascript
computed: {
// 方式1:直接访问
doneTodos() {
return this.$store.getters.doneTodos
},
// 方式2:mapGetters 辅助函数
...mapGetters(['doneTodos', 'doneTodosCount']),
// 方式3:重命名
...mapGetters({
finishedTodos: 'doneTodos'
}),
// 带参数的 getter
todo() {
return this.$store.getters.getTodoById(2)
}
}
2.4 Mutations(变更)
定义: 唯一修改 state 的方法(必须是同步)
javascript
mutations: {
// 基本 mutation
increment(state) {
state.count++
},
// 携带参数
addTodo(state, todo) {
state.todos.push(todo)
},
// 对象风格的提交
updateUser(state, payload) {
state.user = {
...state.user,
...payload
}
}
}
组件中提交:
javascript
methods: {
// 方式1:直接提交
increment() {
this.$store.commit('increment')
},
addTodo() {
this.$store.commit('addTodo', {
id: 3,
text: '新的任务',
done: false
})
},
// 对象风格提交
updateUser() {
this.$store.commit('updateUser', {
name: '张三',
age: 25
})
},
// 方式2:mapMutations 辅助函数
...mapMutations(['increment']),
...mapMutations({
add: 'addTodo' // 重命名
})
}
2.5 Actions(动作)
定义: 处理异步操作和业务逻辑
javascript
actions: {
// 基本 action
incrementAsync({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
},
// 异步 API 调用
async fetchUser({ commit }, userId) {
try {
const response = await api.getUser(userId)
commit('SET_USER', response.data)
return response.data
} catch (error) {
commit('SET_ERROR', error.message)
throw error
}
},
// 组合多个 action
async login({ dispatch, commit }, credentials) {
// 1. 显示加载状态
commit('SET_LOADING', true)
try {
// 2. 调用 API
const user = await dispatch('authenticate', credentials)
// 3. 获取用户信息
await dispatch('fetchUserInfo', user.id)
// 4. 跳转页面(在组件中处理更好)
return user
} finally {
// 5. 隐藏加载状态
commit('SET_LOADING', false)
}
}
}
组件中分发:
javascript
methods: {
// 方式1:直接分发
fetchData() {
this.$store.dispatch('fetchUser', 123)
.then(user => {
console.log('获取成功', user)
})
.catch(error => {
console.error('获取失败', error)
})
},
// 方式2:mapActions 辅助函数
...mapActions(['incrementAsync', 'fetchUser']),
...mapActions({
getUser: 'fetchUser' // 重命名
}),
// 异步处理
async handleLogin() {
try {
const user = await this.login(credentials)
this.$router.push('/dashboard')
} catch (error) {
this.showError(error.message)
}
}
}
三、Module(模块化)
3.1 为什么需要模块?
当应用变得复杂时,单一的 store 文件会变得臃肿。模块允许我们将 store 分割成小模块。
3.2 基本模块结构
javascript
// store/modules/user.js
const userModule = {
// 开启命名空间(推荐)
namespaced: true,
state: () => ({
profile: null,
token: '',
permissions: []
}),
mutations: {
SET_PROFILE(state, profile) {
state.profile = profile
},
SET_TOKEN(state, token) {
state.token = token
}
},
actions: {
async login({ commit }, credentials) {
const response = await api.login(credentials)
commit('SET_PROFILE', response.user)
commit('SET_TOKEN', response.token)
return response
},
logout({ commit }) {
commit('SET_PROFILE', null)
commit('SET_TOKEN', '')
localStorage.removeItem('token')
}
},
getters: {
isAuthenticated: state => !!state.token,
userName: state => state.profile?.name || 'Guest'
}
}
export default userModule
3.3 注册模块
javascript
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import userModule from './modules/user'
import productModule from './modules/product'
import cartModule from './modules/cart'
Vue.use(Vuex)
export default new Vuex.Store({
// 根 state
state: {
appName: '电商系统'
},
// 注册模块
modules: {
user: userModule,
product: productModule,
cart: cartModule
}
})
3.4 模块访问方式
javascript
// 开启命名空间时的访问
computed: {
// state 访问
profile() {
return this.$store.state.user.profile
},
// getter 访问
isAuthenticated() {
return this.$store.getters['user/isAuthenticated']
}
},
methods: {
// action 调用
login() {
this.$store.dispatch('user/login', credentials)
},
// mutation 提交
updateProfile() {
this.$store.commit('user/SET_PROFILE', newProfile)
}
}
3.5 模块辅助函数
javascript
import { mapState, mapGetters, mapActions } from 'vuex'
export default {
computed: {
// 方式1:直接映射
...mapState('user', ['profile', 'token']),
...mapGetters('user', ['isAuthenticated', 'userName']),
// 方式2:使用 createNamespacedHelpers
...mapState({
userProfile: state => state.user.profile,
cartItems: state => state.cart.items
})
},
methods: {
...mapActions('user', ['login', 'logout']),
...mapActions('cart', ['addToCart']),
// 重命名
...mapActions({
userLogin: 'user/login',
addProduct: 'cart/addToCart'
})
}
}