Vuex学习笔记

如题,结合官方文档与网络帖子,利用AI整理

Vuex 学习指南(组合式 API 版)

Date: October 16, 2025

Code: https://github.com/vuejs/vuex

目录

  1. 什么是 Vuex

  2. 为什么需要 Vuex

  3. Vuex 核心概念

  4. 组合式 API 基本使用

  5. 模块化管理

  6. 实际项目应用

  7. 最佳实践

  8. 性能优化

  9. 常见问题

  10. Vuex 4 新特性

什么是 Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

Vuex 的核心思想

  • 单一状态树:整个应用的状态被存储在一个单一的对象中

  • 状态是只读的:唯一改变状态的方法是提交 mutation

  • 使用纯函数来执行修改:Mutation 必须是同步函数

为什么需要 Vuex

传统组件通信的问题

在大型 Vue 应用中,组件间通信会变得非常复杂:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- 传统的组件通信方式 -->
<!-- 父组件 -->
<template>
<div>
<ChildA :data="data" @update="handleUpdate" />
<ChildB :data="data" @update="handleUpdate" />
</div>
</template>

<script setup>
import { ref } from 'vue'
import ChildA from './ChildA.vue'
import ChildB from './ChildB.vue'

const data = ref({ count: 0 })

const handleUpdate = (newData) => {
data.value = newData
}
</script>

//子组件
this.$emit(update,'newdata')

Vuex 解决的问题

  1. 多个视图依赖于同一状态

  2. 来自不同视图的行为需要变更同一状态

  3. 复杂的组件树中,跨层级通信困难

什么时候使用 Vuex

  • 构建中大型单页应用

  • 需要在多个组件间共享状态

  • 状态逻辑复杂,需要统一管理

  • 需要追踪状态变更历史

Vuex 核心概念

1. State

State 是存储应用状态的地方,就像组件中的 data:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// store/index.js
import { createStore } from 'vuex'

const store = createStore({
state() {
return {
count: 0,
user: {
name: '',
isLogin: false
},
products: []
}
}
})

export default store

在组件中访问 State(组合式 API):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script setup>
import { useStore } from 'vuex'
import { computed } from 'vue'

const store = useStore()

// 直接访问
const count = computed(() => store.state.count)
const user = computed(() => store.state.user)

// 或者使用 mapState(仍然支持)
import { mapState } from 'vuex'

const { count, user } = mapState({
count: state => state.count,
user: state => state.user
})
</script>

2. Mutations

Mutations 是修改状态的唯一方法,必须是同步函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// store/index.js
const store = createStore({
state() {
return {
count: 0
}
},
mutations: {
// 基本 mutation
increment(state) {
state.count++
},

// 带参数的 mutation
incrementBy(state, payload) {
state.count += payload.amount
}
}
})

提交 Mutation(组合式 API):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script setup>
import { useStore } from 'vuex'

const store = useStore()

// 直接提交
const increment = () => {
store.commit('increment')
}

const incrementBy = () => {
store.commit('incrementBy', { amount: 5 })
}

// 或者使用 mapMutations
import { mapMutations } from 'vuex'

const { increment, incrementBy } = mapMutations([
'increment',
'incrementBy'
])
</script>

3. Actions

Actions 用于处理异步操作,可以包含任意异步代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// store/index.js
const store = createStore({
state() {
return {
user: null,
loading: false
}
},
mutations: {
setUser(state, user) {
state.user = user
},
setLoading(state, status) {
state.loading = status
}
},
actions: {
// 登录异步操作
async login({ commit }, credentials) {
commit('setLoading', true)
try {
const response = await api.login(credentials)
commit('setUser', response.data)
return response.data
} catch (error) {
throw error
} finally {
commit('setLoading', false)
}
}
}
})

分发 Action(组合式 API):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<script setup>
import { useStore } from 'vuex'

const store = useStore()

// 直接分发
const handleLogin = async () => {
try {
const user = await store.dispatch('login', {
username: 'admin',
password: '123456'
})
console.log('登录成功:', user)
} catch (error) {
console.error('登录失败:', error)
}
}

// 或者使用 mapActions
import { mapActions } from 'vuex'

