Getter进阶 #

Getter 传参详解 #

返回函数模式 #

Getter 本身不支持直接传参,但可以通过返回函数来实现:

javascript
const store = createStore({
  state: {
    products: [
      { id: 1, name: 'Product 1', category: 'electronics', price: 100 },
      { id: 2, name: 'Product 2', category: 'clothing', price: 50 },
      { id: 3, name: 'Product 3', category: 'electronics', price: 200 }
    ]
  },
  
  getters: {
    // 返回函数,支持参数
    productsByCategory: state => category => {
      return state.products.filter(p => p.category === category)
    },
    
    // 多参数
    productsByPriceRange: state => (min, max) => {
      return state.products.filter(p => p.price >= min && p.price <= max)
    },
    
    // 组合参数
    filterProducts: state => filters => {
      let result = state.products
      
      if (filters.category) {
        result = result.filter(p => p.category === filters.category)
      }
      
      if (filters.minPrice) {
        result = result.filter(p => p.price >= filters.minPrice)
      }
      
      if (filters.maxPrice) {
        result = result.filter(p => p.price <= filters.maxPrice)
      }
      
      return result
    }
  }
})

在组件中使用 #

vue
<template>
  <div>
    <select v-model="selectedCategory">
      <option value="">All</option>
      <option value="electronics">Electronics</option>
      <option value="clothing">Clothing</option>
    </select>
    
    <ul>
      <li v-for="product in filteredProducts" :key="product.id">
        {{ product.name }} - ${{ product.price }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      selectedCategory: ''
    }
  },
  
  computed: {
    filteredProducts() {
      const getter = this.$store.getters.productsByCategory
      return this.selectedCategory 
        ? getter(this.selectedCategory)
        : this.$store.state.products
    }
  }
}
</script>

注意事项 #

javascript
// 注意:返回函数的 getter 不会缓存结果
getters: {
  // 每次调用都会重新计算
  getProductById: state => id => {
    return state.products.find(p => p.id === id)
  }
}

// 如果需要缓存,考虑使用 map 或对象
getters: {
  // 这个会缓存
  productsMap: state => {
    const map = {}
    state.products.forEach(p => {
      map[p.id] = p
    })
    return map
  }
}

// 使用时
computed: {
  product() {
    return this.$store.getters.productsMap[this.productId]
  }
}

Getter 组合 #

链式调用 #

Getter 可以访问其他 Getter:

javascript
getters: {
  // 基础 getter
  cartItems: state => state.cart.items,
  
  // 使用其他 getter
  cartTotal: (state, getters) => {
    return getters.cartItems.reduce((total, item) => {
      return total + item.price * item.quantity
    }, 0)
  },
  
  // 继续链式
  cartWithTax: (state, getters) => {
    return getters.cartTotal * 1.1  // 10% tax
  },
  
  // 更复杂的组合
  cartSummary: (state, getters) => {
    return {
      items: getters.cartItems,
      subtotal: getters.cartTotal,
      tax: getters.cartTotal * 0.1,
      total: getters.cartWithTax,
      itemCount: getters.cartItems.length
    }
  }
}

跨模块 Getter #

javascript
// store/modules/cart.js
getters: {
  cartItems: state => state.items,
  cartTotal: state => state.items.reduce((t, i) => t + i.price * i.quantity, 0)
}

// store/modules/products.js
getters: {
  productById: state => id => state.products.find(p => p.id === id)
}

// store/modules/user.js
getters: {
  userDiscount: state => state.membershipLevel === 'vip' ? 0.9 : 1
}

// 在组件中组合
computed: {
  cartWithDetails() {
    const items = this.$store.getters['cart/cartItems']
    const discount = this.$store.getters['user/userDiscount']
    
    return items.map(item => {
      const product = this.$store.getters['products/productById'](item.productId)
      return {
        ...item,
        product,
        finalPrice: product.price * discount
      }
    })
  }
}

在 Getter 中访问根状态 #

javascript
// 模块中的 getter
const moduleA = {
  namespaced: true,
  
  state: {
    items: []
  },
  
  getters: {
    // 第三和第四个参数:rootState 和 rootGetters
    itemsWithUser: (state, getters, rootState, rootGetters) => {
      return state.items.map(item => ({
        ...item,
        user: rootState.user.byId[item.userId]
      }))
    },
    
    // 使用 rootGetters
    itemsWithDiscount: (state, getters, rootState, rootGetters) => {
      const discount = rootGetters['user/discount']
      return state.items.map(item => ({
        ...item,
        discountedPrice: item.price * discount
      }))
    }
  }
}

性能优化 #

1. 避免重复计算 #

