Vue.js可复用搜索筛选器与网格布局实现问题求助
哈哈,这个需求我太懂了!之前做后台管理系统的时候也碰到过,不想写一堆重复的筛选和布局代码,咱们一步步来搞定这俩问题,保证代码干净又高效~
核心思路:封装独立的可复用组件
每个section本质上都是“筛选输入框 + 三列网格展示”的组合,而且各自的数据源独立、筛选状态独立——那直接把这个组合封装成一个独立组件就完事儿了!每个组件实例对应一个section,天然互相隔离,完全不用担心影响其他模块。
1. 组件化实现:让每个section自给自足
先写一个叫FilterableGridSection的子组件,把所有通用逻辑都塞进去,父组件只需要传对应的标题和数据源就行。
子组件代码(FilterableGridSection.vue)
<template> <section class="grid-section"> <!-- 每个section自己的标题 --> <h2>{{ sectionTitle }}</h2> <!-- 独立的筛选输入框(如果要全局统一筛选,就把这个移到父组件,用props传关键词) --> <input v-model="localFilter" placeholder="搜索{{ sectionTitle }}内容..." class="filter-input" /> <!-- 三列网格布局 --> <div class="three-column-grid"> <div v-for="(column, idx) in splitFilteredContents" :key="idx" class="grid-column"> <div v-for="item in column" :key="item.id" class="grid-card"> <h3>{{ item.title }}</h3> <!-- 这里放你的内容,比如描述、图片之类的 --> <p>{{ item.desc }}</p> </div> </div> </div> </section> </template> <script> export default { name: 'FilterableGridSection', props: { // 每个section的标题 sectionTitle: { type: String, required: true }, // 每个section对应的数据源 contents: { type: Array, required: true, // 简单校验:每个item必须有title字段用于筛选 validator: val => val.every(item => item.title) }, // 可选:如果需要全局统一筛选,就用这个props替代localFilter // globalFilter: { // type: String, // default: '' // } }, data() { return { // 每个section自己的筛选关键词,互不干扰 localFilter: '' } }, computed: { // 第一步:通用筛选逻辑,用computed缓存结果,只有依赖变化才重新计算 filteredContents() { const keyword = this.localFilter.toLowerCase(); // 如果用全局筛选,就换成this.globalFilter return this.contents.filter(item => item.title.toLowerCase().includes(keyword) ); }, // 第二步:把筛选后的内容分成三列,同样用computed缓存 splitFilteredContents() { const columns = [[], [], []]; this.filteredContents.forEach((item, idx) => { // 按索引取模分配到三列 columns[idx % 3].push(item); }); return columns; } } } </script> <style scoped> .grid-section { margin: 2rem 0; } .filter-input { margin-bottom: 1.5rem; padding: 0.6rem; border-radius: 4px; border: 1px solid #ddd; width: 100%; max-width: 320px; } .three-column-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.2rem; } .grid-card { padding: 1rem; border: 1px solid #eee; border-radius: 6px; background: #fff; box-shadow: 0 2px 4px rgba(0,0,0,0.05); } </style>
父组件调用方式
在父组件里,你只需要循环或者逐个渲染这个子组件,给每个组件传不同的sectionTitle和contents就行:
<template> <div class="app-wrapper"> <!-- 如果是全局统一筛选,就在这里加输入框,绑定globalFilter --> <!-- <input v-model="globalFilter" placeholder="全局搜索所有内容..." /> --> <FilterableGridSection sectionTitle="Section 1" :contents="contents1" <!-- :global-filter="globalFilter" --> /> <FilterableGridSection sectionTitle="Section 2" :contents="contents2" /> <FilterableGridSection sectionTitle="Section 3" :contents="contents3" /> </div> </template> <script> import FilterableGridSection from './FilterableGridSection.vue'; export default { components: { FilterableGridSection }, data() { return { globalFilter: '', // 你的三个数据源 contents1: [{ id: 1, title: '标题1', desc: '内容1' }, ...], contents2: [{ id: 1, title: '标题A', desc: '内容A' }, ...], contents3: [{ id: 1, title: '标题X', desc: '内容X' }, ...] } } } </script>
2. 用computed解决性能和复用问题
你担心的“如何区分各section内容”其实根本不是问题——因为每个组件实例都是独立的,它们的computed属性只会监听自己的contents和localFilter(或globalFilter),完全不会和其他组件的状态混淆。而且computed会自动缓存计算结果,只有当依赖的响应式数据变化时才会重新计算,比每次渲染都执行的method性能好太多。
进阶优化:让组件更灵活
如果以后需要调整筛选字段(比如不是按title,而是按category筛选),或者修改列数,只需要给子组件加几个props就行:
// 在子组件的props里新增 filterField: { type: String, default: 'title' // 默认按title筛选 }, columnCount: { type: Number, default: 3, // 默认三列 validator: val => val > 0 }
然后修改computed里的逻辑:
filteredContents() { const keyword = this.localFilter.toLowerCase(); return this.contents.filter(item => item[this.filterField].toLowerCase().includes(keyword) ); }, splitFilteredContents() { // 按配置的列数生成空数组 const columns = Array.from({ length: this.columnCount }, () => []); this.filteredContents.forEach((item, idx) => { columns[idx % this.columnCount].push(item); }); return columns; }
这样组件的复用性就更强了,想怎么改就怎么改~
内容的提问来源于stack exchange,提问作者Deep
相关产品推荐
相关产品推荐