const { login } = mapActions([
'login'
])

const handleLogin = async () => {
try {
await login({ username: 'admin', password: '123456' })
} catch (error) {
console.error(error)
}
}
</script>

4. Getters

Getters 用于从 state 中派生出一些状态,类似于计算属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// store/index.js
const store = createStore({
state() {
return {
todos: [
{ id: 1, text: '学习Vuex', done: true },
{ id: 2, text: '编写代码', done: false }
]
}
},
getters: {
// 基础 getter
doneTodos: (state) => {
return state.todos.filter(todo => todo.done)
},

// 带参数的 getter
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
},

// 基于其他 getter 的计算
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
})

使用 Getters(组合式 API):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script setup>
import { useStore } from 'vuex'
import { computed } from 'vue'

const store = useStore()

// 直接访问
const doneTodos = computed(() => store.getters.doneTodos)
const todoById = (id) => store.getters.getTodoById(id)

// 或者使用 mapGetters
import { mapGetters } from 'vuex'

const { doneTodos, doneTodosCount } = mapGetters([
'doneTodos',
'doneTodosCount'
])
</script>

组合式 API 基本使用

1. 安装 Vuex

1
2
# Vue 3
npm install vuex@4 --save

2. 创建 Store

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// store/index.js
import { createStore } from 'vuex'

const store = createStore({
state() {
return {
count: 0
}
},
mutations: {
increment(state) {
state.count++
}
},
actions: {
incrementAsync({ commit }) {
return new Promise((resolve) => {
setTimeout(() => {
commit('increment')
resolve()
}, 1000)
})
}
},
getters: {
doubleCount: (state) => state.count * 2
}
})

export default store

3. 在 Vue 应用中注入 Store

1
2
3
4
5
6
7
8
// main.js
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'

const app = createApp(App)
app.use(store)
app.mount('#app')

4. 在组件中使用(组合式 API)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<template>
<div>
<p>Count: {{ count }}</p>
<p>Double Count: {{ doubleCount }}</p>
<button @click="handleIncrement">Increment</button>
<button @click="handleIncrementAsync">Increment Async</button>
</div>
</template>

<script setup>
import { useStore } from 'vuex'
import { computed } from 'vue'

const store = useStore()

// 访问状态
const count = computed(() => store.state.count)
const doubleCount = computed(() => store.getters.doubleCount)

// 提交 mutation
const handleIncrement = () => {
store.commit('increment')
}

// 分发 action
const handleIncrementAsync = async () => {
await store.dispatch('incrementAsync')
}
</script>

5. 组合式 API 的优势

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<script setup>
import { useStore } from 'vuex'
import { computed, watch } from 'vue'

const store = useStore()

// 可以更好地组织相关的逻辑
const count = computed(() => store.state.count)
const doubleCount = computed(() => count.value * 2)

// 可以使用watch监听状态变化
watch(count, (newVal, oldVal) => {
console.log(`Count changed from ${oldVal} to ${newVal}`)
})

// 可以将逻辑抽取为组合函数
const useCounter = () => {
const count = computed(() => store.state.count)

const increment = () => {
store.commit('increment')
}

return {
count,
increment
}
}

const { count, increment } = useCounter()
</script>

模块化管理

当应用变得复杂时,需要将 Store 分割成模块:

1. 模块结构

1
2
3
4
5
6
7
store/
├── index.js # 组装模块
├── getters.js # 全局 getters
└── modules/
├── user.js # 用户模块
├── cart.js # 购物车模块
└── product.js # 商品模块

2. 创建模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// store/modules/user.js
export default {
namespaced: true, // 启用命名空间
state() {
return {
token: localStorage.getItem('token') || '',
userInfo: JSON.parse(localStorage.getItem('userInfo')) || {}
}
},
mutations: {
SET_TOKEN(state, token) {
state.token = token
localStorage.setItem('token', token)
},
SET_USER_INFO(state, userInfo) {
state.userInfo = userInfo
localStorage.setItem('userInfo', JSON.stringify(userInfo))
},
LOGOUT(state) {
state.token = ''
state.userInfo = {}
localStorage.removeItem('token')
localStorage.removeItem('userInfo')
}
},
actions: {
async login({ commit }, credentials) {
const response = await api.login(credentials)
commit('SET_TOKEN', response.token)
commit('SET_USER_INFO', response.userInfo)
return response
},

logout({ commit }) {
commit('LOGOUT')
}
},
getters: {
isAuthenticated: (state) => !!state.token,
userRole: (state) => state.userInfo.role || ''
}
}

