返回 导航

Vue.js

hangge.com

Vue.js - 使用Vuex进行状态管理详解3(getters全局计算属性)

作者:hangge | 2026-10-01 09:59
    在前文中,我们介绍了 Vuex 中 state 的基本使用方式,包括直接访问和通过 mapState 辅助函数映射。但在实际开发中,我们经常需要对 state 中的数据进行加工处理后再使用,比如过滤列表、计算总价、格式化数据等。 如果在每个组件中都写一遍相同的计算逻辑,不仅代码冗余,维护起来也非常麻烦。为了解决这个问题,Vuex 提供了 getters,可以理解为 store 的计算属性。本文将详细介绍 getters 的各种用法,包括基本使用、参数传递、返回函数以及 mapGetters 辅助函数的使用技巧。

一、基本介绍

1,getters 是什么?

(1)getters 可以理解为 Vuex store 的计算属性。它允许我们从 store 的 state 中派生出一些状态,例如对列表进行过滤、计算总和等操作。

(2)与组件中的计算属性类似,getters 会根据它的依赖自动缓存结果,只有当依赖的 state 发生变化时才会重新计算。
提示:从 Vue 3.0 开始,getter 的结果不再像计算属性一样会被缓存起来(这是一个已知问题)。如果你需要缓存功能,建议在组件中再次使用计算属性包装。

2,为什么需要 getters?

(1)假设我们有多个组件都需要用到同一个计算逻辑,比如计算购物车总价、过滤已完成的待办事项等。如果不使用 getters,通常有两种方案:
  • 方案一:在每个组件的 computed 中复制相同的计算代码
  • 方案二:封装成一个公共函数,在多个组件中导入使用
(2)但这两种方式都不够理想:方案一会导致代码冗余,方案二虽然复用了代码但仍然需要在每个组件中导入。

(3)使用 getters 后,我们可以在 store 中统一定义计算逻辑,然后让所有组件共享使用,既避免了代码冗余,又无需手动导入。

二、getters 基本使用

1,定义 getters

    下面通过一个案例来演示 getters 的基本使用。该案例的功能是:计算购物车中书籍的总价。首先在 src/store/index.js 文件中定义 state 和 getters:
  • 可以看到,在 getters 中,我们定义了一个名为 totalPrice 的方法,该方法的第一个参数会自动获取 state 对象。接着,遍历 state.books,计算购买书籍的总价并返回。
import { createStore } from 'vuex'

const store = createStore({
  state() {
    return {
      // 购物车书籍列表
      books: [
        { name: 'Vue.js', count: 10, price: 10 },
        { name: 'React', count: 5, price: 20 },
        { name: 'Webpack', count: 4, price: 25 }
      ],
      // 书籍折扣(暂未使用,后续案例会用到)
      discount: 0.9
    }
  },
  // 定义 getters
  getters: {
    // 计算购买的书籍总价
    totalPrice(state) {
      let totalPrice = 0
      for (const book of state.books) {
        totalPrice += book.count * book.price
      }
      return totalPrice
    }
  }
})

export default store

2,在组件中使用 getters

(1)定义好 getters 后,我们可以在组件中通过 $store.getters 访问。新建一个组件文件:
<template>
  <div>
    <h4>书籍总价:{{ $store.getters.totalPrice }}</h4>
  </div>
</template>

<script>
export default {}
</script>

(2)保存代码后,在浏览器中运行效果如下,可以看到书籍总价为 300 元:

三、getters 方法的参数

1,参数说明

getters 中定义的方法可以接收多个参数:
  • 第一个参数:state 对象,可以访问 store 中定义的状态
  • 第二个参数:getters 对象,可以访问其他 getter
提示:如果在模块中定义 getter,还会有第三个参数 rootState(全局 state)和第四个参数 rootGetters(所有 getters)。

2,使用 getters 参数

(1)继续完善上面的案例,我们让书籍总价应用折扣。修改 store/index.js 文件,为 totalPrice 方法增加了第二个参数,用于接收 getters 对象,然后通过该对象访问 currentDiscount 方法获取折扣值。
import { createStore } from 'vuex'

const store = createStore({
  state() {
    return {
      books: [
        { name: 'Vue.js', count: 10, price: 10 },
        { name: 'React', count: 5, price: 20 },
        { name: 'Webpack', count: 4, price: 25 }
      ],
      discount: 0.9  // 书籍打9折
    }
  },
  getters: {
    // 获取当前的折扣
    currentDiscount(state) {
      return state.discount
    },
    // 计算书籍总价(应用折扣)
    totalPrice(state, getters) {
      let totalPrice = 0
      for (const book of state.books) {
        totalPrice += book.count * book.price
      }
      // 通过 getters 访问当前折扣
      return totalPrice * getters.currentDiscount
    }
  }
})

export default store

(2)保存代码后,在浏览器中运行,可以看到购买书籍总价打 9 折后为 270 元:

四、getters 返回函数类型

