mapState辅助函数 #

简介 #

当一个组件需要获取多个状态时,将这些状态都声明为计算属性会有些重复和冗余。mapState 辅助函数可以帮助我们生成计算属性,让你少按几次键。

基本用法 #

数组语法 #

当映射的计算属性名称与 state 子节点名称相同时,可以使用数组语法:

javascript
import { mapState } from 'vuex'

export default {
  computed: {
    // 使用对象展开运算符将此对象混入到外部对象中
    ...mapState([
      'count',
      'user',
      'todos'
    ])
  }
}

// 等价于
export default {
  computed: {
    count() {
      return this.$store.state.count
    },
    user() {
      return this.$store.state.user
    },
    todos() {
      return this.$store.state.todos
    }
  }
}

对象语法 #

当需要重命名或使用自定义逻辑时,使用对象语法:

javascript
import { mapState } from 'vuex'

export default {
  computed: {
    ...mapState({
      // 箭头函数
      count: state => state.count,
      
      // 传字符串参数
      user: 'user',
      
      // 使用 this 访问组件局部状态
      fullName(state) {
        return `${state.user.firstName} ${this.lastName}`
      }
    })
  }
}

使用场景 #

场景一:简单映射 #

vue
<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>User: {{ user.name }}</p>
    <p>Todos: {{ todos.length }}</p>
  </div>
</template>

<script>
import { mapState } from 'vuex'

export default {
  computed: {
    ...mapState(['count', 'user', 'todos'])
  }
}
</script>

场景二:重命名映射 #

vue
<template>
  <div>
    <p>Current Count: {{ currentCount }}</p>
    <p>Current User: {{ currentUser.name }}</p>
  </div>
</template>

<script>
import { mapState } from 'vuex'

export default {
  computed: {
    ...mapState({
      currentCount: 'count',
      currentUser: 'user'
    })
  }
}
</script>

场景三:组合局部状态 #

vue
<template>
  <div>
    <p>Total: {{ total }}</p>
  </div>
</template>

<script>
import { mapState } from 'vuex'

export default {
  data() {
    return {
      localCount: 10
    }
  },
  
  computed: {
    ...mapState({
      // 结合 store 状态和组件局部状态
      total(state) {
        return state.count + this.localCount
      }
    })
  }
}
</script>

场景四:混合使用 #

vue
<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Double: {{ doubleCount }}</p>
    <p>Full Name: {{ fullName }}</p>
  </div>
</template>

<script>
import { mapState, mapGetters } from 'vuex'

export default {
  data() {
    return {
      lastName: 'Doe'
    }
  },
  
  computed: {
    // mapState 映射
    ...mapState(['count']),
    
    // mapGetters 映射
    ...mapGetters(['doubleCount']),
    
    // 自定义计算属性
    fullName() {
      return `${this.$store.state.user.firstName} ${this.lastName}`
    }
  }
}
</script>

高级用法 #

动态映射 #

javascript
import { mapState } from 'vuex'

export default {
  computed: {
    // 动态生成映射
    ...mapState(
      this.isAdmin 
        ? ['user', 'permissions', 'settings']
        : ['user']
    )
  },
  
  data() {
    return {
      isAdmin: false
    }
  }
}

模块映射 #

javascript
import { mapState } from 'vuex'

export default {
  computed: {
    // 映射模块状态
    ...mapState('user', {
      userName: state => state.name,
      userAge: state => state.age
    }),
    
    // 使用 createNamespacedHelpers
    ...mapState('cart', ['items', 'total'])
  }
}

使用 createNamespacedHelpers #

javascript
import { createNamespacedHelpers } from 'vuex'

const { mapState } = createNamespacedHelpers('user')

export default {
  computed: {
    // 直接映射,无需指定模块名
    ...mapState(['name', 'age', 'email'])
  }
}

组合式 API 中使用 #

封装 mapState #

javascript
// composables/useMapState.js
import { computed } from 'vue'
import { useStore } from 'vuex'

export function useMapState(keys) {
  const store = useStore()
  
  const stateMap = {}
  
  keys.forEach(key => {
    stateMap[key] = computed(() => store.state[key])
  })
  
  return stateMap
}

使用封装 #

vue
<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>User: {{ user.name }}</p>
  </div>
</template>

<script>
import { useMapState } from '@/composables/useMapState'

export default {
  setup() {
    const { count, user } = useMapState(['count', 'user'])
    
    return {
      count,
      user
    }
  }
}
</script>

更完整的实现 #

javascript
// composables/useMapState.js
import { computed } from 'vue'
import { useStore } from 'vuex'

export function useMapState(mapper) {
  const store = useStore()
  const stateMap = {}
  
  if (Array.isArray(mapper)) {
    // 数组语法
    mapper.forEach(key => {
      stateMap[key] = computed(() => store.state[key])
    })
  } else {
    // 对象语法
    Object.keys(mapper).forEach(key => {
      const value = mapper[key]
      
      if (typeof value === 'function') {
        stateMap[key] = computed(() => value(store.state))
      } else if (typeof value === 'string') {
        stateMap[key] = computed(() => store.state[value])
      }
    })
  }
  
  return stateMap
}

最佳实践 #

1. 合理命名 #

javascript
// 推荐:清晰的命名
computed: {
  ...mapState({
    currentUser: 'user',
    currentCount: 'count'
  })
}

// 不推荐:混淆的命名
computed: {
  ...mapState({
    data: 'user'  // 不清楚是什么数据
  })
}

2. 避免过度使用 #

javascript
// 如果只需要一个状态,直接使用计算属性更清晰
computed: {
  count() {
    return this.$store.state.count
  }
}

// 多个状态时使用 mapState
computed: {
  ...mapState(['count', 'user', 'todos', 'settings'])
}

3. 与其他辅助函数配合 #

javascript
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'

export default {
  computed: {
    ...mapState(['count', 'user']),
    ...mapGetters(['doubleCount', 'isLoggedIn'])
  },
  
  methods: {
    ...mapMutations(['INCREMENT', 'SET_USER']),
    ...mapActions(['fetchUser', 'login'])
  }
}

常见问题 #

1. this 未定义 #

javascript
// 错误:箭头函数中没有 this
...mapState({
  total: state => state.count + this.localCount  // this 是 undefined
})

// 正确:使用普通函数
...mapState({
  total(state) {
    return state.count + this.localCount
  }
})

2. 命名冲突 #

javascript
// 问题:与组件 data 或 methods 冲突
export default {
  data() {
    return {
      count: 0  // 与 mapState 冲突
    }
  },
  computed: {
    ...mapState(['count'])
  }
}

// 解决:重命名
computed: {
  ...mapState({
    storeCount: 'count'
  })
}

3. 模块命名空间 #

javascript
// 问题:直接映射模块状态失败
...mapState(['user/name'])  // 不工作

// 解决:使用命名空间参数
...mapState('user', ['name'])

// 或使用 createNamespacedHelpers
import { createNamespacedHelpers } from 'vuex'
const { mapState } = createNamespacedHelpers('user')

总结 #

mapState 辅助函数的使用要点:

语法 使用场景 示例
数组语法 名称相同 ...mapState(['count'])
对象语法 重命名/自定义逻辑 ...mapState({ num: 'count' })
函数语法 结合局部状态 ...mapState({ total: s => s.count + this.local })

继续学习 状态结构设计,了解如何合理组织状态结构。

最后更新:2026-03-28