3. 注册模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// store/index.js
import { createStore } from 'vuex'
import user from './modules/user'
import cart from './modules/cart'
import product from './modules/product'
import getters from './getters'

const store = createStore({
modules: {
user,
cart,
product
},
getters
})

export default store

4. 使用模块化 Store(组合式 API)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<script setup>
import { useStore } from 'vuex'
import { computed } from 'vue'

const store = useStore()

// 访问模块状态
const token = computed(() => store.state.user.token)
const isAuthenticated = computed(() => store.getters['user/isAuthenticated'])

// 提交模块 mutation
const setToken = (token) => {
store.commit('user/SET_TOKEN', token)
}

// 分发模块 action
const handleLogin = async (credentials) => {
try {
await store.dispatch('user/login', credentials)
} catch (error) {
console.error(error)
}
}

// 或者使用 createNamespacedHelpers
import { createNamespacedHelpers } from 'vuex'

const { mapState, mapGetters, mapMutations, mapActions } = createNamespacedHelpers('user')

const { token, userInfo } = mapState([
'token',
'userInfo'
])

const { isAuthenticated } = mapGetters([
'isAuthenticated'
])

const { SET_TOKEN } = mapMutations([
'SET_TOKEN'
])

const { login, logout } = mapActions([
'login',
'logout'
])
</script>

实际项目应用

购物车示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// store/modules/cart.js
export default {
namespaced: true,
state() {
return {
items: JSON.parse(localStorage.getItem('cart')) || [],
totalPrice: 0
}
},
mutations: {
ADD_TO_CART(state, product) {
const existingItem = state.items.find(item => item.id === product.id)

if (existingItem) {
existingItem.quantity++
} else {
state.items.push({ ...product, quantity: 1 })
}

localStorage.setItem('cart', JSON.stringify(state.items))
this.commit('cart/CALCULATE_TOTAL')
},

REMOVE_FROM_CART(state, productId) {
state.items = state.items.filter(item => item.id !== productId)
localStorage.setItem('cart', JSON.stringify(state.items))
this.commit('cart/CALCULATE_TOTAL')
},

UPDATE_QUANTITY(state, { productId, quantity }) {
const item = state.items.find(item => item.id === productId)
if (item) {
item.quantity = quantity
localStorage.setItem('cart', JSON.stringify(state.items))
this.commit('cart/CALCULATE_TOTAL')
}
},

CLEAR_CART(state) {
state.items = []
localStorage.removeItem('cart')
this.commit('cart/CALCULATE_TOTAL')
},

CALCULATE_TOTAL(state) {
state.totalPrice = state.items.reduce((total, item) => {
return total + (item.price * item.quantity)
}, 0)
}
},
actions: {
addToCart({ commit }, product) {
commit('ADD_TO_CART', product)
},

removeFromCart({ commit }, productId) {
commit('REMOVE_FROM_CART', productId)
},

updateQuantity({ commit }, payload) {
commit('UPDATE_QUANTITY', payload)
},

clearCart({ commit }) {
commit('CLEAR_CART')
}
},
getters: {
cartItems: (state) => state.items,
cartCount: (state) => state.items.reduce((count, item) => count + item.quantity, 0),
totalPrice: (state) => state.totalPrice,
isEmpty: (state) => state.items.length === 0
}
}

在组件中使用购物车(组合式 API)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<template>
<div class="cart">
<h2>购物车</h2>

<div v-if="isEmpty" class="empty-cart">
购物车为空
</div>

<div v-else>
<div v-for="item in cartItems" :key="item.id" class="cart-item">
<span>{{ item.name }}</span>
<span>¥{{ item.price }}</span>
<div class="quantity-controls">
<button @click="updateQuantity(item.id, item.quantity - 1)" :disabled="item.quantity <= 1">
-
</button>
<span>{{ item.quantity }}</span>
<button @click="updateQuantity(item.id, item.quantity + 1)">
+
</button>
<button @click="removeFromCart(item.id)" class="remove-btn">
删除
</button>
</div>
</div>

