章節連結
Vue 3 純前端專案的資料篩選、排序是極常見的需求。由於原始資料的巢狀結構需非同步載入,且順序也是動態的,可利用 Pinia 的 getters 暫存並搭配創造唯一值 id,讓非同步載入的資料不會喪失追蹤特性。
內容
因為 getters 會像 couputed 屬性一樣被快取。 當依賴的 state 不變,不論你呼叫幾次 getter,它都只會計算一次,大幅提升效能。state 是 reactive 的,深層的監聽也沒問題。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
// stores/products.ts import { defineStore } from 'pinia' interface ProductDetail { rating: number; sales: number; } interface Product { id: number; name: string; price: number; details: ProductDetail | null; } type SortKey = 'name' | 'price' | 'rating'; // 定義 Store export const useProductStore = defineStore('products', { state: () => ({ // 原始資料,每筆都有唯一的 id items: [ { id: 101, name: 'B Product', price: 200, details: null }, { id: 102, name: 'C Product', price: 100, details: null }, { id: 103, name: 'A Product', price: 300, details: null }, ] as Product[], }), getters: { sortedItems(state) { return (sortBy: SortKey = 'name', direction: 'asc' | 'desc' = 'asc') => { // 關鍵:建立一個複本再排序,絕不直接修改 state! const listToSort = [...state.items]; listToSort.sort((a, b) => { let valA: string | number; let valB: string | number; // 處理巢狀非同步資料 if (sortBy === 'rating') { valA = a.details?.rating ?? -1; valB = b.details?.rating ?? -1; } else { valA = a[sortBy]; valB = b[sortBy]; } if (valA < valB) return direction === 'asc' ? -1 : 1; if (valA > valB) return direction === 'asc' ? 1 : -1; return 0; }); return listToSort; }; }, }, actions: { async fetchProductDetails(productId: number) { console.log(`正在為 ID: ${productId} 獲取資料...`); // 模擬 API 呼叫 const fetchedDetails: ProductDetail = await new Promise(resolve => setTimeout(() => resolve({ rating: Math.random() * 5, sales: 100 }), 1000) ); // 用 id 找到原始 state 中的對象 const product = this.items.find(p => p.id === productId); if (product) { // 直接修改 state,相關的 getters 會自動重新計算 product.details = fetchedDetails; } }, }, }) |
先前開發時偷懶使用 array 的 index 來當做 key。由於這個值在排序的情況下會是動態的,所以不可行
按讚加入粉絲團