
程序猿之Vue - Day07 - Vuex 状态管理:state、mutations、actions、getters 与模块化实战
一、Vuex 概述
Vuex 是 Vue.js 官方提供的状态管理工具。用于集中式地存储和管理多个组件之间共享的状态。
 
 
1. 适用场景
- 
	多个组件共享同一个状态(如用户登录信息) 
- 
	多个组件需要协调修改同一份数据(如购物车) 
2. 优势
- 
	状态集中管理,数据一致性强 
- 
	响应式系统自动更新视图 
- 
	辅助函数简化操作 
- 
	适合中大型项目结构 
二、初始化 Vuex 项目
 
 
1. 安装 Vuex(Vue2)
yarn add vuex@3
# 或 npm install vuex@3
2. 创建 store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({})
export default store
3. 在 main.js 中挂载
import store from './store'
new Vue({
  store,
  render: h => h(App)
}).$mount('#app')
三、State - 状态定义与访问
1. 定义状态
state: {
  count: 0
}
2. 使用状态
- 
	模板中: 
<p>{{ $store.state.count }}</p>
- 
	计算属性中: 
computed: {
  count () {
    return this.$store.state.count
  }
}
- 
	mapState 辅助函数: 
import { mapState } from 'vuex'
computed: {
  ...mapState(['count'])
}
四、Mutations - 同步更改状态
1. 定义 Mutations
mutations: {
  increment (state) {
    state.count++
  },
  changeCount (state, newCount) {
    state.count = newCount
  }
}
2. 提交 Mutation
this.$store.commit('increment')
this.$store.commit('changeCount', 100)
3. mapMutations 辅助函数
import { mapMutations } from 'vuex'
methods: {
  ...mapMutations(['increment', 'changeCount'])
}
五、Actions - 异步更改状态
1. 定义 Actions
actions: {
  asyncChangeCount ({ commit }, payload) {
    setTimeout(() => {
      commit('changeCount', payload)
    }, 1000)
  }
}
2. 派发 Actions
this.$store.dispatch('asyncChangeCount', 666)
3. mapActions 辅助函数
import { mapActions } from 'vuex'
methods: {
  ...mapActions(['asyncChangeCount'])
}
六、Getters - 派生状态
1. 定义 Getters
getters: {
  doubleCount: state => state.count * 2
}
2. 使用 Getters
<p>{{ $store.getters.doubleCount }}</p>
或
import { mapGetters } from 'vuex'
computed: {
  ...mapGetters(['doubleCount'])
}
七、开启严格模式
const store = new Vuex.Store({
  strict: true,
  state: { count: 0 },
  mutations: { increment (state) { state.count++ } }
})
在严格模式下,state 必须通过 mutation 更改,否则抛出错误。
八、Modules - 模块化管理
1. 定义模块 user.js
export default {
  namespaced: true,
  state: () => ({
    userInfo: { name: '张三', age: 18 }
  }),
  mutations: {
    setUser (state, payload) {
      state.userInfo = payload
    }
  },
  actions: {
    setUserAsync ({ commit }, payload) {
      setTimeout(() => {
        commit('setUser', payload)
      }, 1000)
    }
  },
  getters: {
    upperName: state => state.userInfo.name.toUpperCase()
  }
}
2. 注册模块
import user from './modules/user'
export default new Vuex.Store({
  modules: {
    user
  }
})
九、模块中使用状态、方法、派生数据
1. 使用 State
this.$store.state.user.userInfo.name
...mapState('user', ['userInfo'])
2. 使用 Mutations
this.$store.commit('user/setUser', payload)
...mapMutations('user', ['setUser'])
3. 使用 Actions
this.$store.dispatch('user/setUserAsync', payload)
...mapActions('user', ['setUserAsync'])
4. 使用 Getters
this.$store.getters['user/upperName']
...mapGetters('user', ['upperName'])
十、综合实战:Vuex 购物车模块
1. 模块定义:cart.js
import axios from 'axios'
export default {
  namespaced: true,
  state: () => ({
    list: []
  }),
  mutations: {
    updateList (state, payload) {
      state.list = payload
    },
    updateCount (state, { id, count }) {
      const item = state.list.find(i => i.id === id)
      item && (item.count = count)
    }
  },
  actions: {
    async getList ({ commit }) {
      const res = await axios.get('http://localhost:3000/cart')
      commit('updateList', res.data)
    },
    async updateCount ({ commit }, payload) {
      await axios.patch(`http://localhost:3000/cart/${payload.id}`, {
        count: payload.count
      })
      commit('updateCount', payload)
    }
  },
  getters: {
    total: state => state.list.reduce((acc, cur) => acc + cur.count, 0),
    totalPrice: state => state.list.reduce((acc, cur) => acc + cur.count * cur.price, 0)
  }
}
2. 挂载模块
import cart from './modules/cart'
const store = new Vuex.Store({
  modules: { cart }
})
3. 页面中调用
created () {
  this.$store.dispatch('cart/getList')
}
computed: {
  ...mapState('cart', ['list']),
  ...mapGetters('cart', ['total', 'totalPrice'])
}
十一、总结
| 核心概念 | 作用描述 | 修改方式 | 
|---|---|---|
| state | 储存共享数据 | 通过 mutations 修改 | 
| mutations | 定义同步更改 state 的方法 | commit('方法名', 参数) | 
| actions | 异步逻辑,最终调用 mutations | dispatch('方法名', 参数) | 
| getters | 派生数据计算 | 用于展示或缓存数据 | 
| modules | 模块化拆分项目结构 | 提高可维护性 | 
一、理论理解
Vue 是一个渐进式框架,其核心理念是组件化和数据驱动。在小型项目中,组件之间的数据传递可以通过 props 与 $emit 实现,但随着项目规模扩大,组件层级增多、状态流转路径变长,管理多个组件间共享状态就变得困难。
这时引入 Vuex 的状态管理机制就成为一种“工程刚需”:
- 
	它构建了一套全局的状态管理架构,遵循“单向数据流”原则; 
