Vue.js 3如何向插槽内所有子组件传递v-for循环索引变量
Vue 3动态表格v-for索引传递给插槽子组件实现方案
原有方案失效原因
- 普通默认插槽的内容作用域属于调用MyTable的父组件,和MyTable内部v-for循环的上下文完全隔离,循环内的
row、index变量默认无法被插槽内容访问 provide/inject是组件实例级别的API,同一个MyTable实例下循环执行provide会反复覆盖同key的值,最终所有子组件inject拿到的都是循环结束时最后一次赋值的索引- 模板内写
(currentRow = index)属于渲染副作用,不符合Vue响应式渲染规则,赋值结果同样会被最后一次循环覆盖
正确实现(无需渲染函数,保持预期的组件调用方式不变)
核心思路:把MyTableColumn作为纯配置声明组件,不渲染实际DOM;MyTable初始化时先收集所有列配置,内部自行循环行和列渲染表格,循环上下文完全在组件内部可控,需要自定义内容时通过作用域插槽向外传值。
1. 编写MyTableColumn.vue
该组件仅做列配置声明,不需要渲染任何DOM:
<script setup> defineProps({ cellTitle: { type: String, required: true }, cellValue: { type: String, required: true } }) </script> <template> <!-- 无实际渲染内容 --> </template>
2. 编写MyTable.vue
组件内部先收集列配置,再循环渲染表头和表体,行循环的索引、行数据不需要向外透传给列组件,直接在内部渲染时使用即可:
<script setup> import { ref, onMounted } from 'vue' const props = defineProps({ source: { type: Array, required: true, default: () => [] } }) // 隐藏的容器用于挂载插槽、收集列配置 const columnCollectorRef = ref(null) const columns = ref([]) onMounted(() => { // 提取默认插槽内所有MyTableColumn的配置 const slotVnodes = columnCollectorRef.value.$slots.default() columns.value = slotVnodes .filter(vnode => vnode.type?.__name === 'MyTableColumn') .map(vnode => ({ title: vnode.props.cellTitle, field: vnode.props.cellValue })) }) </script> <template> <!-- 配置收集容器,v-if="false"不会渲染到页面 --> <div v-if="false" ref="columnCollectorRef"> <slot></slot> </div> <table border="1" cellpadding="6" cellspacing="0"> <!-- 表头 --> <thead> <tr> <th v-for="col in columns" :key="col.field">{{ col.title }}</th> </tr> </thead> <!-- 表体行循环 --> <tbody> <tr v-for="(row, rowIndex) in props.source" :key="rowIndex"> <td v-for="col in columns" :key="col.field"> <!-- 直接取当前行对应字段的值,可自由使用rowIndex --> {{ row[col.field] }} </td> </tr> </tbody> </table> </template>
3. 扩展支持自定义单元格(可选)
如果需要支持自定义单元格内容、在自定义内容里拿到行索引/行数据,可以给列组件增加作用域插槽支持,在MyTable渲染单元格时把当前行上下文作为插槽参数传出即可,调用示例如下:
<template> <h1>my table</h1> <MyTable :source="[ { fname: 'Jhon', lname: 'Smith' }, { fname: 'Arthur', lname: 'Cromer' }, ]" > <MyTableColumn cellTitle="序号" cellValue="index"> <template #cell="{ rowIndex }"> {{ rowIndex + 1 }} </template> </MyTableColumn> <MyTableColumn cellTitle="First Name" cellValue="fname"></MyTableColumn> <MyTableColumn cellTitle="Last Name" cellValue="lname"></MyTableColumn> </MyTable> </template>
注意事项
之前尝试作用域插槽未生效,核心问题是作用域插槽的渲染位置必须放在v-for循环内部,才能拿到每次循环的上下文;如果把插槽放在循环外层,自然无法获取每次迭代的索引值。
内容的提问来源于stack exchange,提问作者ahmed
相关产品推荐
相关产品推荐

