mapState是 Vuex 提供的辅助函数(语法糖) ,核心作用是简化 Vue 组件中获取 Vuexstate数据的代码 ------ 它能批量将 Vuex 仓库中的state变量,映射为组件的计算属性 ,避免手动为每个state变量编写重复的计算属性,同时依托计算属性的响应式特性,保证state变化时组件数据同步更新。前置准备:Vuex Store 基础配置
先定义 Vuex 的仓库(
store/index.js),作为所有写法的数据源,包含根级state和模块化state
javascript
// 1. 导入Vue核心库(Vue2必备,用于安装Vuex插件)
import Vue from 'vue'
// 2. 导入Vuex状态管理库(实现组件间数据共享、状态统一管理)
import Vuex from 'vuex'
// 3. 安装Vuex插件:让Vue实例能够识别并使用Vuex的相关功能(如$store)
Vue.use(Vuex)
// 4. 定义Vuex子模块:user模块(负责管理用户相关的状态,开启命名空间避免命名冲突)
const userModule = {
// 开启命名空间:
// - 作用1:子模块的mutations/actions/getters会被限定在当前模块下,避免和根/其他模块冲突
// - 作用2:调用子模块的方法时,需要指定模块名(如commit('user/setToken'))
namespaced: true,
// 子模块的state:存储当前模块的私有状态(仅该模块可直接访问,其他模块需通过命名空间获取)
state: {
token: 'module-token-123', // 子模块下的token(用户令牌)
avatar: '/module-avatar.png' // 子模块下的用户头像路径
},
// 子模块的mutations:唯一能修改当前模块state的方法(同步操作)
mutations: {
// 修改子模块的token值
// state:当前子模块的state对象(非根state);val:调用mutations时传入的新值
setToken(state, val) {
state.token = val
}
}
}
// 5. 创建并导出Vuex的根Store实例(整个应用的状态总仓库)
export default new Vuex.Store({
// 根级state:存储整个应用的公共状态(所有组件均可访问,优先级低于子模块同名state)
state: {
token: 'root-token-456', // 根级的token(用户令牌)
avatar: '/root-avatar.png', // 根级的用户头像路径
user: '张三' // 根级存储的用户名
},
// 根级mutations:唯一能修改根级state的方法(同步操作,不能写异步代码)
mutations: {
// 修改根级的token值
// state:根级的state对象;val:调用时传入的新token值
setToken(state, val) {
state.token = val
}
},
// 注册子模块:将userModule挂载到根Store中,命名为「user」
// 挂载后,子模块的状态可通过 $store.state.user.token 访问
// 子模块的mutations需通过 $store.commit('user/setToken', 新值) 调用
modules: {
user: userModule // 键名「user」为模块名,值为定义好的子模块对象
}
})
结合代码,从基础到进阶 拆解 mapState 的所有核心写法,关联代码上下文,理解不同写法的适用场景和等价逻辑:
一、mapState 核心作用(先锚定代码中的基础用法)
在示例代码中,mapState 最核心的价值是替代手动编写重复的计算属性:
js
javascript
// 代码中注释的「手动写法」(繁琐且冗余)
computed: {
token() { return this.$store.state.token },
avatar() { return this.$store.state.avatar }
}
// 代码中使用的「mapState简化写法」
computed: mapState(['token','avatar'])
mapState是 Vuex 提供的辅助函数 ,本质是返回一个 "计算属性对象",批量将 Vuex 的state映射为组件的计算属性,且依托计算属性的响应式特性 ,保证state变化时组件数据同步更新(这也是代码中推荐用mapState/ 计算属性,而非data取state的核心原因)。
二、mapState 核心写法
写法 1:数组写法(代码中的基础用法)
组件计算属性名 ≡ Vuex
state变量名,极简映射,模板直接用同名变量。
语法(对应代码)js
javascript
computed: mapState(['state变量名1', 'state变量名2'])
代码中的应用 js
javascript
<template>
<div class="about">
<!-- 演示Vuex的mapState辅助函数「数组写法」:批量映射根级state到组件计算属性 -->
<h2>mapState 数组写法</h2>
<!-- 映射后的computed属性:token(对应根级store的state.token) -->
<p>根级token:{{ token }}</p>
<!-- 映射后的computed属性:avatar(对应根级store的state.avatar) -->
<p>根级avatar:{{ avatar }}</p>
</div>
</template>
<script>
// 从vuex导入mapState辅助函数:用于简化「将Vuex的state映射为组件计算属性」的写法
import { mapState } from 'vuex'
export default {
// 组件名称(非必需,用于Vue开发者工具识别、递归组件等)
name: 'AboutView',
// 计算属性:Vuex的mapState辅助函数(数组写法)
// 核心作用:批量将根级store.state中的指定属性,映射为组件的同名计算属性
// 数组写法规则:数组中的元素 = 根级state的属性名,映射后组件内可直接用该名称访问
computed: mapState(['token', 'avatar'])
// 【等价的手动写法】:mapState本质是简化了以下重复的computed代码
// computed: {
// // 手动映射根级state的token为组件计算属性
// token() {
// return this.$store.state.token
// },
// // 手动映射根级state的avatar为组件计算属性
// avatar() {
// return this.$store.state.avatar
// }
// }
}
</script>
说明
- 要求:组件内计算属性名 ≡ Vuex state 中的变量名 (代码中
token/avatar在 Vuex 的state中存在,映射后模板可直接用{``{token}}/{``{avatar}});- 模板联动:代码模板中
<p>{``{token}}</p>等价于<p>{``{this.$store.state.token}}</p>,但写法更简洁;- 适用场景:简单场景,组件使用的属性名和
state变量名完全一致(如代码仅需直接映射token/avatar的场景)。
写法 2:对象写法(进阶,扩展代码)
当需要自定义计算属性名 ,或对 state 数据做二次处理时使用,分 3 种细分场景:
场景 2.1:自定义计算属性名(重命名)
如果想让组件内的属性名和
state变量名不一致(比如代码中想把token改叫userToken):
javascript
<template>
<div class="about">
<!-- 演示Vuex的mapState辅助函数「对象写法」:支持自定义属性名(重命名)映射根级state -->
<h2>mapState 对象写法(重命名)</h2>
<!-- userToken:组件内自定义名称,对应根级store.state.token -->
<p>重命名后的token:{{ userToken }}</p>
<!-- userAvatar:组件内自定义名称,对应根级store.state.avatar -->
<p>重命名后的avatar:{{ userAvatar }}</p>
</div>
</template>
<script>
// 从vuex导入mapState辅助函数:用于简化「Vuex state → 组件计算属性」的映射逻辑
import { mapState } from 'vuex'
export default {
// 组件名称(用于Vue开发者工具识别、递归组件等场景)
name: 'AboutView',
// 计算属性:mapState的「对象写法」------解决数组写法"属性名必须和state一致"的限制
computed: mapState({
// 【键】:组件内要使用的自定义属性名(可随意命名,避免和组件内其他变量冲突)
// 【值】:Vuex根级state中的原始属性名(需和store.state中的属性完全一致)
userToken: 'token', // 映射:this.userToken → this.$store.state.token
userAvatar: 'avatar' // 映射:this.userAvatar → this.$store.state.avatar
})
// 【等价的手动写法】:mapState对象写法本质是简化了以下重复代码
// computed: {
// // 自定义属性名userToken,返回根级state的token
// userToken() {
// return this.$store.state.token
// },
// // 自定义属性名userAvatar,返回根级state的avatar
// userAvatar() {
// return this.$store.state.avatar
// }
// }
}
</script>
mapState对象写法的核心优势 :数组写法要求「组件内属性名」必须和「Vuex state 属性名」完全一致,而对象写法支持自定义属性名(重命名),避免:
- 组件内变量名冲突(比如组件已有
token变量,可映射为userToken);- 变量名语义化(比如把
avatar映射为userAvatar,更易读)。对象写法的严格规则:
- 键(Key):组件内要使用的自定义属性名(模板中直接用这个名);
- 值(Value):Vuex state 中的原始属性名 (必须和
store.state中的属性一一对应)。
场景 2.2:处理 state 数据(箭头函数)
通过箭头函数对
state数据做格式化 / 二次处理,模板直接用处理后的值。
javascript
computed: mapState({
// 对token做处理:为空时显示「未登录」
token: (state) => state.token || '未登录',
// 对avatar拼接路径:代码中可直接用{{avatar}}显示处理后的值
avatar: (state) => '/static/images/' + state.avatar
})
⚠️ 注意:箭头函数无法访问组件实例的 this (比如代码中的 newToken 是 data 变量,箭头函数里拿不到)。
箭头函数无自身 this,无法访问组件的 data/methods,仅适合纯处理 state 数据。
场景 2.3:访问组件 this(普通函数)
用普通函数替代箭头函数,可访问组件自身的
data/methods,结合组件处理state数据。(比如代码中的newToken)
javascript
computed: mapState({
// 普通函数,this指向组件实例(可访问data/methods)
token(state) {
// 结合代码中的newToken:输入框有值时显示输入值,否则显示state.token
return this.newToken || state.token
}
})
写法 3:展开运算符写法(实际开发最常用)
代码中直接写
computed: mapState(['token','avatar'])会覆盖整个computed,如果组件需要自定义计算属性(比如给token做二次处理),必须用 ES6 展开运算符**...** 兼容:
javascript
<template>
<div class="about">
<!-- 模板中同时使用自定义计算属性和mapState映射属性 -->
<h2>mapState 展开运算符写法</h2>
<p>mapState映射的token:{{ token }}</p> <!-- 来自...mapState(['token']) -->
<p>mapState映射的avatar:{{ avatar }}</p> <!-- 来自...mapState(['avatar']) -->
<p>自定义计算属性(token翻倍):{{ doubleToken }}</p> <!-- 自定义计算属性,依赖映射的token -->
</div>
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'AboutView',
computed: {
// 1. 组件自定义计算属性
// this.token 是mapState映射后的计算属性(对应this.$store.state.token)
doubleToken() {
return this.token ? this.token + this.token : ''
},
// 2. 展开mapState映射的属性(和自定义属性共存)
// ... 展开运算符:把mapState返回的{token: fn, avatar: fn}对象展开为两个独立的计算属性
// 若省略...,computed会被mapState的返回值完全覆盖,doubleToken会失效
...mapState(['token','avatar'])
// 展开对象写法的mapState
// ...mapState({
// userToken: 'token',
// userAvatar: 'avatar'
})
}
}
</script>
✅ 这是真实项目中最推荐的写法(兼顾简洁性和扩展性)。
写法 4:模块化(命名空间)写法(扩展真实项目场景)
大型项目中 Vuex 会分模块(比如把 user 相关的 state 放到 user 模块),此时需要指定模块名才能映射:
步骤 1:先配置 Vuex 模块化(示例)
javascript
// store/modules/user.js(新增模块文件)
export default {
namespaced: true, // 必须开启命名空间
state: {
token: 'module-123',
avatar: '/img/module-avatar.png'
},
mutations: {
setToken(state, val) {
state.token = val
}
}
}
// store/index.js(注册模块)
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
Vue.use(Vuex)
export default new Vuex.Store({
modules: { user } // 注册user模块
})
步骤 2:组件中映射模块化的 state
javascript
// 替代代码中的mapState写法(映射user模块的state)
computed: {
// 方式1:模块化 + 数组写法
...mapState('user', ['token','avatar']),
// 方式2:模块化 + 对象写法(自定义名称)
// ...mapState('user', {
// userToken: 'token',
// userAvatar: 'avatar'
// })
}
此时模板中 {``{token}} 对应的是 this.$store.state.user.token,和代码中原始的根级 state 映射逻辑一致,只是多了模块维度。
三、各写法对比(结合代码场景)
| 写法类型 | 代码中的对应 / 扩展示例 | 核心特点 | 适用场景 | ||
|---|---|---|---|---|---|
| 数组写法 | mapState(['token','avatar']) |
最简,属性名和 state 变量名一致 | 简单场景(如代码仅需直接映射) | ||
| 对象写法(重命名) | mapState({userToken: 'token'}) |
自定义组件内属性名 | 需要重命名 state 变量时 | ||
| 对象写法(处理数据) | `mapState({token: (state) => state.token | ' 未登录 '})` | 纯处理 state 数据,无组件 this | 格式化 state 数据 | |
| 对象写法(访问 this) | `mapState({token: function(state) { return this.newToken | state.token }})` | 结合组件自身数据 | 联动组件 data/methods 时 | |
| 展开运算符写法 | {doubleToken(){...}, ...mapState(['token','avatar'])} |
兼容自定义计算属性 | 组件有自定义计算属性(推荐) | ||
| 模块化写法 | ...mapState('user', ['token','avatar']) |
映射模块内的 state | 大型项目 Vuex 分模块时 |
四、核心注意事项(结合代码)
mapState必须写在computed中:代码中mapState挂载在computed下,依托计算属性的响应式,保证state变化时模板同步更新(对比data中取state非响应式的坑);- 避免直接覆盖
computed:代码中直接computed: mapState(...)仅适用于无自定义计算属性的场景,有自定义属性时必须用展开运算符;- 模块化需开启
namespaced: true:否则mapState('模块名', ...)无法识别模块;- 箭头函数无
this:如需访问代码中的newToken(组件data),必须改用普通函数。
总结
从代码中的基础数组写法,到进阶的对象写法、模块化写法,
mapState的核心是简化计算属性编写,且始终依托计算属性的响应式特性。实际开发中,优先用「展开运算符 + 数组 / 对象写法」(兼顾简洁和扩展性),分模块的项目则用「模块化写法」。