- 
	使用 state → view → action → mutation → state 的闭环模式,提升了状态的透明性、可调试性和可维护性; 
- 
	它将“响应式”和“集中式状态管理”结合在一起,天然支持 Vue 的响应式特性; 
- 
	通过 辅助函数(如 mapState、mapMutations),使状态管理的接入成本进一步降低; 
- 
	借助 模块化(modules) 机制,Vuex 可以将不同业务领域的状态拆分为独立作用域,适配中大型应用需求。 
理解 Vuex 的本质,就是理解“状态管理的复杂性来源于状态之间的依赖性和修改来源的不确定性”,Vuex 通过规则和约束提供了解决方案。
二、大厂实战理解(BAT、字节、Google、OpenAI、NVIDIA)
✅ 字节跳动
字节前端团队在字节小程序、飞书文档、抖音等中后台系统中,往往不会直接使用 Vuex,而是借助更高级的状态方案(如 Zustand / Recoil 或者自研 hooks-based 状态系统),但 Vuex 的设计思想仍然是核心原型:
- 
	明确区分状态读取(getters)与更新(mutations) 
- 
	将副作用逻辑(如异步请求)从业务组件中抽离到 actions 
- 
	将状态模块化,与页面/功能模块一一对应,减少耦合 
字节对前端状态的要求是“功能内聚、变更集中、追踪方便”,这和 Vuex 的模块化命名空间 + 单向数据流机制高度契合。
✅ 腾讯(微信团队)
微信小程序生态中,Vuex 是初学者常用的状态管理参考模型。在 企业微信管理后台、腾讯课堂 CRM 系统 等 Vue 项目中,Vuex 是默认的状态管理方案。腾讯强调:
“在多人协作开发中,Vuex 的集中式状态记录和 strict 模式有助于防止状态乱改,是开发者协作的'契约基础'。”
✅ Google / OpenAI / NVIDIA
这些公司更强调架构合理性与可维护性,普遍使用 React+Redux,但它们对于“状态管理”的通识要求如下:
- 
	单一数据源,便于集中观测、调试、审计 
- 
	状态更新透明,追踪路径唯一 
- 
	支持异步任务与中间件的扩展能力 