<div class="cart-summary">
<p>总计: ¥{{ totalPrice }}</p>
<p>商品数量: {{ cartCount }}</p>
<button @click="clearCart" class="clear-btn">
清空购物车
</button>
</div>
</div>
</div>
</template>

<script setup>
import { useStore } from 'vuex'
import { computed } from 'vue'

const store = useStore()

// 获取购物车状态
const cartItems = computed(() => store.getters['cart/cartItems'])
const cartCount = computed(() => store.getters['cart/cartCount'])
const totalPrice = computed(() => store.getters['cart/totalPrice'])
const isEmpty = computed(() => store.getters['cart/isEmpty'])

// 购物车操作
const addToCart = (product) => {
store.dispatch('cart/addToCart', product)
}

const removeFromCart = (productId) => {
store.dispatch('cart/removeFromCart', productId)
}

const updateQuantity = (productId, quantity) => {
if (quantity <= 0) {
removeFromCart(productId)
return
}

store.dispatch('cart/updateQuantity', {
productId,
quantity
})
}

const clearCart = () => {
store.dispatch('cart/clearCart')
}
</script>

用户认证示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// store/modules/auth.js
export default {
namespaced: true,
state() {
return {
token: localStorage.getItem('token') || '',
user: JSON.parse(localStorage.getItem('user')) || null,
loading: false,
error: null
}
},
mutations: {
SET_TOKEN(state, token) {
state.token = token
localStorage.setItem('token', token)
},

SET_USER(state, user) {
state.user = user
localStorage.setItem('user', JSON.stringify(user))
},

SET_LOADING(state, loading) {
state.loading = loading
},

SET_ERROR(state, error) {
state.error = error
},

CLEAR_AUTH(state) {
state.token = ''
state.user = null
localStorage.removeItem('token')
localStorage.removeItem('user')
}
},
actions: {
async login({ commit }, { email, password }) {
try {
commit('SET_LOADING', true)
commit('SET_ERROR', null)

const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
})

const data = await response.json()

if (!response.ok) {
throw new Error(data.message || 'Login failed')
}

commit('SET_TOKEN', data.token)
commit('SET_USER', data.user)

return data.user
} catch (error) {
commit('SET_ERROR', error.message)
throw error
} finally {
commit('SET_LOADING', false)
}
},

async logout({ commit, state }) {
try {
commit('SET_LOADING', true)

await fetch('/api/auth/logout', {
method: 'POST',
headers: {
'Authorization': `Bearer ${state.token}`
}
})
} finally {
commit('CLEAR_AUTH')
commit('SET_LOADING', false)
}
},

async checkAuth({ commit, state }) {
if (!state.token) return false

try {
commit('SET_LOADING', true)

const response = await fetch('/api/auth/me', {
headers: {
'Authorization': `Bearer ${state.token}`
}
})

if (!response.ok) {
commit('CLEAR_AUTH')
return false
}

const user = await response.json()
commit('SET_USER', user)
return true
} catch (error) {
commit('CLEAR_AUTH')
return false
} finally {
commit('SET_LOADING', false)
}
}
},
getters: {
isAuthenticated: (state) => !!state.token,
currentUser: (state) => state.user,
isLoading: (state) => state.loading,
authError: (state) => state.error,
userRole: (state) => state.user?.role || 'user'
}
}

最佳实践

1. 使用组合函数封装逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// composables/useAuth.js
import { useStore } from 'vuex'
import { computed } from 'vue'

export function useAuth() {
const store = useStore()

// 状态
const isAuthenticated = computed(() => store.getters['auth/isAuthenticated'])
const currentUser = computed(() => store.getters['auth/currentUser'])
const isLoading = computed(() => store.getters['auth/isLoading'])
const authError = computed(() => store.getters['auth/authError'])

// 方法
const login = async (credentials) => {
return store.dispatch('auth/login', credentials)
}

const logout = async () => {
return store.dispatch('auth/logout')
}

const checkAuth = async () => {
return store.dispatch('auth/checkAuth')
}

return {
// 状态
isAuthenticated,
currentUser,
isLoading,
authError,

// 方法
login,
logout,
checkAuth
}
}

