尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Vuex核心概念与模块化实践:从状态管理到组件通信

Vuex核心概念与模块化实践:从状态管理到组件通信 1. 项目概述为什么Vuex是Vue开发绕不开的一环最近在带几个前端新人发现他们学Vue时组件通信和数据流管理这块总是卡壳。尤其是当项目稍微复杂一点组件层级一多父子组件用props和$emit传数据就显得力不从心更别提兄弟组件或者远房组件之间的数据共享了。这时候一个集中的状态管理工具就显得尤为重要而Vuex就是Vue生态里解决这个问题的“官方答案”。很多面试官也喜欢问Vuex因为它直接关系到你对Vue应用架构的理解深度。这个“vue数据管理vuex小demo练习”项目就是针对这个痛点设计的。它不是一个复杂的商城或后台系统而是一个聚焦于Vuex核心概念和基础用法的练习场。通过构建一个功能明确、场景典型的小应用我们可以把Vuex的state、getters、mutations、actions、modules这些抽象概念落到具体的代码操作上。你会发现理解了Vuex不仅能让你写出更清晰、更易维护的代码更能让你从“页面仔”的思维进阶到“应用状态管理者”的视角。无论你是刚学完Vue基础想找项目练手还是在面试前需要巩固Vuex知识点这个练习都能给你带来实实在在的收获。2. 核心思路拆解Vuex在小型应用中的价值与设计很多人有个误区觉得Vuex是大型应用的专利小项目用不上。其实不然Vuex的核心价值在于提供了一种可预测的状态变更模式。即使在一个很小的Demo里引入Vuex也能让数据流向变得一目了然。2.1 状态集中化告别“数据传话筒”游戏在没有Vuex的传统组件通信中如果组件A的数据需要传递给组件D你可能需要经过A-B-C-D的层层传递或者借助Event Bus等全局事件机制后者又容易导致事件名冲突、难以调试。Vuex的做法是把所有需要共享的状态State抽离出来放在一个全局的“仓库”Store里。任何组件无论层级多深都可以直接“连接”到这个仓库读取所需状态或者触发状态变更。在这个练习Demo中我们会设计一个经典场景一个简单的计数器搭配一个任务列表Todo List。计数器状态和任务列表就是我们的共享状态。页面头部显示计数器页面主体显示任务列表还有一个独立的组件用于添加新任务。如果没有Vuex添加任务的组件需要把新任务数据通过事件一层层抛给父组件再由父组件更新列表并传递给显示列表的组件。有了Vuex添加任务的组件直接提交一个mutation来更新仓库里的列表显示列表的组件自动就能获取到最新的数据。数据流从“网状”变成了“星型”所有变更都通过中心仓库进行清晰可追溯。2.2 单向数据流让状态变更不再“神秘”Vuex严格遵循单向数据流这是它确保状态可预测的关键。这个流可以简单概括为View - Actions - Mutations - State - View。View视图组件中触发一个动作比如用户点击“增加”按钮。Actions动作组件会dispatch一个action。Action可以包含异步操作比如调用API。在我们的Demo里虽然可能没有异步操作但我们会遵循这个规范将业务逻辑放在action里。Mutations变更Action最终会commit一个mutation。Mutation是唯一能直接修改State的地方并且必须是同步函数。这就像会计记账所有修改都必须通过一个固定的、同步的流程记录在案。State状态Mutation执行后State被更新。View视图因为组件是响应式的连接到这个State的组件视图会自动更新。在这个小练习里我们会严格实践这个流程。即使只是一个简单的“增加计数”操作我们也会让组件调用dispatch(‘incrementAsync’)一个action然后在这个action里再commit(‘INCREMENT’)一个mutation。一开始可能会觉得多此一举但这能帮你养成好习惯在复杂场景比如计数增加前需要先请求接口下架构的优势就体现出来了。2.3 模块化设计为未来复杂度留出空间虽然我们的Demo很小但我们会提前引入Vuex的模块Modules概念。把计数器的状态和相关操作放在一个counter模块里把任务列表的状态和相关操作放在一个todos模块里。这样做的好处是结构清晰不同的业务域状态分离避免一个巨大的、难以维护的store对象。命名空间默认情况下模块内部的action、mutation和getter是注册在全局命名空间的。我们可以通过添加namespaced: true来使其带命名空间这样不同模块可以有同名的mutation避免冲突。便于扩展当Demo未来想加入“用户信息”、“系统设置”等功能时直接新增模块即可对原有代码影响最小。通过这个设计我们的练习项目虽然功能简单但五脏俱全完整涵盖了Vuex最核心的设计思想和最佳实践。3. 环境搭建与项目初始化工欲善其事必先利其器。我们先从零开始搭建一个干净的Vue项目并集成Vuex。3.1 使用Vue CLI快速创建项目首先确保你安装了Node.js和npm或yarn、pnpm。然后我们使用Vue官方脚手架Vue CLI来创建项目。虽然现在有更轻量的Vite方案但Vue CLI的生态和稳定性对于初学者练习来说非常友好。打开终端执行以下命令# 全局安装Vue CLI如果尚未安装 npm install -g vue/cli # 创建一个新的Vue项目项目名设为 vuex-practice-demo vue create vuex-practice-demo在创建过程中命令行会交互式地让你进行选择Please pick a preset选择Manually select features手动选择特性。Check the features needed for your project用空格键选中Babel,Router,Vuex。注意这里一定要选中Vuex这样CLI会自动帮我们完成Vuex的安装和基础配置。Router也选上虽然本Demo可能用不到多页面但作为一个完整练习项目有它更规范。后续的选项如路由模式选hash模式即可、代码风格等根据个人喜好选择或者直接按回车使用默认配置。创建完成后进入项目目录并启动开发服务器cd vuex-practice-demo npm run serve浏览器打开http://localhost:8080看到Vue的欢迎页面说明项目创建成功。3.2 解读Vue CLI自动生成的Vuex结构用代码编辑器打开项目重点关注src/store目录。Vue CLI已经为我们生成了Vuex store的雏形。src/store/index.js这是Vuex store的入口文件。它创建并导出了一个新的Vuex.Store实例。初始的index.js内容通常如下import Vue from vue import Vuex from vuex Vue.use(Vuex) export default new Vuex.Store({ state: { }, mutations: { }, actions: { }, modules: { } })src/main.js主入口文件可以看到store已经被导入并注入到了根Vue实例中new Vue({ store, ... }).$mount(‘#app’)。这意味着在整个Vue应用的任何组件中我们都可以通过this.$store来访问这个store。注意Vue CLI生成的结构是将所有state、mutations等都写在一个文件里。为了实践我们之前提到的模块化思想接下来我们会动手改造这个结构。3.3 改造目录结构实现模块化Store我们将把store目录改造成更清晰的样子src/store/ ├── index.js # 组装模块并导出 store 的地方 ├── modules/ # 模块文件夹 │ ├── counter.js # 计数器模块 │ └── todos.js # 任务列表模块 └── mutation-types.js # 可选Mutation 事件类型常量定义文件创建模块文件在src/store/modules/目录下新建counter.js和todos.js。编写counter模块(src/store/modules/counter.js)const state { count: 0 } const mutations { INCREMENT(state) { state.count }, DECREMENT(state) { state.count-- }, SET_COUNT(state, value) { state.count value } } const actions { // 异步增加模拟一个异步操作 incrementAsync({ commit }) { setTimeout(() { commit(INCREMENT) }, 1000) }, // 带参数的action setCountByValue({ commit }, value) { if (typeof value number) { commit(SET_COUNT, value) } } } const getters { // 一个简单的getter返回计数的两倍 doubleCount: state state.count * 2, // 一个带参数的getter判断计数是否大于某个值 isCountGreaterThan: state (threshold) { return state.count threshold } } export default { // 开启命名空间使模块具有更高的封装性和复用性 namespaced: true, state, mutations, actions, getters }编写todos模块(src/store/modules/todos.js)const state { todos: [ { id: 1, text: 学习Vuex, done: true }, { id: 2, text: 完成Demo练习, done: false }, { id: 3, text: 写项目总结, done: false } ], nextId: 4 } const mutations { ADD_TODO(state, todo) { state.todos.push({ id: state.nextId, text: todo.text, done: false }) }, TOGGLE_TODO(state, id) { const todo state.todos.find(t t.id id) if (todo) { todo.done !todo.done } }, DELETE_TODO(state, id) { const index state.todos.findIndex(t t.id id) if (index ! -1) { state.todos.splice(index, 1) } } } const actions { addTodo({ commit }, todoText) { // 可以在这里做一些校验或预处理 if (!todoText || todoText.trim() ) { return Promise.reject(new Error(任务内容不能为空)) } commit(ADD_TODO, { text: todoText.trim() }) return Promise.resolve() // 返回一个Promise便于组件处理完成状态 }, toggleTodo({ commit }, id) { commit(TOGGLE_TODO, id) }, deleteTodo({ commit }, id) { commit(DELETE_TODO, id) } } const getters { // 获取所有任务 allTodos: state state.todos, // 获取未完成的任务 activeTodos: state state.todos.filter(todo !todo.done), // 获取已完成的任务 completedTodos: state state.todos.filter(todo todo.done), // 统计数量 todosCount: state state.todos.length, activeCount: state state.todos.filter(todo !todo.done).length } export default { namespaced: true, state, mutations, actions, getters }组装Store(src/store/index.js)import Vue from vue import Vuex from vuex import counter from ./modules/counter import todos from ./modules/todos Vue.use(Vuex) export default new Vuex.Store({ modules: { counter, todos } })至此一个模块化、结构清晰的Vuex Store就搭建好了。接下来我们将在组件中使用它。4. 组件开发连接Store与视图我们将创建三个主要组件Counter.vue计数器显示与操作TodoList.vue任务列表展示AddTodo.vue添加新任务。为了布局美观我们使用一个简单的App.vue作为容器。4.1 计数器组件映射State调用Action创建src/components/Counter.vuetemplate div classcounter h2计数器模块演示/h2 div classcount-display p当前计数: strong{{ count }}/strong/p p两倍计数 (来自Getter): strong{{ doubleCount }}/strong/p p计数是否大于5: strong{{ isGreaterThan5 ? 是 : 否 }}/strong/p /div div classcontrols button clickincrement同步增加 ()/button button clickdecrement同步减少 (-)/button button clickincrementAsync :disabledasyncLoading {{ asyncLoading ? 增加中... : 异步增加 (1秒后) }} /button br input typenumber v-model.numberinputValue placeholder设置计数 button clicksetCount设置/button button clickreset重置为0/button /div /div /template script // 引入Vuex提供的辅助函数它们能帮助我们更简洁地将store中的属性映射到组件 import { mapState, mapGetters, mapActions } from vuex export default { name: Counter, data() { return { inputValue: 0, asyncLoading: false } }, computed: { // 使用展开运算符将store中的state和getter映射为组件的计算属性 // 由于使用了命名空间需要指定模块路径 ‘counter/’ ...mapState(counter, [count]), // 映射 this.count 为 store.state.counter.count ...mapGetters(counter, [doubleCount, isCountGreaterThan]), // 映射 getters // 基于映射的getter创建一个新的计算属性 isGreaterThan5() { // 注意isCountGreaterThan 是一个返回函数的getter return this.isCountGreaterThan(5) } }, methods: { // 映射actions。映射后可以直接通过 this.incrementAsync() 调用 ...mapActions(counter, [incrementAsync, setCountByValue]), // 本地方法用于触发同步的mutation通过dispatch action来实现 increment() { // 直接commit mutation不推荐在组件中直接commit应通过action // this.$store.commit(counter/INCREMENT) // 更规范的做法dispatch一个action即使这个action可能只是commit一个mutation this.$store.dispatch(counter/incrementAsync).catch(() { // 处理异步action可能出现的错误 }) }, decrement() { this.$store.commit(counter/DECREMENT) }, setCount() { if (this.inputValue ! ) { // 调用映射过来的action并传递参数 this.setCountByValue(this.inputValue) this.inputValue 0 } }, reset() { this.setCountByValue(0) } } } /script style scoped .counter { padding: 20px; border: 1px solid #eee; border-radius: 8px; margin-bottom: 20px; } .count-display { margin-bottom: 15px; font-size: 1.1em; } .controls button { margin: 5px; padding: 8px 15px; cursor: pointer; } .controls input { padding: 8px; margin: 5px; width: 120px; } /style关键点解析mapState,mapGetters,mapActions这些辅助函数是Vuex的“语法糖”能极大简化在组件中引入store属性的代码。注意在模块化且开启命名空间后第一个参数需要指定模块名。Action vs Mutation组件中应优先使用dispatchaction来改变状态而不是直接commitmutation。这保持了数据流的一致性也为未来添加异步逻辑留出空间。我们在increment方法中故意演示了两种方式。Getter的使用doubleCount是普通getterisCountGreaterThan是方法式getter返回一个函数。在模板中使用方法式getter时需要像调用函数一样传递参数。4.2 任务列表组件展示与交互创建src/components/TodoList.vuetemplate div classtodo-list h2任务列表模块演示 ({{ activeCount }} 项待完成)/h2 div classfilters button :class{ active: filter all } clickfilter all全部 ({{ todosCount }})/button button :class{ active: filter active } clickfilter active未完成 ({{ activeCount }})/button button :class{ active: filter completed } clickfilter completed已完成 ({{ completedCount }})/button /div ul v-iffilteredTodos.length 0 li v-fortodo in filteredTodos :keytodo.id :class{ completed: todo.done } input typecheckbox :checkedtodo.done changetoggleTodo(todo.id) span classtodo-text{{ todo.text }}/span button classdelete-btn clickdeleteTodo(todo.id)×/button /li /ul p v-else暂无任务~/p /div /template script import { mapGetters, mapActions } from vuex export default { name: TodoList, data() { return { filter: all // all, active, completed } }, computed: { // 映射todos模块的getters ...mapGetters(todos, [ allTodos, activeTodos, completedTodos, todosCount, activeCount ]), // 本地计算属性根据筛选条件返回对应的任务列表 filteredTodos() { switch (this.filter) { case active: return this.activeTodos case completed: return this.completedTodos default: return this.allTodos } }, // 映射一个getter作为计算属性别名 completedCount() { return this.todosCount - this.activeCount } }, methods: { // 映射todos模块的actions ...mapActions(todos, [toggleTodo, deleteTodo]) } } /script style scoped .todo-list { padding: 20px; border: 1px solid #eee; border-radius: 8px; } .filters { margin-bottom: 15px; } .filters button { margin-right: 10px; padding: 5px 10px; background: #f0f0f0; border: none; border-radius: 4px; cursor: pointer; } .filters button.active { background: #42b983; color: white; } ul { list-style: none; padding-left: 0; } li { padding: 10px; border-bottom: 1px solid #eee; display: flex; align-items: center; } li.completed .todo-text { text-decoration: line-through; color: #888; } .todo-text { flex-grow: 1; margin-left: 10px; } .delete-btn { background: #ff6b6b; color: white; border: none; border-radius: 50%; width: 24px; height: 24px; cursor: pointer; font-size: 16px; line-height: 1; } /style关键点解析Getter的威力activeTodos,completedTodos等getter将复杂的过滤逻辑从组件中剥离Store负责计算组件只管使用。这使得组件逻辑更简洁且过滤逻辑可以在多个组件中复用。本地状态与全局状态filter筛选条件是组件自身的状态因为它只影响这个组件的视图不需要在其他组件中共享。这种区分很重要不是所有数据都需要放进Vuex。Action的异步处理虽然我们这里的toggleTodo和deleteTodo是同步的但映射的是action。如果未来需要在这些操作前后加入API调用或日志记录只需要修改action组件代码完全不用动。4.3 添加任务组件表单处理与Action调用创建src/components/AddTodo.vuetemplate div classadd-todo h2添加新任务/h2 form submit.preventhandleSubmit input typetext v-modelnewTodoText placeholder输入任务内容按回车或点击添加 keyup.enterhandleSubmit reftodoInput button typesubmit :disabled!newTodoText.trim()添加任务/button /form p v-iferror classerror-message{{ error }}/p p v-ifsuccess classsuccess-message任务添加成功/p /div /template script import { mapActions } from vuex export default { name: AddTodo, data() { return { newTodoText: , error: , success: false } }, methods: { ...mapActions(todos, [addTodo]), async handleSubmit() { // 清除之前的状态 this.error this.success false if (!this.newTodoText.trim()) { this.error 任务内容不能为空 this.$refs.todoInput.focus() return } try { // 调用Vuex action并等待其完成因为我们的action返回了Promise await this.addTodo(this.newTodoText) // 成功后的处理 this.newTodoText this.success true this.$refs.todoInput.focus() // 2秒后清除成功信息 setTimeout(() { this.success false }, 2000) } catch (err) { // 处理action中可能抛出的错误 this.error err.message || 添加任务失败 this.$refs.todoInput.focus() } } }, mounted() { // 组件挂载后自动聚焦输入框提升用户体验 this.$refs.todoInput.focus() } } /script style scoped .add-todo { padding: 20px; border: 1px solid #eee; border-radius: 8px; margin-bottom: 20px; } form { display: flex; gap: 10px; margin-bottom: 10px; } input { flex-grow: 1; padding: 10px; border: 1px solid #ccc; border-radius: 4px; } button { padding: 10px 20px; background-color: #42b983; color: white; border: none; border-radius: 4px; cursor: pointer; } button:disabled { background-color: #ccc; cursor: not-allowed; } .error-message { color: #ff6b6b; margin-top: 5px; } .success-message { color: #42b983; margin-top: 5px; } /style关键点解析Action的异步与错误处理我们设计的addTodoaction返回了一个Promise。在组件中我们使用async/await来调用它这样可以很方便地进行成功和失败的处理。这是处理异步操作如API请求的标准模式。表单验证验证逻辑可以放在组件内如非空检查也可以放在action内如我们todos模块action中的校验。简单的、与UI强相关的校验放组件复杂的、与业务规则相关的校验放action。本地UI状态error和success消息是纯粹的UI状态它们不需要共享因此放在组件的data中即可无需污染Vuex store。4.4 整合主应用组件最后修改src/App.vue将三个组件组合起来template div idapp header h1Vuex 核心概念练习 Demo/h1 p通过计数器与任务列表实践 State, Getters, Mutations, Actions, Modules。/p /header main AddTodo / div classmain-content Counter / TodoList / /div /main footer p尝试操作计数器、添加/完成任务并打开Vue DevTools查看Vuex状态变化。/p /footer /div /template script import AddTodo from ./components/AddTodo.vue import Counter from ./components/Counter.vue import TodoList from ./components/TodoList.vue export default { name: App, components: { AddTodo, Counter, TodoList } } /script style #app { font-family: Avenir, Helvetica, Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; color: #2c3e50; } header, footer { text-align: center; margin-bottom: 30px; } .main-content { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; } media (max-width: 600px) { .main-content { grid-template-columns: 1fr; } } /style现在运行npm run serve一个完整的Vuex练习Demo就呈现在眼前了。你可以操作计数器添加、完成、删除任务并直观地感受到数据是如何通过Vuex在组件间流动和共享的。5. 核心概念深度解析与避坑指南通过上面的实践我们已经使用了Vuex的各个核心部分。下面我们来深入剖析一些关键概念和实际开发中容易踩的坑。5.1 Mutation 必须是同步函数这是铁律这是Vuex中最重要的一条规则。Mutation的作用是同步地修改State。为什么可追踪性Vue DevTools依赖于mutation的同步特性来提供“时光旅行”调试功能。每个mutation被记录后状态快照就被确定下来。如果mutation是异步的DevTools就无法准确知道状态何时改变调试会变得异常困难。状态确定性同步修改确保了在任何时候只要你知道了之前所有的mutation序列你就能计算出当前的确切状态。这是状态管理可预测性的基石。错误示例mutations: { ASYNC_UPDATE(state) { setTimeout(() { // 错误在mutation里执行异步操作 state.someValue ‘new’ }, 1000) } }正确做法将异步操作放在Action中。actions: { asyncUpdate({ commit }) { setTimeout(() { commit(‘SYNC_UPDATE’) // 在回调中提交同步的mutation }, 1000) } }, mutations: { SYNC_UPDATE(state) { state.someValue ‘new’ } }5.2 Action 与 Mutation 的职责划分很多人分不清什么逻辑该放Action什么该放Mutation。记住一个简单的原则Mutation只做一件事——同步地修改State。它应该尽可能简单、纯粹。Action可以处理业务逻辑。它可以包含异步操作调用API、条件判断、组合多个Mutation甚至dispatch其他Action。在我们的Demo中ADD_TODOmutation只负责向state.todos数组push一个新对象。addTodoaction负责校验输入内容非空然后才commit(‘ADD_TODO’)。如果未来需要先调用接口保存到服务器再更新本地状态也只需要修改这个action。5.3 模块的命名空间与访问方式开启namespaced: true后模块内的所有getter、action、mutation都会自动根据模块名调整路径。在组件中访问…mapState(‘counter’, [‘count’])…mapActions(‘todos’, [‘addTodo’])this.$store.getters[‘counter/doubleCount’]this.$store.dispatch(‘todos/addTodo’, someText)在模块内部访问全局内容如果模块内部的action需要访问根状态或提交根mutation可以通过rootState和rootGetters参数。actions: { someAction({ dispatch, commit, rootState, rootGetters }) { // 访问根状态 console.log(rootState.someRootState) // 提交根mutation commit(‘someRootMutation’, null, { root: true }) // 派发根action dispatch(‘someRootAction’, null, { root: true }) } }{ root: true }这个选项告诉Vuex这个commit/dispatch是针对根store的而不是当前命名空间模块。5.4 表单处理与Vuex State的绑定陷阱一个常见的需求是用一个表单如input v-model”userInput”来直接修改Vuex state。如果你直接使用v-model”$store.state.someModule.value”Vue会抛出警告因为你在组件中直接修改了store state这绕过了mutation破坏了单向数据流。解决方案有两种“双向绑定”计算属性input v-model”message” script computed: { message: { get() { return this.$store.state.someModule.message }, set(value) { this.$store.commit(‘someModule/UPDATE_MESSAGE’, value) } } } /script这种方式清晰明了是Vuex官方文档推荐的做法。使用Vuex的v-model辅助方法Vuex 2.x Vuex提供了一个mapFields辅助函数需要引入或者你可以手动实现类似功能。但在实际项目中第一种方法已经足够且更直观。在我们的Counter组件中设置计数的输入框就采用了类似第一种方案的思想使用组件本地数据inputValue绑定输入框然后在点击“设置”按钮时通过action去提交mutation来更新store state。5.5 Vuex State 的初始化与持久化初始化Store中的state必须提前初始化好所有属性。Vue的响应式系统无法探测到对象属性的添加或删除。如果你需要动态添加新的状态属性要么提前声明设为null或空值要么使用Vue.set(state, ‘newProp’, value)。持久化页面刷新后Vuex state会重置。如果有些状态需要持久化如用户登录token你需要配合localStorage或sessionStorage。通常的做法是在store初始化时尝试从localStorage读取数据。订阅mutationstore.subscribe在每次状态变更后将特定状态写回localStorage。也可以使用现成的插件如vuex-persistedstate。6. 使用Vue DevTools进行调试Vue DevTools是开发Vue和Vuex应用的神器。确保浏览器已安装Vue DevTools扩展。Vuex选项卡在这里你可以看到完整的Vuex状态树。我们的counter和todos模块会清晰展示。你可以直接修改state的值用于调试效果会实时反映在页面上。Mutation日志所有被提交的mutation都会按时间顺序列出。点击任意一个mutation你可以看到当时的状态快照和传递的payload参数。你可以通过右上角的“时光旅行”按钮回退到任意一个mutation之前的状态这对于复现bug极其有用。Action日志类似地你可以看到所有被dispatch的action及其执行情况。调试技巧当你的状态变化不符合预期时首先打开DevTools的Vuex面板查看mutation日志。确认是哪个mutation被触发payload是否正确。这能帮你快速定位问题是出在组件dispatch了错误的action或参数、action逻辑错误还是mutation修改state的逻辑错误。7. 项目总结与扩展思考通过这个看似简单的计数器任务列表Demo我们实际上串联了Vuex的所有核心概念。从模块化Store的搭建到组件中通过辅助函数映射state、getters、actions再到严格遵循Action-Mutation-State的单向数据流最后利用DevTools进行调试。每一个环节都是构建可维护中大型Vue应用的基础。可以尝试的扩展练习添加API模拟在todos模块的actions中使用setTimeout或axios模拟网络请求在添加、切换任务前先“请求服务器”成功后再commit mutation。练习异步action和加载状态的处理。实现数据持久化使用localStorage让任务列表在页面刷新后不丢失。尝试Vuex Composition API如果你使用Vue 3可以尝试在组合式APIsetup()函数中使用useStore来访问store这比mapState等辅助函数更灵活。探索更多场景比如实现一个购物车模块包含商品列表、添加至购物车、计算总价等功能进一步练习复杂状态和getter的计算。记住Vuex是一个约定大于配置的框架。遵循它的规则尤其是mutation同步原则和单向数据流一开始可能会觉得繁琐但随着项目复杂度的增长你会越来越感激它带来的结构清晰和可维护性。这个Demo就是你理解并掌握这套约定的第一步。
返回列表