Vue 3 的组合式 API 风格中,Vuex
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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | 在 Vue 3 的组合式 API 风格中,Vuex 仍然是集中式状态管理工具,但使用方式更加灵活。不过官方已推荐新一代状态库 **Pinia**(可视为 Vuex 5),建议在新项目中使用 Pinia。以下是 Vuex 4(支持 Vue 3)的使用方法: --- ### 一、Vuex 核心概念 - **State**: 全局状态数据 - **Mutations**: 同步修改状态的方法 - **Actions**: 异步操作,触发 Mutations - **Getters**: 计算属性 --- ### 二、基本使用步骤 #### 1. 安装 Vuex ```bash npm install vuex@next --save ``` #### 2. 创建 Store ```js // store/index.js import { createStore } from 'vuex' ; export default createStore({ state: { count: 0 }, mutations: { increment(state) { state.count++; } }, actions: { asyncIncrement({ commit }) { setTimeout(() => commit( 'increment' ), 1000); } }, getters: { doubleCount: (state) => state.count * 2 } }); ``` #### 3. 在组件中使用(组合式 API) ```vue <script setup> import { computed } from 'vue' ; import { useStore } from 'vuex' ; const store = useStore(); // 获取 State(使用计算属性保持响应式) const count = computed(() => store.state.count); // 使用 Getter const doubleCount = computed(() => store.getters.doubleCount); // 触发 Mutation const increment = () => store.commit( 'increment' ); // 触发 Action const asyncIncrement = () => store.dispatch( 'asyncIncrement' ); </script> <template> <div>{{ count }}</div> <div>{{ doubleCount }}</div> <button @click= "increment" >+1</button> <button @click= "asyncIncrement" >Async +1</button> </template> ``` --- ### 三、组合式封装 可将 Store 逻辑封装到自定义 Hook 中,提高复用性: ```js // composables/useCounter.js import { computed } from 'vue' ; import { useStore } from 'vuex' ; export default function useCounter() { const store = useStore(); return { count: computed(() => store.state.count), doubleCount: computed(() => store.getters.doubleCount), increment: () => store.commit( 'increment' ), asyncIncrement: () => store.dispatch( 'asyncIncrement' ) }; } ``` 在组件中使用: ```vue <script setup> import useCounter from './composables/useCounter' ; const { count, doubleCount, increment, asyncIncrement } = useCounter(); </script> ``` --- ### 四、替代方案:Pinia(推荐) Pinia 是 Vue 官方推荐的状态库,专为组合式 API 设计,更简洁: #### 1. 安装 Pinia ```bash npm install pinia ``` #### 2. 定义 Store ```js // stores/counter.js import { defineStore } from 'pinia' ; export const useCounterStore = defineStore( 'counter' , { state: () => ({ count: 0 }), actions: { increment() { this .count++; }, async asyncIncrement() { setTimeout(() => this .increment(), 1000); } }, getters: { doubleCount: (state) => state.count * 2 } }); ``` #### 3. 在组件中使用 ```vue <script setup> import { useCounterStore } from '@/stores/counter' ; import { storeToRefs } from 'pinia' ; const counterStore = useCounterStore(); const { count, doubleCount } = storeToRefs(counterStore); // 保持响应式 const increment = () => counterStore.increment(); const asyncIncrement = () => counterStore.asyncIncrement(); </script> ``` --- ### 五、总结 - **Vuex 4**:适用于已有 Vuex 的项目迁移到 Vue 3,但 API 略显繁琐。 - **Pinia**:新项目首选,完美契合组合式 API,代码更简洁,无 `mutations`,支持 TypeScript。 根据项目需求选择,新项目建议直接使用 Pinia。 |
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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | 在 Vue 3 + TypeScript + 组合式 API 中使用 Vuex Modules,需要特别注意类型系统的支持。以下是完整的配置和使用示例: --- ### 一、项目结构(TypeScript 适配) ```bash src/ ├── store/ │ ├── index.ts # 主 Store │ ├── modules/ │ │ ├── user.ts # 用户模块 │ │ └── product.ts # 商品模块 ``` --- ### 二、定义带类型的 Vuex 模块 #### 1. 用户模块定义 `store/modules/user.ts` ```typescript import { Module } from 'vuex' ; // 定义模块的 State 类型 interface UserState { name: string; age: number; } const userModule: Module<UserState, any> = { namespaced: true , // 使用箭头函数返回初始化状态(确保类型安全) state: (): UserState => ({ name: 'Guest' , age: 0 }), mutations: { SET_NAME(state, payload: string) { state.name = payload; }, SET_AGE(state, payload: number) { state.age = payload; } }, actions: { async fetchUser({ commit }, userId: number) { const user = await mockApi(userId); commit( 'SET_NAME' , user.name); commit( 'SET_AGE' , user.age); } }, getters: { greeting: (state) => `Hello, ${state.name}!` } }; // 模拟 API 请求 async function mockApi(userId: number): Promise<{ name: string; age: number }> { return { name: 'Alice' , age: 25 }; } export default userModule; ``` --- ### 三、创建主 Store `store/index.ts` ```typescript import { createStore, Store, ModuleTree } from 'vuex' ; import userModule, { UserState } from './modules/user' ; // 定义根 State 类型 interface RootState { // 其他全局状态... user: UserState; // 模块状态会被合并到这里 } // 自动推断模块类型 const modules: ModuleTree<RootState> = { user: userModule }; const store = createStore<RootState>({ modules }); // 定义带类型提示的 useStore 函数 export function useStore(): Store<RootState> { return store; } export default store; ``` --- ### 四、在组件中使用(组合式 API + TS) #### 组件示例 `components/UserProfile.vue` ```vue <script setup lang= "ts" > import { computed } from 'vue' ; import { useStore } from '@/store' ; const store = useStore(); // 访问模块 state(类型自动推断) const userName = computed(() => store.state.user.name); // 调用模块 mutation(需要完整命名空间路径) const updateName = () => store.commit( 'user/SET_NAME' , 'Bob' ); // 调用模块 action(类型安全参数) const loadUser = () => store.dispatch( 'user/fetchUser' , 123); // 使用模块 getter const greeting = computed(() => store.getters[ 'user/greeting' ] as string); </script> <template> <div>Name: {{ userName }}</div> <div>{{ greeting }}</div> <button @click= "updateName" >Update Name</button> <button @click= "loadUser" >Load User</button> </template> ``` --- ### 五、增强类型提示(可选) #### 1. 创建类型化辅助函数 `store/helpers.ts` ```typescript import { useStore } from '@/store' ; import { computed } from 'vue' ; export function useUserModule() { const store = useStore(); return { // State userName: computed(() => store.state.user.name), userAge: computed(() => store.state.user.age), // Mutations setName: (name: string) => store.commit( 'user/SET_NAME' , name), // Actions fetchUser: (userId: number) => store.dispatch( 'user/fetchUser' , userId), // Getters greeting: computed(() => store.getters[ 'user/greeting' ] as string) }; } ``` #### 在组件中使用: ```vue <script setup lang= "ts" > import { useUserModule } from '@/store/helpers' ; const { userName, greeting, fetchUser } = useUserModule(); </script> ``` --- ### 六、处理模块间的类型交互 #### 1. 在模块中访问根状态(类型安全) ```typescript // 在 user 模块的 action 中: actions: { someAction({ commit, rootState }) { // 需要明确类型断言 const globalToken = (rootState as RootState).token; } } ``` #### 2. 全局 Action/Mutation 的类型定义 ```typescript // store/index.ts interface RootState { token: string; // 添加全局状态 } // 添加全局 mutations/actions const store = createStore<RootState>({ state: () => ({ token: 'initial_token' }), mutations: { SET_TOKEN(state, payload: string) { state.token = payload; } }, modules }); ``` --- ### 七、注意事项 1. **命名空间必要性** 必须启用 `namespaced: true `,否则模块的 mutations/actions 会全局注册,失去类型安全 2. **严格类型检查** 在 `tsconfig.json` 中启用严格模式: ```json { "compilerOptions" : { "strict" : true , "noImplicitAny" : false // Vuex 可能需要这个设置为 false } } ``` 3. **类型断言的使用** 在访问 `rootState` 或跨模块操作时,可能需要使用类型断言 --- ### 八、对比 Pinia(推荐替代方案) Pinia 的 TypeScript 支持更优秀,天然适配组合式 API: ```typescript // stores/user.ts import { defineStore } from 'pinia' ; interface UserState { name: string; age: number; } export const useUserStore = defineStore( 'user' , { state: (): UserState => ({ name: 'Guest' , age: 0 }), actions: { async fetchUser(userId: number) { const user = await mockApi(userId); this .name = user.name; this .age = user.age; } }, getters: { greeting: (state) => `Hello, ${state.name}!` } }); ``` **优势**: - 自动类型推导 - 无命名空间路径 - 更简洁的 API - 原生组合式 API 支持 --- ### 总结 | 操作 | Vuex 4 + TS | Pinia + TS | |--------------------|--------------------------------------|------------------------------| | 模块定义 | 需要手动处理类型和命名空间 | 每个 Store 天然独立 | | 类型提示 | 需要较多类型断言 | 完全自动推断 | | 组合式 API 友好度 | 需要包装 `useStore` | 直接通过函数调用 | | 代码量 | 较多模板代码 | 简洁直观 | **建议**:新项目应优先选择 Pinia,既有 Vuex 项目可参考上述模式维护。如果必须使用 Vuex,推荐通过封装辅助函数增强类型安全。 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)