2. 在组件中使用组合函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<script setup>
import { useAuth } from '@/composables/useAuth'
import { onMounted } from 'vue'

const {
isAuthenticated,
currentUser,
isLoading,
authError,
login,
checkAuth
} = useAuth()

// 组件挂载时检查认证状态
onMounted(async () => {
await checkAuth()
})

const handleLogin = async (email, password) => {
try {
await login({ email, password })
// 登录成功后的处理
} catch (error) {
console.error('Login failed:', error)
}
}
</script>

3. 状态持久化

1
2
3
4
5
6
7
8
9
10
11
// store/index.js
import { createStore } from 'vuex'
import createPersistedState from 'vuex-persistedstate'

const store = createStore({
plugins: [
createPersistedState({
paths: ['auth.token', 'auth.user', 'cart.items']
})
]
})

4. 严格模式

1
2
3
const store = createStore({
strict: process.env.NODE_ENV !== 'production'
})

5. 模块化最佳实践

1
2
3
4
5
// 1. 每个模块独立文件
// 2. 启用命名空间
// 3. 按业务功能划分模块
// 4. 模块内部状态私有
// 5. 使用组合函数封装模块逻辑

6. 异步操作规范

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// store/modules/example.js
actions: {
async fetchData({ commit }, params) {
try {
commit('SET_LOADING', true)
commit('SET_ERROR', null)

const data = await api.fetchData(params)
commit('SET_DATA', data)

return data
} catch (error) {
commit('SET_ERROR', error.message)
throw error
} finally {
commit('SET_LOADING', false)
}
}
}

性能优化

1. 使用计算属性缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script setup>
import { useStore } from 'vuex'
import { computed } from 'vue'

const store = useStore()

// ✅ 正确:使用计算属性缓存
const filteredItems = computed(() => {
return store.state.items.filter(item => item.active)
})

// ❌ 错误:每次访问都会重新计算
const getFilteredItems = () => {
return store.state.items.filter(item => item.active)
}
</script>

2. 避免不必要的状态更新

1
2
3
4
5
6
7
8
9
// store/mutations.js
mutations: {
SET_USER(state, user) {
// 只有当用户信息真正改变时才更新
if (JSON.stringify(state.user) !== JSON.stringify(user)) {
state.user = user
}
}
}

3. 使用 shallowRef 优化大对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<script setup>
import { useStore } from 'vuex'
import { shallowRef, watch } from 'vue'

const store = useStore()

// 对于大对象,使用 shallowRef 避免深层响应式转换
const largeData = shallowRef(store.state.largeData)

// 监听状态变化并手动更新
watch(
() => store.state.largeData,
(newData) => {
largeData.value = newData
},
{ deep: true }
)
</script>

4. 模块懒加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 动态注册模块
const registerModule = async () => {
const module = await import('./store/modules/lazyModule')
store.registerModule('lazy', module.default)
}

// 在路由懒加载中使用
const routes = [
{
path: '/lazy',
component: () => {
registerModule()
return import('./views/LazyView.vue')
}
}
]

5. 使用 createSelector 优化 getter

1
2
3
4
5
6
7
8
9
10
11
12
13
// store/getters.js
import { createSelector } from 'vuex'

const selectItems = state => state.items
const selectFilter = state => state.filter

// 使用 createSelector 创建记忆化的 selector
export const selectFilteredItems = createSelector(
[selectItems, selectFilter],
(items, filter) => {
return items.filter(item => item.name.includes(filter))
}
)

常见问题

1. 如何在组合式 API 中使用 map 辅助函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script setup>
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
import { toRefs } from 'vue'

// 使用 toRefs 将对象转换为响应式引用
const { count } = toRefs(mapState({
count: state => state.count
}))

const { doubleCount } = toRefs(mapGetters([
'doubleCount'
]))

const { increment } = mapMutations([
'increment'
])

const { fetchData } = mapActions([
'fetchData'
])
</script>