Vuex 恰好就是对 Redux 思想的 Vue 化实现:
- 
	Redux 的 reducer 对应 Vuex 的 mutations 
- 
	Redux 的 middleware 对应 Vuex 的 actions 
- 
	Redux 的 store modules 对应 Vuex 的 modules 
- 
	Vuex 内部通过 Vue 的响应式系统天然构建了“store → view”的数据联动 
在开源世界中,Vuex 被看作是“前端状态设计教科书级”的存在。无论你未来是否继续使用 Vuex,它都值得你深入理解。
面试题
一、基础必问
❓1. Vuex 是什么?它解决了什么问题?
答:
Vuex 是 Vue 官方提供的状态管理工具,它实现了数据的集中式管理和统一的读写流程。
它解决了多组件之间数据共享与同步的复杂问题,让状态可预测、可调试、可追踪。
❓2. Vuex 中为什么 mutations 必须是同步的?
答:
Vuex 设计时要求 mutations 是同步的,是为了方便状态变更的记录与调试。
Vuex 支持 Devtools 记录每一次状态变更(Time-travel Debugging),异步会导致状态变更无法精确追踪 —— 所以异步操作必须放在 actions 中,由 actions 触发同步的 mutations。
❓3. Vuex 的 state 为什么是响应式的?
答:
Vuex 的 state 本质上是使用 Vue 实例来包装的,借助 Vue 的响应式系统(Object.defineProperty 或 Proxy)实现数据的响应式追踪。
当组件访问 state 时,本质上是建立了依赖关系,一旦 state 改变,相关组件会自动重新渲染。
二、中阶理解
❓4. mapState 和 $store.state 有什么区别?什么时候用 mapState?
答:
- 
	$store.state.xxx是直接访问 store 中的 state;
- 
	mapState()是辅助函数,用于将 state 映射为计算属性,语法更优雅、响应式更自然;
- 
	实际开发中更推荐使用 mapState,尤其在组合多个状态源时,它支持模块命名空间和对象语法配置。
❓5. Vuex 如何实现模块化?模块化后 getters/mutations/actions 如何命名防止冲突?
答:
Vuex 支持通过 modules 属性进行模块划分,每个模块都有自己的 state/mutations/actions/getters。
为防止命名冲突,需要在模块中设置:
export default {
  namespaced: true
}
此后,访问模块内属性时使用:模块名/属性名 的命名方式。
如调用 mutation:
store.commit('user/setUserInfo', payload)
三、高阶原理
❓6. 请解释 Vuex 中的「单向数据流」模型及其设计原因?
答:
Vuex 遵循「单向数据流」:View → Dispatch Action → Commit Mutation → Update State → Rerender View
设计这个流程的原因:
- 
	降低状态变更的不可控性 
- 
	保证所有状态变更都有迹可循(mutation 是唯一途径) 
- 
	避免多点同时修改造成“状态竞态冲突” 
- 
	更利于 debugging 和 devtools 支持 
❓7. 为什么 Vuex 在 Vue3 中被 Pinia 替代?Vuex 存在哪些设计缺陷?
答:
Vuex 的一些痛点在 Vue3 时代逐渐暴露出来:
- 
	语法冗长(大量样板代码) 
- 
	mutations/action 分离强制较多,不够灵活 
- 
	TypeScript 支持不够优雅 
- 
	写法不够组合式(不支持 Composition API) 
Pinia 是 Vue 官方推荐的新状态管理库,设计上更轻量、组合式、支持 TS 推导更好,但其核心理念仍继承了 Vuex。
场景题
✅ 场景一:动态 Tab 多页签系统中的状态同步问题
❓问题描述:
你正在为字节跳动开发一个多页签页面管理系统,用户可以打开多个「编辑页签」,每个页签都对应一个页面组件。要求:
- 
	所有页签组件都共用同一份用户草稿数据(如草稿 ID) 
- 
	用户在任何一个页签中修改草稿内容,其他页签中应实时同步 
请问:
- 
	如何设计 Vuex 的 state 和 mutation 实现该需求? 
- 
	如何防止组件间数据写入冲突? 
✅参考解法:
- 
	使用 Vuex 的 state.draftData存储草稿内容
