Vue.js能否使用单个v-for循环向两个不同div渲染元素
需求目标
需要实现的DOM输出结构如下:
<div id="tabs"> <div id="first"> <a>tab 1</a> <a>tab 2</a> </div> <div id="second"> <a>tab 3</a> </div> </div>
现有实现的问题
硬编码双循环方案
目前的实现是写死两个分组容器,分别用v-for渲染对应分组下的tab,代码如下:
// tabs.js export default { data() { return { tabs: { first: [{ name: 'tab1' }, { name: 'tab2' }], second: [{ name: 'tab3' }], } } }, template: ` <div id="tabs"> <div id="first"> <a v-for="tab in tabs.first">{{ tab.name }}</a> </div> <div id="second"> <a v-for="tab in tabs.second">{{ tab.name }}</a> </div> </div> ` }
这个写法性能没问题,但可维护性差:后续新增、删除分组时,既要改data里的数据结构,还要同步改模板里的DOM结构,很容易漏改。
嵌套循环加v-if的构思方案
之前尝试过把所有tab存成一个平级数组,用category字段标记所属分组,外层循环生成分组容器,内层循环全量遍历所有tab,用v-if判断tab是否属于当前分组再渲染,代码如下:
// tabs.js export default { data() { return { tabs: { test: [ { name: 'tab1', category: 'first' }, { name: 'tab2', category: 'first' }, { name: 'tab3', category: 'second' } ] } } }, template: ` <div id="tabs"> <div v-for='category in ["first", "second"]' :id='category' :key='category'> <template v-for="tab in tabs.test"> <a v-if="tab.category === category">{{ tab.name }}</a> </template> </div> </div> ` }
这个写法不需要硬编码多个分组容器,但性能很差:有几个分组就要把全量tab从头到尾遍历几遍,存在大量无效判断,tab数量多的时候性能损耗非常明显。
优化实现方案
直接用Vuev-for遍历对象的语法,遍历你已经按分组装好的tabs对象即可,同时拿到分组ID和对应分组下的tab列表,不需要硬编码模板,也没有冗余遍历:
// tabs.js export default { data() { return { tabs: { first: [{ name: 'tab1' }, { name: 'tab2' }], second: [{ name: 'tab3' }], } } }, template: ` <div id="tabs"> <div :id="categoryId" v-for="(tabList, categoryId) in tabs" :key="categoryId"> <a v-for="tab in tabList" :key="tab.name">{{ tab.name }}</a> </div> </div> ` }
这个方案的优势:
- 性能和硬编码双循环完全一致:总遍历次数等于tab总数量,没有任何冗余迭代
- 维护成本低:后续新增、删除、修改分组,只需要调整data里的tabs对象,不需要动模板代码
- 输出的DOM结构和需求要求的完全一致,没有多余属性或节点
如果你的源数据是带category字段的平级数组,可以先用计算属性做一次分组预处理,只在源数据变化时重新计算分组结果,渲染时直接遍历分组后的对象即可,同样不会有嵌套循环的性能问题:
// tabs.js export default { data() { return { rawTabs: [ { name: 'tab1', category: 'first' }, { name: 'tab2', category: 'first' }, { name: 'tab3', category: 'second' } ] } }, computed: { groupedTabs() { return this.rawTabs.reduce((res, tab) => { const cat = tab.category; if (!res[cat]) res[cat] = []; res[cat].push(tab); return res; }, {}) } }, template: ` <div id="tabs"> <div :id="categoryId" v-for="(tabList, categoryId) in groupedTabs" :key="categoryId"> <a v-for="tab in tabList" :key="tab.name">{{ tab.name }}</a> </div> </div> ` }
内容的提问来源于stack exchange,提问作者exec1990
相关产品推荐
相关产品推荐