2. 如何处理 v-model 和 Vuex 的冲突

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<template>
<!-- 使用计算属性的 get 和 set -->
<input v-model="userName">
</template>

<script setup>
import { useStore } from 'vuex'
import { computed } from 'vue'

const store = useStore()

const userName = computed({
get() {
return store.state.user.name
},
set(value) {
store.commit('updateUserName', value)
}
})
</script>

3. 如何在组合式 API 中监听状态变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script setup>
import { useStore } from 'vuex'
import { watch, computed } from 'vue'

const store = useStore()

const count = computed(() => store.state.count)

// 监听状态变化
watch(count, (newVal, oldVal) => {
console.log(`Count changed from ${oldVal} to ${newVal}`)
})

// 监听深层对象变化
watch(
() => store.state.user,
(newUser, oldUser) => {
console.log('User changed')
},
{ deep: true }
)
</script>

4. 如何在模块间通信

1
2
3
4
5
6
7
8
9
10
11
12
13
// store/modules/moduleA.js
actions: {
async moduleAAction({ dispatch, commit, rootGetters }) {
// 调用其他模块的 action
await dispatch('moduleB/moduleBAction', payload, { root: true })

// 提交其他模块的 mutation
commit('moduleB/moduleBMutation', payload, { root: true })

// 访问其他模块的 getter
const data = rootGetters['moduleB/moduleBGetter']
}
}

5. 如何处理异步数据加载状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<script setup>
import { useStore } from 'vuex'
import { computed } from 'vue'

const store = useStore()

const data = computed(() => store.state.data)
const loading = computed(() => store.state.loading)
const error = computed(() => store.state.error)

const fetchData = async () => {
try {
await store.dispatch('fetchData')
} catch (error) {
console.error(error)
}
}
</script>

<template>
<div>
<div v-if="loading">加载中...</div>
<div v-else-if="error">{{ error }}</div>
<div v-else>
<!-- 显示数据 -->
</div>
</div>
</template>

Vuex 4 新特性

1. 与 Vue 3 的兼容性

1
2
3
4
5
6
7
8
9
10
// Vue 3 中创建 Store
import { createStore } from 'vuex'

const store = createStore({
state() {
return {
count: 0
}
}
})

2. Composition API 支持

1
2
3
4
5
6
7
8
9
<script setup>
import { useStore } from 'vuex'
import { computed } from 'vue'

const store = useStore()

const count = computed(() => store.state.count)
const increment = () => store.commit('increment')
</script>

3. 新的插件系统

1
2
3
4
5
6
7
8
9
10
11
// 创建插件
const loggerPlugin = (store) => {
store.subscribe((mutation, state) => {
console.log(`[${mutation.type}]:`, mutation.payload)
})
}

// 使用插件
const store = createStore({
plugins: [loggerPlugin]
})

4. 改进的 TypeScript 支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// TypeScript 支持
interface State {
count: number
user: User | null
}

interface User {
id: number
name: string
}

const store = createStore<State>({
state: {
count: 0,
user: null
},
mutations: {
setUser(state, user: User) {
state.user = user
}
}
})

5. 更好的 DevTools 集成

1
2
3
4
5
6
7
8
9
10
// 自定义 devtools 配置
const store = createStore({
devtools: {
enabled: process.env.NODE_ENV !== 'production',
features: {
timeTravel: true,
persistState: true
}
}
})

关键要点:组合式 API 集成:使用 useStore() 在组合式 API 中访问 Store

  • 组合函数封装:将相关的状态和逻辑封装为可复用的组合函数

  • 模块化设计:按业务功能划分模块,启用命名空间

  • 类型安全:Vuex 4 提供了更好的 TypeScript 支持

  • 性能优化:利用计算属性缓存、shallowRef 等特性优化性能

何时使用 Vuex:

  • 构建中大型单页应用

  • 需要在多个组件间共享状态

  • 状态逻辑复杂,需要统一管理

  • 需要追踪状态变更历史

通过合理使用 Vuex 和组合式 API,可以显著提高 Vue 3 应用的可维护性和可扩展性,让状态管理变得更加清晰和可控。

参考资源: