Pinia 性能优化 #

概述 #

Pinia 本身已经非常轻量和高效,但在大型应用中,合理优化 Store 可以进一步提升性能。本节将介绍 Pinia 的性能优化技巧。

响应式优化 #

避免不必要的响应式 #

ts
// 不推荐:大型对象完全响应式
export const useDataStore = defineStore('data', {
  state: () => ({
    largeData: {} as Record<string, any>  // 整个对象都是响应式的
  })
})

// 推荐:使用 shallowRef 或 markRaw
import { shallowRef, markRaw } from 'vue'

export const useDataStore = defineStore('data', () => {
  // shallowRef:只有 .value 是响应式的
  const largeData = shallowRef({})
  
  // markRaw:完全跳过响应式
  const staticData = markRaw({ /* 大型静态数据 */ })
  
  return { largeData, staticData }
})

使用 shallowRef 处理大型数据 #

ts
import { defineStore } from 'pinia'
import { shallowRef, triggerRef } from 'vue'

export const useListStore = defineStore('list', () => {
  // 使用 shallowRef 避免深层响应式
  const items = shallowRef<Item[]>([])
  
  async function fetchItems() {
    const response = await fetch('/api/items')
    items.value = await response.json()
    // 手动触发更新
    triggerRef(items)
  }
  
  function updateItem(id: number, updates: Partial<Item>) {
    const index = items.value.findIndex(item => item.id === id)
    if (index !== -1) {
      // 创建新数组触发更新
      items.value = [
        ...items.value.slice(0, index),
        { ...items.value[index], ...updates },
        ...items.value.slice(index + 1)
      ]
    }
  }
  
  return { items, fetchItems, updateItem }
})

组件订阅优化 #

精确订阅 State #

vue
<script setup>
import { storeToRefs } from 'pinia'
import { useUserStore } from '@/stores/user'

const userStore = useUserStore()

// 推荐:只订阅需要的属性
const { name, email } = storeToRefs(userStore)

// 不推荐:订阅整个 store
// const userStore = useUserStore()
// 模板中使用 userStore.user 会订阅整个对象
</script>

<template>
  <!-- 推荐:精确使用 -->
  <p>{{ name }}</p>
  <p>{{ email }}</p>
  
  <!-- 不推荐:访问整个对象 -->
  <!-- <p>{{ userStore.user.name }}</p> -->
</template>

使用 computed 派生数据 #

vue
<script setup>
import { computed } from 'vue'
import { useProductStore } from '@/stores/product'

const productStore = useProductStore()

// 推荐:使用 computed 缓存计算结果
const expensiveValue = computed(() => {
  return productStore.products.reduce((sum, p) => sum + p.price, 0)
})

// 不推荐:在模板中直接计算
// {{ productStore.products.reduce((sum, p) => sum + p.price, 0) }}
</script>

Store 设计优化 #

拆分大型 Store #

ts
// 不推荐:一个 Store 包含所有状态
export const useAppStore = defineStore('app', {
  state: () => ({
    user: null,
    products: [],
    orders: [],
    cart: [],
    settings: {}
  })
})

// 推荐:按功能拆分
export const useUserStore = defineStore('user', { /* ... */ })
export const useProductStore = defineStore('product', { /* ... */ })
export const useOrderStore = defineStore('order', { /* ... */ })
export const useCartStore = defineStore('cart', { /* ... */ })

延迟加载 Store #

ts
// 懒加载 Store
const useAdminStore = () => {
  return import('@/stores/admin').then(m => m.useAdminStore())
}

// 在需要时才加载
async function showAdminPanel() {
  const adminStore = await useAdminStore()
  // ...
}

按需加载数据 #

ts
export const useProductStore = defineStore('product', {
  state: () => ({
    products: [],
    productCache: new Map<number, Product>()
  }),
  
  actions: {
    async getProduct(id: number) {
      // 检查缓存
      if (this.productCache.has(id)) {
        return this.productCache.get(id)!
      }
      
      // 从服务器获取
      const response = await fetch(`/api/products/${id}`)
      const product = await response.json()
      
      // 缓存结果
      this.productCache.set(id, product)
      
      return product
    }
  }
})

批量更新优化 #

使用 $patch 批量更新 #

ts
// 不推荐:多次单独更新
userStore.name = 'John'
userStore.email = 'john@example.com'
userStore.age = 25
userStore.updatedAt = new Date()

// 推荐:使用 $patch 批量更新
userStore.$patch({
  name: 'John',
  email: 'john@example.com',
  age: 25,
  updatedAt: new Date()
})

// 或者使用函数形式
userStore.$patch((state) => {
  state.name = 'John'
  state.email = 'john@example.com'
  state.age = 25
  state.updatedAt = new Date()
})

防抖和节流 #

ts
import { defineStore } from 'pinia'
import { useDebounceFn, useThrottleFn } from '@vueuse/core'

export const useSearchStore = defineStore('search', {
  state: () => ({
    query: '',
    results: []
  }),
  
  actions: {
    // 防抖搜索
    searchDebounced: useDebounceFn(async function(this: any, query: string) {
      this.query = query
      const response = await fetch(`/api/search?q=${query}`)
      this.results = await response.json()
    }, 300),
    
    // 节流更新
    updateResults: useThrottleFn(function(this: any, results: any[]) {
      this.results = results
    }, 100)
  }
})

虚拟列表优化 #

分页加载 #

ts
export const useProductStore = defineStore('product', {
  state: () => ({
    products: [],
    page: 1,
    pageSize: 20,
    hasMore: true,
    loading: false
  }),
  
  actions: {
    async loadMore() {
      if (this.loading || !this.hasMore) return
      
      this.loading = true
      try {
        const response = await fetch(
          `/api/products?page=${this.page}&size=${this.pageSize}`
        )
        const data = await response.json()
        
        this.products.push(...data.items)
        this.page++
        this.hasMore = data.hasMore
      } finally {
        this.loading = false
      }
    }
  }
})

虚拟滚动 #

vue
<template>
  <VirtualList
    :items="productStore.products"
    :item-size="50"
  >
    <template #default="{ item }">
      <ProductItem :product="item" />
    </template>
  </VirtualList>
</template>

缓存策略 #

数据缓存 #

ts
export const useProductStore = defineStore('product', () => {
  const products = ref<Product[]>([])
  const lastFetchTime = ref(0)
  const cacheTime = 5 * 60 * 1000  // 5 分钟缓存
  
  async function fetchProducts(forceRefresh = false) {
    const now = Date.now()
    
    // 检查缓存是否有效
    if (!forceRefresh && products.value.length > 0 && now - lastFetchTime.value < cacheTime) {
      return products.value
    }
    
    const response = await fetch('/api/products')
    products.value = await response.json()
    lastFetchTime.value = now
    
    return products.value
  }
  
  return { products, fetchProducts }
})

使用 Map 缓存 #

ts
export const useProductStore = defineStore('product', () => {
  const productCache = ref(new Map<number, { data: Product; timestamp: number }>())
  const cacheTime = 10 * 60 * 1000  // 10 分钟
  
  async function getProduct(id: number) {
    const cached = productCache.value.get(id)
    const now = Date.now()
    
    // 检查缓存
    if (cached && now - cached.timestamp < cacheTime) {
      return cached.data
    }
    
    // 获取新数据
    const response = await fetch(`/api/products/${id}`)
    const product = await response.json()
    
    // 更新缓存
    productCache.value.set(id, {
      data: product,
      timestamp: now
    })
    
    return product
  }
  
  return { getProduct }
})

监控和调试 #

性能监控 #

ts
// plugins/performance.ts
import type { PiniaPluginContext } from 'pinia'

export function performancePlugin({ store }: PiniaPluginContext) {
  store.$onAction(({ name, after, onError }) => {
    const startTime = performance.now()
    
    after(() => {
      const duration = performance.now() - startTime
      if (duration > 100) {
        console.warn(`Slow action: ${store.$id}.${name} took ${duration.toFixed(2)}ms`)
      }
    })
    
    onError((error) => {
      console.error(`Action failed: ${store.$id}.${name}`, error)
    })
  })
}

状态变化监控 #

ts
// 监控状态变化频率
store.$subscribe((mutation, state) => {
  console.log(`State changed: ${mutation.type}`)
  console.log('Changes:', mutation)
})

最佳实践总结 #

1. 精确订阅 #

ts
// 只订阅需要的状态
const { name, email } = storeToRefs(userStore)

2. 批量更新 #

ts
// 使用 $patch 批量更新
store.$patch({ /* ... */ })

3. 合理缓存 #

ts
// 缓存不常变化的数据
const cached = cache.get(key)
if (cached) return cached

4. 延迟加载 #

ts
// 按需加载 Store 和数据
const store = await import('@/stores/admin')

5. 避免深层响应式 #

ts
// 使用 shallowRef 处理大型数据
const largeData = shallowRef({})

下一步 #

现在你已经掌握了性能优化技巧,接下来让我们学习常见问题解答。

最后更新:2026-03-28