javascript
// 不推荐:每次都计算
getters: {
  expensiveList: state => {
    // 复杂计算
    return state.items.map(item => ({
      ...item,
      computed: heavyCalculation(item)
    }))
  }
}

// 推荐:使用缓存
getters: {
  // 简单 getter 会被缓存
  processedItems: state => {
    return state.items.map(item => ({
      ...item,
      computed: heavyCalculation(item)
    }))
  }
}

2. 使用选择器模式 #

javascript
// 创建选择器
getters: {
  // 基础选择器
  productsById: state => {
    return state.products.reduce((map, product) => {
      map[product.id] = product
      return map
    }, {})
  },
  
  // 使用基础选择器
  productById: (state, getters) => id => {
    return getters.productsById[id]
  }
}

// 组件中使用
computed: {
  product() {
    return this.$store.getters.productsById[this.productId]
  }
}

3. 延迟计算 #

javascript
getters: {
  // 只在需要时计算
  expensiveGetter: state => {
    return {
      get value() {
        // 延迟计算
        return heavyCalculation(state.data)
      }
    }
  }
}

实战模式 #

数据转换模式 #

javascript
state: {
  rawUsers: [
    { id: 1, firstName: 'John', lastName: 'Doe', email: 'john@example.com' },
    { id: 2, firstName: 'Jane', lastName: 'Smith', email: 'jane@example.com' }
  ]
},

getters: {
  // 转换为显示格式
  usersForDisplay: state => {
    return state.rawUsers.map(user => ({
      id: user.id,
      fullName: `${user.firstName} ${user.lastName}`,
      avatar: `https://avatar.example.com/${user.email}`
    }))
  },
  
  // 转换为选择器选项
  userOptions: state => {
    return state.rawUsers.map(user => ({
      label: `${user.firstName} ${user.lastName}`,
      value: user.id
    }))
  },
  
  // 转换为下拉菜单数据
  userSelectData: (state, getters) => ({
    options: getters.userOptions,
    selected: null
  })
}

分页模式 #

javascript
state: {
  posts: [],  // 所有文章
  pageSize: 10,
  currentPage: 1
},

getters: {
  totalPages: state => Math.ceil(state.posts.length / state.pageSize),
  
  paginatedPosts: state => {
    const start = (state.currentPage - 1) * state.pageSize
    const end = start + state.pageSize
    return state.posts.slice(start, end)
  },
  
  pagination: (state, getters) => ({
    currentPage: state.currentPage,
    totalPages: getters.totalPages,
    hasNext: state.currentPage < getters.totalPages,
    hasPrev: state.currentPage > 1,
    items: getters.paginatedPosts
  })
}

搜索过滤模式 #

javascript
state: {
  products: [],
  searchQuery: '',
  filters: {
    category: null,
    minPrice: null,
    maxPrice: null,
    inStock: null
  }
},

getters: {
  filteredProducts: state => {
    let result = state.products
    
    // 搜索
    if (state.searchQuery) {
      const query = state.searchQuery.toLowerCase()
      result = result.filter(p => 
        p.name.toLowerCase().includes(query) ||
        p.description.toLowerCase().includes(query)
      )
    }
    
    // 分类过滤
    if (state.filters.category) {
      result = result.filter(p => p.category === state.filters.category)
    }
    
    // 价格过滤
    if (state.filters.minPrice !== null) {
      result = result.filter(p => p.price >= state.filters.minPrice)
    }
    if (state.filters.maxPrice !== null) {
      result = result.filter(p => p.price <= state.filters.maxPrice)
    }
    
    // 库存过滤
    if (state.filters.inStock !== null) {
      result = result.filter(p => p.inStock === state.filters.inStock)
    }
    
    return result
  },
  
  // 分组
  productsByCategory: (state, getters) => {
    const groups = {}
    getters.filteredProducts.forEach(product => {
      if (!groups[product.category]) {
        groups[product.category] = []
      }
      groups[product.category].push(product)
    })
    return groups
  }
}

TypeScript 支持 #

typescript
// types.ts
interface User {
  id: number
  name: string
  email: string
}

interface RootState {
  users: User[]
  currentUserId: number | null
}

// store.ts
import { GetterTree } from 'vuex'

const getters: GetterTree<RootState, RootState> = {
  currentUser: (state): User | null => {
    return state.users.find(u => u.id === state.currentUserId) || null
  },
  
  userById: (state) => (id: number): User | undefined => {
    return state.users.find(u => u.id === id)
  }
}

总结 #

Getter 进阶要点:

技巧 说明
传参 返回函数实现参数传递
组合 Getter 可以访问其他 Getter
跨模块 使用 rootState 和 rootGetters
性能 利用缓存、避免重复计算

继续学习 Mutation基础,了解如何修改状态。

最后更新:2026-03-28