vue 表格组件 vxe-grid 自定义插槽模板的用法详解
vxe-grid 的自定义插槽非常灵活,单元格可以渲染图片、按钮、格式化文本,甚至多个元素的组合。插槽支持两种写法:模板插槽(Vue template 中的 #xxx_default)与 JSX 插槽(在列配置中直接返回 JSX)。两者能力等价,选择取决于项目是否已启用 JSX。本文说明两种写法的用法、插槽命名规则,以及渲染性能的取舍。
vxe-grid 如何用插槽自定义单元格内容?
在列配置中通过 slots: { default: '插槽名' } 指定插槽,模板中定义对应名称的插槽即可接管该列渲染。插槽名建议遵循 {field}_default 命名规则,便于识别与维护。
- 生效前提:
- 列上必须配置 slots.default,否则单元格按默认方式渲染字段值。
- 模板插槽需在 vxe-grid 内定义,插槽名与 slots.default 的值一致。
- JSX 插槽需将 script setup 的 lang 设为 jsx,并在列配置中直接写 default 函数。
- 预期结果:指定列不再显示原始字段值,而是渲染插槽中定义的内容;分组列头中的子列同样可以使用插槽。
插槽参数有哪些?
两种写法接收的参数相同,均为一个对象,常用字段如下:
| 参数 | 类型 | 说明 |
|---|---|---|
| row | Object | 当前行数据 |
| column | Object | 当前列配置 |
| rowIndex | Number | 当前行索引(从 0 开始) |
| columnIndex | Number | 当前列索引 |
| $grid | Object | 表格实例,可调用表格方法 |
模板插槽通过解构使用:#imgUrl_default="{ row }";JSX 插槽通过函数参数使用:default({ row }) { ... }。
方式一:模板插槽
适用场景:项目未启用 JSX,或团队更习惯 Vue 模板语法。
<template>
<div>
<vxe-grid v-bind="gridOptions">
<template #imgUrl_default="{ row }">
<vxe-image :src="row.imgUrl" width="36" height="30"></vxe-image>
</template>
<template #name_default="{ row }">
<vxe-button mode="text" @click="openDetail(row)">点击{{ row.name }}</vxe-button>
</template>
<template #sex_default="{ row }">
<span>{{ formatSex(row) }}</span>
</template>
<template #address_default="{ row }">
<vxe-image src="https://vxeui.com/resource/img/546.gif" width="36" height="30"></vxe-image>
<span>{{ row.address }}</span>
<vxe-image src="https://n.sinaimg.cn/sinacn17/w120h120/20180314/89fc-fyscsmv5911424.gif" width="30" height="30"></vxe-image>
</template>
</vxe-grid>
</div>
</template>
<script setup>
import { reactive } from 'vue'
import { VxeUI } from 'vxe-pc-ui'
const gridOptions = reactive({
border: true,
showOverflow: true,
columns: [
{ type: 'seq', width: 70 },
{ field: 'imgUrl', title: 'imgUrl', slots: { default: 'imgUrl_default' } },
{ field: 'name', title: 'Name', slots: { default: 'name_default' } },
{
title: 'Group1',
children: [
{ field: 'sex', title: 'Sex', slots: { default: 'sex_default' } },
{ field: 'num', title: 'Number' },
{ field: 'age', title: 'Age' }
]
},
{ field: 'address', title: 'Address', width: 200, slots: { default: 'address_default' } }
],
data: [
{ id: 10001, name: 'Test1', role: 'Develop', sex: '0', age: 28, num: 234, imgUrl: 'https://vxeui.com/resource/img/fj577.jpg', address: 'test abc' },
{ id: 10002, name: 'Test2', role: 'Test', sex: '1', age: 22, num: 34, imgUrl: 'https://vxeui.com/resource/img/fj581.jpeg', address: 'Guangzhou' },
{ id: 10003, name: 'Test3', role: 'PM', sex: '0', age: 32, num: 12, imgUrl: 'https://vxeui.com/resource/img/fj581.jpeg', address: 'Shanghai' }
]
})
const formatSex = (row) => {
return row.sex === '1' ? '男' : '女'
}
const openDetail = (row) => {
VxeUI.modal.message({
status: 'success',
content: `点击了${row.name}`
})
}
</script>

方式二:JSX 插槽
适用场景:项目已配置 JSX 支持,或列配置需要动态生成、希望渲染逻辑与列定义写在一起。
<template>
<div>
<vxe-grid v-bind="gridOptions"></vxe-grid>
</div>
</template>
<script setup lang="jsx">
import { reactive } from 'vue'
import { VxeUI } from 'vxe-pc-ui'
const gridOptions = reactive({
border: true,
showOverflow: true,
columns: [
{ type: 'seq', width: 70 },
{
field: 'imgUrl',
title: 'imgUrl',
slots: {
default({ row }) {
return <vxe-image src={row.imgUrl} width="36" height="30" />
}
}
},
{
field: 'name',
title: 'Name',
slots: {
default({ row }) {
return (
<vxe-button mode="text" onClick={() => openDetail(row)}>
点击{row.name}
</vxe-button>
)
}
}
},
{
title: 'Group1',
children: [
{
field: 'sex',
title: 'Sex',
slots: {
default: ({ row }) => {
return <span>{formatSex(row)}</span>
}
}
},
{ field: 'num', title: 'Number' },
{ field: 'age', title: 'Age' }
]
},
{
field: 'address',
title: 'Address',
width: 200,
slots: {
default({ row }) {
return (
<div>
<vxe-image src="https://vxeui.com/resource/img/546.gif" width="36" height="30" />
<span>{row.address}</span>
<vxe-image src="https://n.sinaimg.cn/sinacn17/w120h120/20180314/89fc-fyscsmv5911424.gif" width="36" height="30" />
</div>
)
}
}
}
],
data: [
{ id: 10001, name: 'Test1', role: 'Develop', sex: '0', age: 28, num: 234, imgUrl: 'https://vxeui.com/resource/img/fj577.jpg', address: 'test abc' },
{ id: 10002, name: 'Test2', role: 'Test', sex: '1', age: 22, num: 34, imgUrl: 'https://vxeui.com/resource/img/fj581.jpeg', address: 'Guangzhou' },
{ id: 10003, name: 'Test3', role: 'PM', sex: '0', age: 32, num: 12, imgUrl: 'https://vxeui.com/resource/img/fj581.jpeg', address: 'Shanghai' }
]
})
const formatSex = (row) => {
return row.sex === '1' ? '男' : '女'
}
const openDetail = (row) => {
VxeUI.modal.message({
status: 'success',
content: `点击了${row.name}`
})
}
</script>
渲染效果与模板插槽完全一致。区别仅在于渲染逻辑写在列配置内,而非模板中。
模板插槽与 JSX 插槽如何选择?
| 维度 | 模板插槽 | JSX 插槽 |
|---|---|---|
| 语法 | Vue 模板 | JSX |
| 是否需要构建配置 | 否 | 需要 JSX 支持 |
| 渲染逻辑位置 | 模板中集中定义 | 列配置内就地定义 |
| 动态列场景 | 插槽需提前定义,灵活性一般 | 可随列配置动态生成,灵活性更高 |
| 可读性 | 结构清晰,适合复杂模板 | 逻辑与列定义在一起,适合简单渲染 |
| 能力 | 等价 | 等价 |
两者能力上没有差异,选择主要看项目技术栈与团队习惯。若列是静态的,模板插槽更直观;若列由接口或配置动态生成,JSX 插槽更容易随列一起构造。
渲染性能:三种方式的取舍
各自适用场景
| 方式 | 性能 | 灵活性 | 适用场景 |
|---|---|---|---|
| 提前转换数据 data: [{...}] | 最优 | 低 | 数据结构固定,展示逻辑简单,追求极致性能 |
| formatter 格式化 | 优 | 中 | 纯文本格式化,如性别、状态码转中文 |
| slots 自定义插槽 | 较差 | 最高 | 需要渲染组件、图片、按钮、多元素组合 |
- data 预转换:在请求返回后立即把展示字段算好,表格渲染时直接取用。性能最好,但数据变化时需重新转换。
- formatter:字段值变化时触发,适合简单的值映射。比插槽轻量,但不支持渲染组件。
- slots:每次渲染都会实时调用,灵活性最高但开销最大。数据量大、单元格内容复杂时需注意性能。
建议:能用 formatter 解决的就不要用插槽;必须渲染组件(图片、按钮等)时才使用插槽。若插槽内逻辑较重,可考虑提前算好部分数据放入行对象,减少插槽内的计算。
关键配置参数
| 配置项 | 类型 | 说明 |
|---|---|---|
| 列 slots.default | String / Function | 模板插槽填插槽名字符串;JSX 插槽填渲染函数 |
| 插槽名 | String | 建议遵循 {field}_default 命名,便于识别 |
| slots.default 参数 | Object | 含 row、column、rowIndex、columnIndex、$grid |

浙公网安备 33010602011771号