1,返回函数的作用

(1)getters 中定义的方法不仅可以返回值,还支持返回一个函数。如果返回的是函数,那么在使用时相当于调用这个函数。

(2)这种方式可以让我们向 getter 传递参数,以实现数据的动态查询或过滤。比如:计算某本书的总价、根据 ID 查找某条数据等。
注意:getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果。如果需要缓存,建议在组件中再包装一层计算属性。

2,使用示例

(1)继续修改 store/index.js,在 getters 中添加 totalPriceByName 方法。totalPriceByName 方法返回的是一个函数,该函数接收 bookName 参数,用于筛选指定的书籍并计算总价。
import { createStore } from 'vuex'

const store = createStore({
  state() {
    return {
      books: [
        { name: 'Vue.js', count: 10, price: 10 },
        { name: 'React', count: 5, price: 20 },
        { name: 'Webpack', count: 4, price: 25 },
      ],
      discount: 0.9, // 书籍打9折
    }
  },
  getters: {
    // 获取当前的折扣
    currentDiscount(state) {
      return state.discount
    },
    // 计算书籍总价(应用折扣)
    totalPrice(state, getters) {
      let totalPrice = 0
      for (const book of state.books) {
        totalPrice += book.count * book.price
      }
      // 通过 getters 访问当前折扣
      return totalPrice * getters.currentDiscount
    },
    // 计算指定书籍的总价
    totalPriceByName(state) {
      // 返回一个函数,该函数接收 bookName 参数
      return (bookName) => {
        let totalPrice = 0
        for (const book of state.books) {
          if (bookName === book.name) {
            // 只计算当前这本书的总价
            totalPrice += book.count * book.price
          }
        }
        return totalPrice
      }
    },
  },
})

export default store

(2)在组件中使用时,我们需要调用这个返回的函数并传入参数:
<template>
  <div>
    <h4>书籍总价:{{ $store.getters.totalPrice }}</h4>
    <h4>Vue.js 书籍总价:{{ $store.getters.totalPriceByName('Vue.js') }}</h4>
  </div>
</template>

<script>
export default {}
</script>

(3)保存代码后,在浏览器中运行效果如下:

五、mapGetters 辅助函数

1,在 computed 属性中使用

(1)与 mapState 类似,Vuex 提供了 mapGetters 辅助函数,用于简化获取 getters 的操作,将 store 中的 getter 映射到局部计算属性。

(2)mapGetters 支持两种语法形式:
提示:使用对象形式时,键名是组件中使用的属性名,值是 store 中 getter 的名称。这样可以将 getter 重命名为更简洁或更符合当前组件语境的名称。
<template>
  <div>
    <h4>书籍总价:{{ totalPrice }}</h4>
    <h4>书籍折扣:{{ discount }}</h4>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
  computed: {
    // 方式一:接收数组(使用相同的名称)
    ...mapGetters(['totalPrice']),
    
    // 方式二:接收对象(重命名 getter)
    ...mapGetters({
      discount: 'currentDiscount'  // 将 currentDiscount 映射为 discount
    })
  }
}
</script>

2,在 setup 函数中使用

(1)在 Vue 3 的 Composition API 中,mapGetters 返回的对象中的属性并不是计算属性,因此不能直接在模板中使用。
注意:mapGetters 返回的是普通函数对象,需要使用 computed 包装后才能变成响应式的计算属性。

(2)为了解决这个问题,我们需要封装一个自定义 Hook 函数。新建 src/hooks/useGetters.js 文件,改代码的目的是将 mapGetters 返回的普通函数转换成响应式的计算属性函数。
import { useStore, mapGetters } from 'vuex'
import { computed } from 'vue'

export function useGetters(mapper) {
  const store = useStore()
  
  // 1. 使用 mapGetters 辅助函数
  const stateFns = mapGetters(mapper)
  
  const state = {}
  
  // 2. 遍历将普通函数转换成计算属性
  Object.keys(stateFns).forEach(fnKey => {
    // 绑定 $store 并包装成计算属性
    state[fnKey] = computed(stateFns[fnKey].bind({ $store: store }))
  })
  
  return state
}

(3)接着,在 src/hooks/index.js 中统一导出:
import { useState } from './useState'
import { useGetters } from './useGetters'

export { useState, useGetters }

(4)最后,在组件中使用封装好的 useGetters:
<template>
  <div>
    <h4>书籍总价:{{ $store.getters.totalPrice }}</h4>
    <h4>Vue.js 书籍总价:{{ totalPriceByName('Vue.js') }}</h4>
  </div>
</template>

<script>
import { useGetters } from './hooks'

export default {
  setup() {
    // 调用自定义 Hook
    const storeGetters = useGetters(['totalPriceByName'])
    
    return {
      ...storeGetters
    }
  }
}
</script>

(6)保存代码后,在浏览器中便可查看效果,与直接使用 $store.getters 的效果一致。
评论

全部评论(0)

回到顶部