- 
	所有页签组件使用 mapState读取该数据
- 
	通过 mutation: updateDraft实现内容修改,使用 immutable 风格(复制新对象)防止突变副作用
- 
	可考虑在 state 中添加 activeTabId标识当前激活页签,结合节流防止重复渲染
✅ 场景二:复杂表单组件的状态解耦问题(京东后台)
❓问题描述:
你在京东开发一个复杂的商品上架表单,包含 8 个模块组件(基本信息、图片上传、价格设置、库存、类目选择等)。每个模块都有自己的数据,但需要:
- 
	在顶部页面统一显示「表单是否有变动」 
- 
	页面提交按钮汇总所有模块数据 
请问:
- 
	如何使用 Vuex 模块化管理这些表单状态? 
- 
	如何判断表单是否变更(脏值校验)? 
✅参考解法:
- 
	使用 Vuex 的 modules 机制,每个表单块作为一个 module,如 product/basic,product/images等
- 
	每个模块存储自己的字段数据,并暴露 getter isChanged
- 
	父组件通过 mapGetters('product', ['isFormChanged'])汇总各模块的变动状态,逻辑统一放在父模块 getter 中
- 
	使用 submitForm()action 汇总所有模块 state 并提交
✅ 场景三:Vuex 异步状态管理与网络状态容错(腾讯课堂)
❓问题描述:
在腾讯课堂的课件编辑系统中,用户编辑的操作通过 Vuex actions 异步保存到服务器(自动保存机制)。现在需要:
- 
	支持离线编辑,网络异常时缓存在本地 
- 
	网络恢复后,自动同步本地修改 
你会如何设计 Vuex 中的 actions 实现这个需求?
✅参考解法:
- 
	在 actions: saveDocument中,使用try...catch包裹 axios 请求
- 
	如果请求失败,调用 localStorage.setItem('pendingSave', content)
- 
	使用网络状态监听器( window.addEventListener('online')),检测到恢复后重新 dispatchsaveDocument
- 
	Vuex 可在 state 中维护 isSaving、saveError状态,提供 UI 提示
✅ 场景四:大型项目中避免 Vuex 冗余依赖的问题(阿里飞猪)
❓问题描述:
飞猪旅行的中后台系统包含 30+ 页面模块。随着模块数量增加,Vuex 全局状态仓库过于庞大,导致维护困难、组件耦合性高。
如何优化 Vuex 状态结构,以保证模块解耦、按需加载、首屏性能?
✅参考解法:
- 
	使用 Vuex 动态注册模块( store.registerModule)实现懒加载模块
- 
	页面初始化时,按需加载当前页面所需的模块 
- 
	使用 namespaced: true保证模块之间完全隔离
- 
	对于不常变的数据,考虑使用 localStorage + 缓存策略替代 Vuex 
✅ 场景五:Vuex 状态同步到 URL 的需求(OpenAI 内部工具类系统)
❓问题描述:
你正在开发一个 OpenAI 的内部数据可视化平台,用户配置可视化筛选条件(例如模型类型、时间区间、输出维度),这些状态保存在 Vuex 中。
需求:用户刷新页面或分享链接时,依然保留当前配置状态。
如何实现 Vuex state 和 URL 的双向绑定?
✅参考解法:
- 
	使用 vue-router 的 query 参数同步状态,如 ?model=gpt-4&range=last7days
- 
	在 Vuex 中维护 filters模块,使用watch监听状态变化
- 
	状态变化时同步更新路由参数: this.$router.replace({ query: store.state.filters })
- 
	页面初始化时,根据 URL 参数初始化 Vuex(通过 router.beforeEach) 

 
                   
                   
                   
                   
                             
       
           
                 
                 
                 
                 
                 
                
               
                 
                 
                 
                 
                
               
                 
                 扫一扫
扫一扫
                     
                     
              
             
                   963
					963
					
 被折叠的  条评论
		 为什么被折叠?
被折叠的  条评论
		 为什么被折叠?
		 
		  到【灌水乐园】发言
到【灌水乐园】发言                                
		 
		 
    
   
    
   
             
					 
					 
					


 
            