Code前端首页关于Code前端联系我们

Vue2里的mapState是干啥的?怎么用才顺手?

terry 4小时前 阅读数 5 #Vue
文章标签 Vue2;mapState

想搞懂Vue2里的mapState咋回事?这东西在Vuex状态管理里挺常用,能帮咱们更简洁地处理组件里的状态映射,今天就从“是啥”“咋用”“有啥坑”这些角度唠明白,新手也能跟着学会~

mapState到底是个啥?

咱先把Vuex和state的关系理清楚:Vuex是Vue的状态管理库,state就是存全局数据的“仓库”,比如用户信息、购物车商品数量这些,全项目通用的数据,都存在state里。

mapState呢?它是Vuex提供的「辅助函数」,作用特别直接——把Vuex里的state数据,映射到当前组件的计算属性里,举个大白话例子:原本你得在组件里写 computed: { userInfo() { return this.$store.state.userInfo } } 才能拿到state里的用户信息,现在用mapState,一行代码就能搞定多个state的映射,不用重复写this.$store.state.xxx 这种模板代码,效率直接拉满!

啥场景下非得用mapState?

不是所有情况都得用mapState,但碰到这两类场景,用它能省老多事儿:

  1. 组件需要多个state数据:比如一个页面要同时显示「用户昵称」「购物车商品数」「主题颜色」这三个存在state里的信息,要是不用mapState,得写三个计算属性,每个都重复this.$store.state.xxx;用mapState的话,一行数组写法就能把这三个全映射过来。

  2. 追求代码简洁性:Vuex的核心思想是“单一状态树”,但实际项目里state可能很臃肿,mapState能帮你把和当前组件无关的state过滤掉,只映射需要的,代码看起来更清爽,后期维护也方便。

mapState基础用法咋上手?

学会这三种写法,基本能覆盖90%的场景:

数组形式(最简单直接)

先从Vuex里导入mapState,然后在组件的computed里用数组列出来要映射的state名称,举个完整例子:

<template>
  <div>
    <!-- 直接用映射后的计算属性 -->
    <p>用户昵称:{{ userInfo.name }}</p>
    <p>购物车数量:{{ cartCount }}</p>
  </div>
</template>
<script>
import { mapState } from 'vuex' // 先导入
export default {
  name: 'MyComponent',
  computed: mapState(['userInfo', 'cartCount']) // 数组里写state的key
}
</script>

这种写法的前提是:组件里的计算属性名,和Vuex state里的key完全一样,比如state里有个叫userInfo的对象,组件里计算属性也叫userInfo,这样映射后直接用就行。

对象形式(解决命名冲突)

要是组件自身的data、props,或者其他计算属性,和state的key重名了咋办?这时候用对象形式,给映射后的计算属性改个新名字,比如state里有个userInfo,但组件里已经有个叫user的变量了,就可以这样写:

computed: mapState({
  // 新名字: 'state里的key'
  userData: 'userInfo', 
  cartNum: 'cartCount'
})

这样组件里用{{ userData.name }}{{ cartNum }} 就能拿到对应数据,避免命名冲突。

函数形式(需要自定义逻辑)

有时候映射state时,得结合组件自身的属性,或者对state数据做简单处理,这时候用「函数形式」,参数是Vuex的state,还能拿到组件的this(注意别用箭头函数,否则this指向不对),比如state里存的是用户年龄,组件里要显示“用户年龄:xxx岁”:

computed: mapState({
  userAgeWithUnit(state) { 
    // state是Vuex的state对象,this是当前组件实例
    return `用户年龄:${state.userAge}岁` 
  }
})

结合自定义计算属性(灵活混用)

实际开发中,组件里可能既有从state映射来的计算属性,又有自己的业务逻辑计算属性,这时候用「对象展开符(...)」把mapState和自定义计算属性合并:

computed: {
  ...mapState(['userInfo', 'cartCount']), // 先映射state
  // 自己的计算属性
  isCartEmpty() {
    return this.cartCount === 0
  }
}

这样组件里既能用userInfo这些state数据,又能调用isCartEmpty判断购物车是否为空,灵活度拉满~

用mapState容易踩的坑有哪些?

别觉得学会用法就万事大吉了,这些“小陷阱”稍不注意就栽跟头:

  1. 忘记放在computed里:mapState本质是生成计算属性,所以必须写在computed选项里!要是错放到methods里,不仅拿不到响应式数据,页面还不更新,debug时能把人急死。

  2. 命名冲突埋雷:前面说过用对象形式解决,但要是没注意,组件的data、props和mapState映射的名字重复,后定义的会把先定义的覆盖,比如组件data里有个userInfo,mapState又映射了userInfo,那最终计算属性里的userInfo会变成state里的值,把data里的覆盖掉,逻辑直接乱套。

  3. 箭头函数搞丢this:用函数形式写mapState时,要是写成箭头函数userAge: (state) => state.userAge + this.name,这里的this就不是组件实例了(箭头函数没有自己的this,会向上找作用域),所以必须用普通函数写法,让this指向组件:userAge(state) { return state.userAge + this.name }

mapState和其他Vuex辅助函数咋配合?

Vuex里还有mapGetters(映射getters)、mapActions(映射actions)这些辅助函数,和mapState配合起来用,能让组件逻辑更丝滑:

  • 和mapGetters一起用:getters是state的“派生状态”(比如购物车商品总价,需要根据cartList计算),把mapState和mapGetters都放computed里:
computed: {
  ...mapState(['cartList']),
  ...mapGetters(['cartTotalPrice'])
}

组件里既能拿到原始的购物车列表(cartList),又能直接用getters计算好的总价(cartTotalPrice)。

  • 和mapActions一起用:actions是处理异步操作的,虽然mapActions要放在methods里,但思路一样——简化this.$store.dispatch('xxx')的写法。
methods: {
  ...mapActions(['addToCart']), // 映射actions里的addToCart方法
  handleAdd() {
    this.addToCart({ productId: 123 }) // 直接调用,不用写this.$store.dispatch
  }
}

模块(Module)里的mapState咋用?

实际项目中,Vuex的state会按功能拆成多个模块(比如user模块、cart模块),这时候用mapState得注意命名空间(namespace)

假设我们有个cart模块,state里有cartCount,并且开启了命名空间:

// store/modules/cart.js
export default {
  namespaced: true, // 开启命名空间
  state: { cartCount: 3 },
  // ...其他配置
}

这时候映射cart模块的state,得给mapState传两个参数:模块名和要映射的state数组/对象:

computed: {
  ...mapState('cart', ['cartCount']), // 第一个参数是模块名
  // 或者对象形式
  ...mapState('cart', { cartNum: 'cartCount' })
}

这样就能精准拿到某个模块下的state,避免不同模块间的state重名冲突~

mapState是Vue2 + Vuex开发里简化状态映射的神器,核心就是“少写重复代码,让组件更专注业务”,只要记住「辅助函数的本质是生成计算属性」「三种写法应对不同场景」「避开命名和this的坑」,再结合模块和其他辅助函数,状态管理这块儿就稳了~要是你刚开始学Vuex,建议把项目里的小功能(比如购物车、用户信息)用mapState练手,写着写着就熟练啦~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门