Vue 列表过滤:语法与注意事项详解
Vue 列表过滤:语法与注意事项详解
在 Vue 中,列表过滤是常见的数据处理需求,正确实现能显著提升应用性能和用户体验。下面我将全面介绍 Vue 中列表过滤的语法和注意事项。
核心语法与实现方式
1. 使用计算属性(推荐)
computed: {filteredItems() {const searchTerm = this.searchText.toLowerCase();return this.items.filter(item => item.name.toLowerCase().includes(searchTerm) ||item.description.toLowerCase().includes(searchTerm));}
}
模板中使用:
<div v-for="item in filteredItems" :key="item.id">{{ item.name }}
</div>
2. 使用方法(不推荐用于渲染)
methods: {filterItems(items) {return items.filter(item => item.price > this.minPrice && item.price < this.maxPrice);}
}
模板中使用:
<div v-for="item in filterItems(items)" :key="item.id"><!-- 不推荐:每次渲染都会重新计算 -->
</div>
3. 使用 Vue 过滤器(简单场景)
filters: {priceFilter(items, minPrice) {return items.filter(item => item.price >= minPrice);}
}
模板中使用:
<div v-for="item in items | priceFilter(100)" :key="item.id">{{ item.name }} - {{ item.price }}
</div>
关键注意事项
1. 性能优化
- 优先使用计算属性:自动缓存结果,避免重复计算
- 避免在模板中直接过滤:每次渲染都会重新执行
- 复杂过滤使用 Web Workers:防止阻塞主线程
// 复杂计算的 Web Worker 示例
const worker = new Worker('filter-worker.js');
worker.postMessage({ items: this.items, filter: this.filter });
worker.onmessage = (e) => {this.filteredItems = e.data;
};
2. 空状态处理
<template v-if="filteredItems.length"><div v-for="item in filteredItems" :key="item.id"><!-- 内容 --></div>
</template>
<template v-else><div class="empty-state"><p>未找到匹配结果</p><button @click="resetFilters">重置筛选</button></div>
</template>
3. 多条件过滤
computed: {filteredItems() {return this.items.filter(item => {const matchesSearch = this.searchText ? item.name.includes(this.searchText): true;const matchesCategory = this.selectedCategory ? item.category === this.selectedCategory: true;const inPriceRange = item.price >= this.minPrice && item.price <= this.maxPrice;return matchesSearch && matchesCategory && inPriceRange;});}
}
4. 大型列表优化
- 虚拟滚动:只渲染可视区域元素
<VirtualScroller :items="filteredItems":item-height="50":key="filteredItems.length"
><template v-slot:default="{ item }"><div class="item">{{ item.name }}</div></template>
</VirtualScroller>
- 分页处理:结合过滤与分页
computed: {paginatedItems() {const start = (this.currentPage - 1) * this.pageSize;return this.filteredItems.slice(start, start + this.pageSize);},totalPages() {return Math.ceil(this.filteredItems.length / this.pageSize);}
}
5. 输入处理与防抖
data() {return {searchText: '',debouncedSearch: ''};
},
watch: {searchText(newVal) {clearTimeout(this.debounceTimeout);this.debounceTimeout = setTimeout(() => {this.debouncedSearch = newVal;}, 300); // 300ms 防抖}
},
computed: {filteredItems() {// 使用 debouncedSearch 而非 searchText}
}
6. 过滤与排序结合
computed: {processedItems() {const filtered = this.items.filter(/* 过滤逻辑 */);return filtered.sort((a, b) => {if (this.sortBy === 'price') {return this.sortAsc ? a.price - b.price : b.price - a.price;}// 其他排序逻辑...});}
}
最佳实践总结
场景 | 推荐方案 | 不推荐方案 |
---|---|---|
小型列表 | 计算属性 | 模板内过滤 |
大型列表 | 虚拟滚动 + 分页 | 一次性渲染 |
复杂计算 | Web Workers | 主线程计算 |
用户输入 | 防抖处理 | 实时过滤 |
多条件 | 组合过滤 | 嵌套条件 |
空状态 | 友好提示 | 空白显示 |
完整示例:电商商品过滤
<template><div class="product-filter"><!-- 搜索框 --><input v-model="searchText" placeholder="搜索商品..."class="search-input"><!-- 价格范围 --><div class="price-range"><label>价格范围:</label><input type="number" v-model.number="minPrice" placeholder="最低"><span>-</span><input type="number" v-model.number="maxPrice" placeholder="最高"></div><!-- 类别筛选 --><div class="category-filter"><label>类别:</label><select v-model="selectedCategory"><option value="">全部</option><option v-for="cat in categories" :key="cat" :value="cat">{{ cat }}</option></select></div><!-- 排序 --><div class="sort-options"><label>排序:</label><select v-model="sortBy"><option value="price">价格</option><option value="name">名称</option><option value="rating">评分</option></select><button @click="sortAsc = !sortAsc">{{ sortAsc ? '升序 ↑' : '降序 ↓' }}</button></div><!-- 结果展示 --><div v-if="filteredItems.length > 0" class="results"><div class="result-count">找到 {{ filteredItems.length }} 个商品<span v-if="paginatedItems.length < filteredItems.length">(显示 {{ paginatedItems.length }} 个)</span></div><!-- 虚拟滚动容器 --><VirtualScroller :items="paginatedItems":item-height="100"class="product-list"><template v-slot:default="{ item }"><ProductCard :product="item" /></template></VirtualScroller><!-- 分页控件 --><Pagination :current-page="currentPage":total-pages="totalPages"@page-change="currentPage = $event"/></div><!-- 空状态 --><div v-else class="empty-state"><img src="@/assets/no-results.svg" alt="无结果"><h3>未找到匹配的商品</h3><p>请尝试修改筛选条件</p><button @click="resetFilters">重置筛选</button></div></div>
</template><script>
import { debounce } from 'lodash-es';export default {data() {return {searchText: '',minPrice: 0,maxPrice: 10000,selectedCategory: '',sortBy: 'price',sortAsc: true,currentPage: 1,pageSize: 20,items: [], // 从API获取的实际商品数据debouncedSearch: ''};},computed: {// 获取唯一类别列表categories() {return [...new Set(this.items.map(item => item.category))];},// 过滤后的商品filteredItems() {const searchTerm = this.debouncedSearch.toLowerCase();return this.items.filter(item => {const matchesSearch = searchTerm ? item.name.toLowerCase().includes(searchTerm) || item.description.toLowerCase().includes(searchTerm): true;const matchesCategory = this.selectedCategory ? item.category === this.selectedCategory: true;const inPriceRange = item.price >= this.minPrice && item.price <= this.maxPrice;return matchesSearch && matchesCategory && inPriceRange;}).sort((a, b) => {// 排序逻辑if (this.sortBy === 'price') {return this.sortAsc ? a.price - b.price : b.price - a.price;}if (this.sortBy === 'name') {return this.sortAsc ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name);}if (this.sortBy === 'rating') {return this.sortAsc ? a.rating - b.rating : b.rating - a.rating;}return 0;});},// 分页后的商品paginatedItems() {const start = (this.currentPage - 1) * this.pageSize;return this.filteredItems.slice(start, start + this.pageSize);},// 总页数totalPages() {return Math.ceil(this.filteredItems.length / this.pageSize);}},watch: {// 搜索防抖searchText: debounce(function(newVal) {this.debouncedSearch = newVal;this.currentPage = 1; // 重置到第一页}, 300),// 其他筛选条件变化时重置页码minPrice() { this.currentPage = 1; },maxPrice() { this.currentPage = 1; },selectedCategory() { this.currentPage = 1; },sortBy() { this.currentPage = 1; },sortAsc() { this.currentPage = 1; }},methods: {resetFilters() {this.searchText = '';this.minPrice = 0;this.maxPrice = 10000;this.selectedCategory = '';this.sortBy = 'price';this.sortAsc = true;}},mounted() {// 从API获取数据this.fetchProducts();}
};
</script>
性能监控与调试
使用 Vue 性能工具监控过滤性能:
// 在过滤计算属性中添加性能标记
filteredItems() {const start = performance.now();// ...过滤逻辑const duration = performance.now() - start;if (duration > 50) {console.warn(`过滤耗时 ${duration.toFixed(2)}ms,建议优化`);}return result;
}
总结:高效过滤的黄金法则
- 计算属性优先:利用 Vue 的响应式系统和缓存机制
- 大型数据分治:分页 + 虚拟滚动解决性能瓶颈
- 用户交互优化:防抖处理 + 即时反馈
- 空状态体验:提供清晰的指引和重置选项
- 组合过滤排序:逻辑分离,便于维护
- 性能监控:主动检测潜在性能问题
正确实现列表过滤不仅能提升应用性能,还能显著改善用户体验。根据数据量和复杂度选择合适的过滤策略,是 Vue 开发中的关键技能之一。