前言
在前端开发中,我们经常会遇到这样的需求场景:需要展示大量数据的树形结构表格,并且要在表格单元格内插入输入框、选择器、开关等可交互元素。传统的表格组件往往在面对这种复杂场景时力不从心:性能问题、功能不足、配置复杂等问题接踵而至。
这时候,vxe-table就成为了一个理想的解决方案。本文将详细介绍 vxe-table 的核心功能和使用方法,帮助大家快速上手这个强大的表格组件。
一、vxe-table 简介
1.1 什么是 vxe-table
vxe-table 是一个基于 Vue 的 PC 端表格组件,支持增删改查、虚拟滚动、懒加载、快捷菜单、数据校验、树形结构、打印导出、表单渲染、自定义模板等强大功能。
官网地址:https://vxetable.cn
GitHub:https://github.com/x-extends/vxe-table
1.2 为什么选择 vxe-table
相比其他表格组件,vxe-table 的优势在于:
- 性能优秀:支持虚拟滚动,可流畅展示海量数据(10万+行)
- 功能全面:内置表单渲染、单元格编辑、树形结构等常用功能
- 高度可定制:支持自定义渲染器、插槽、主题配置
- 开箱即用:提供丰富的 API 和完善的文档
- 生态完善:配套 vxe-table-plugin-* 系列插件
1.3 适用场景
- 大数据量表格展示(虚拟滚动)
- 树形表格/树形网格
- 可编辑表格(单元格编辑)
- 复杂表单渲染
- 数据导入导出
- 数据校验
二、快速开始
2.1 安装
使用 npm 或 yarn 安装:
# npm 安装 npm install vxe-table xe-utils # yarn 安装 yarn add vxe-table xe-utils2.2 全局引入
在main.js中引入:
import { createApp } from 'vue' import App from './App.vue' // 引入 vxe-table import VXETable from 'vxe-table' import 'vxe-table/lib/style.css' const app = createApp(App) // 全局安装 app.use(VXETable) app.mount('#app')2.3 按需引入(推荐)
import { createApp } from 'vue' import App from './App.vue' // 按需引入 import { VXETable, // 核心 Icon, Column, Table, // 可选组件 Edit, Menu, Export, Keyboard, Validator } from 'vxe-table' import 'vxe-table/lib/style.css' const app = createApp(App) // 安装需要的模块 app.use(Icon) app.use(Column) app.use(Table) app.use(Edit) app.use(Menu) app.use(Export) app.use(Keyboard) app.use(Validator) app.mount('#app')三、基础使用
3.1 基础表格
最简单的表格使用示例:
<template> <div> <vxe-table :data="tableData"> <vxe-column field="id" title="ID" width="80"></vxe-column> <vxe-column field="name" title="姓名" width="150"></vxe-column> <vxe-column field="age" title="年龄" width="100"></vxe-column> <vxe-column field="address" title="地址"></vxe-column> </vxe-table> </div> </template> <script setup> import { ref } from 'vue' const tableData = ref([ { id: 1, name: '张三', age: 28, address: '北京市朝阳区' }, { id: 2, name: '李四', age: 32, address: '上海市浦东新区' }, { id: 3, name: '王五', age: 25, address: '广州市天河区' } ]) </script>3.2 树形表格
vxe-table 对树形结构有原生支持:
<template> <div> <vxe-table :data="treeData" :tree-config="{ children: 'children', expandAll: true }" border > <vxe-column field="name" title="部门名称" tree-node></vxe-column> <vxe-column field="leader" title="负责人" width="150"></vxe-column> <vxe-column field="count" title="人数" width="100"></vxe-column> </vxe-table> </div> </template> <script setup> import { ref } from 'vue' const treeData = ref([ { name: '技术部', leader: '张三', count: 15, children: [ { name: '前端组', leader: '李四', count: 8 }, { name: '后端组', leader: '王五', count: 7 } ] }, { name: '产品部', leader: '赵六', count: 10, children: [ { name: '产品设计组', leader: '钱七', count: 5 }, { name: '用户研究组', leader: '孙八', count: 5 } ] } ]) </script>四、单元格编辑与表单元素
4.1 单元格编辑(edit-render)
这是 vxe-table 的核心功能之一,可以在单元格中渲染各种表单元素:
输入框(input)
<template> <vxe-table :data="tableData" :edit-config="{ trigger: 'click', mode: 'cell' }"> <vxe-column field="name" title="姓名" :edit-render="{ name: 'input' }"></vxe-column> <vxe-column field="age" title="年龄" :edit-render="{ name: 'input', props: { type: 'number' } }"></vxe-column> </vxe-table> </template> <script setup> import { ref } from 'vue' const tableData = ref([ { id: 1, name: '张三', age: 28 }, { id: 2, name: '李四', age: 32 } ]) </script>下拉选择器(select)
<template> <vxe-table :data="tableData" :edit-config="{ trigger: 'click', mode: 'cell' }"> <vxe-column field="name" title="姓名"></vxe-column> <vxe-column field="status" title="状态" :edit-render="{ name: 'select', options: statusOptions }" ></vxe-column> <vxe-column field="role" title="角色" :edit-render="{ name: 'select', options: roleOptions, props: { multiple: true } }" ></vxe-column> </vxe-table> </template> <script setup> import { ref } from 'vue' const tableData = ref([ { id: 1, name: '张三', status: '1', role: ['admin'] }, { id: 2, name: '李四', status: '0', role: ['user'] } ]) const statusOptions = [ { label: '启用', value: '1' }, { label: '禁用', value: '0' } ] const roleOptions = [ { label: '管理员', value: 'admin' }, { label: '普通用户', value: 'user' }, { label: '访客', value: 'guest' } ] </script>日期选择器(DatePicker)
<template> <vxe-table :data="tableData" :edit-config="{ trigger: 'click', mode: 'cell' }"> <vxe-column field="name" title="姓名"></vxe-column> <vxe-column field="birthday" title="生日" :edit-render="{ name: '$input', props: { type: 'date' } }" ></vxe-column> </vxe-table> </template> <script setup> import { ref } from 'vue' const tableData = ref([ { id: 1, name: '张三', birthday: '1995-05-12' }, { id: 2, name: '李四', birthday: '1992-08-23' } ]) </script>开关(Switch)
<template> <vxe-table :data="tableData" :edit-config="{ trigger: 'click', mode: 'cell' }"> <vxe-column field="name" title="姓名"></vxe-column> <vxe-column field="enabled" title="启用状态" :edit-render="{ name: '$switch' }" ></vxe-column> </vxe-table> </template> <script setup> import { ref } from 'vue' const tableData = ref([ { id: 1, name: '张三', enabled: true }, { id: 2, name: '李四', enabled: false } ]) </script>4.2 自定义渲染器(插槽)
对于更复杂的场景,可以使用插槽自定义渲染:
<template> <vxe-table :data="tableData" :edit-config="{ trigger: 'click', mode: 'cell' }"> <vxe-column field="name" title="姓名"></vxe-column> <vxe-column field="tags" title="标签"> <template #edit="{ row }"> <el-select v-model="row.tags" multiple placeholder="请选择标签"> <el-option label="Vue" value="vue"></el-option> <el-option label="React" value="react"></el-option> <el-option label="Angular" value="angular"></el-option> </el-select> </template> <template #default="{ row }"> <span v-for="tag in row.tags" :key="tag" class="tag">{{ tag }}</span> </template> </vxe-column> </vxe-table> </template> <script setup> import { ref } from 'vue' const tableData = ref([ { id: 1, name: '张三', tags: ['vue', 'react'] }, { id: 2, name: '李四', tags: ['angular'] } ]) </script> <style scoped> .tag { display: inline-block; padding: 2px 8px; margin-right: 5px; background: #f0f0f0; border-radius: 3px; font-size: 12px; } </style>五、树形表格 + 可编辑完整示例
结合前面的知识点,我们来实现一个完整的树形可编辑表格:
<template> <div class="demo-container"> <vxe-toolbar> <template #buttons> <vxe-button @click="insertEvent">新增</vxe-button> <vxe-button @click="saveEvent">保存</vxe-button> <vxe-button @click="removeSelectEvent">删除选中</vxe-button> </template> </vxe-toolbar> <vxe-table ref="tableRef" border resizable :data="tableData" :tree-config="treeConfig" :edit-config="{ trigger: 'click', mode: 'cell' }" :checkbox-config="{ checkStrictly: true }" > <vxe-column type="checkbox" width="60"></vxe-column> <vxe-column type="seq" width="60" title="序号"></vxe-column> <vxe-column field="name" title="部门名称" tree-node :edit-render="{ name: 'input' }" ></vxe-column> <vxe-column field="leader" title="负责人" :edit-render="{ name: 'input' }" ></vxe-column> <vxe-column field="status" title="状态" :edit-render="{ name: 'select', options: statusOptions }" > <template #default="{ row }"> <span :class="'status-' + row.status"> {{ getStatusLabel(row.status) }} </span> </template> </vxe-column> <vxe-column field="count" title="人数" :edit-render="{ name: 'input', props: { type: 'number', min: 0 } }" ></vxe-column> <vxe-column field="enabled" title="启用" width="100" :edit-render="{ name: '$switch' }" > <template #default="{ row }"> <span>{{ row.enabled ? '是' : '否' }}</span> </template> </vxe-column> <vxe-column title="操作" width="200"> <template #default="{ row }"> <vxe-button type="text" @click="insertChildEvent(row)">添加子项</vxe-button> <vxe-button type="text" status="danger" @click="removeEvent(row)">删除</vxe-button> </template> </vxe-column> </vxe-table> </div> </template> <script setup> import { ref, reactive } from 'vue' import { VXETable } from 'vxe-table' const tableRef = ref() const tableData = ref([ { id: 1, name: '技术部', leader: '张三', status: '1', count: 15, enabled: true, children: [ { id: 11, name: '前端组', leader: '李四', status: '1', count: 8, enabled: true }, { id: 12, name: '后端组', leader: '王五', status: '1', count: 7, enabled: true } ] }, { id: 2, name: '产品部', leader: '赵六', status: '1', count: 10, enabled: true, children: [ { id: 21, name: '产品设计组', leader: '钱七', status: '0', count: 5, enabled: false }, { id: 22, name: '用户研究组', leader: '孙八', status: '1', count: 5, enabled: true } ] } ]) const treeConfig = reactive({ children: 'children', expandAll: true, reserve: true }) const statusOptions = [ { label: '启用', value: '1' }, { label: '禁用', value: '0' } ] const getStatusLabel = (status) => { const option = statusOptions.find(item => item.value === status) return option ? option.label : '' } let idCounter = 100 // 新增根节点 const insertEvent = () => { const newRow = { id: ++idCounter, name: '新部门', leader: '', status: '1', count: 0, enabled: true, children: [] } tableData.value.push(newRow) VXETable.modal.message({ content: '新增成功', status: 'success' }) } // 新增子节点 const insertChildEvent = (row) => { const newRow = { id: ++idCounter, name: '新子部门', leader: '', status: '1', count: 0, enabled: true } if (!row.children) { row.children = [] } row.children.push(newRow) VXETable.modal.message({ content: '添加子项成功', status: 'success' }) } // 删除行 const removeEvent = (row) => { VXETable.modal.confirm('确定要删除该行数据吗?').then(type => { if (type === 'confirm') { const removeRow = (list) => { for (let i = 0; i < list.length; i++) { if (list[i].id === row.id) { list.splice(i, 1) return true } if (list[i].children && removeRow(list[i].children)) { return true } } return false } removeRow(tableData.value) VXETable.modal.message({ content: '删除成功', status: 'success' }) } }) } // 删除选中 const removeSelectEvent = () => { const selectRecords = tableRef.value.getCheckboxRecords() if (selectRecords.length === 0) { VXETable.modal.message({ content: '请至少选择一条数据', status: 'warning' }) return } VXETable.modal.confirm(`确定要删除选中的 ${selectRecords.length} 条数据吗?`).then(type => { if (type === 'confirm') { selectRecords.forEach(record => removeEvent(record)) } }) } // 保存数据 const saveEvent = () => { const data = tableRef.value.getTableData().fullData console.log('保存的数据:', data) VXETable.modal.message({ content: '保存成功', status: 'success' }) // 这里可以调用 API 保存数据 } </script> <style scoped> .demo-container { padding: 20px; } .status-1 { color: #67c23a; } .status-0 { color: #f56c6c; } </style>六、高级功能
6.1 虚拟滚动(大数据量)
当数据量达到上万条时,虚拟滚动可以保证流畅性:
<template> <vxe-table border height="600" :data="tableData" :scroll-y="{ enabled: true, gt: 100 }" > <vxe-column field="id" title="ID" width="80"></vxe-column> <vxe-column field="name" title="姓名"></vxe-column> <vxe-column field="age" title="年龄"></vxe-column> </vxe-table> </template> <script setup> import { ref } from 'vue' // 生成10万条数据 const tableData = ref( Array.from({ length: 100000 }, (_, i) => ({ id: i + 1, name: `用户${i + 1}`, age: Math.floor(Math.random() * 60) + 18 })) ) </script>6.2 数据校验
vxe-table 支持强大的数据校验功能:
<template> <vxe-table :data="tableData" :edit-config="{ trigger: 'click', mode: 'cell' }" :edit-rules="editRules" > <vxe-column field="name" title="姓名" :edit-render="{ name: 'input' }" ></vxe-column> <vxe-column field="email" title="邮箱" :edit-render="{ name: 'input' }" ></vxe-column> <vxe-column field="age" title="年龄" :edit-render="{ name: 'input', props: { type: 'number' } }" ></vxe-column> </vxe-table> </template> <script setup> import { ref } from 'vue' const tableData = ref([ { id: 1, name: '张三', email: 'zhangsan@example.com', age: 28 } ]) const editRules = { name: [ { required: true, message: '姓名不能为空' }, { min: 2, max: 10, message: '姓名长度在 2 到 10 个字符' } ], email: [ { required: true, message: '邮箱不能为空' }, { pattern: /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/, message: '邮箱格式不正确' } ], age: [ { required: true, message: '年龄不能为空' }, { type: 'number', min: 18, max: 65, message: '年龄必须在 18 到 65 之间' } ] } </script>6.3 导出 Excel
<template> <div> <vxe-button @click="exportExcel">导出Excel</vxe-button> <vxe-table ref="tableRef" :data="tableData"> <vxe-column field="name" title="姓名"></vxe-column> <vxe-column field="age" title="年龄"></vxe-column> </vxe-table> </div> </template> <script setup> import { ref } from 'vue' const tableRef = ref() const tableData = ref([ { name: '张三', age: 28 }, { name: '李四', age: 32 } ]) const exportExcel = () => { tableRef.value.exportData({ filename: '用户数据', type: 'xlsx' }) } </script>七、性能优化建议
- 使用虚拟滚动:数据量大于 100 条时开启虚拟滚动
- 按需加载:只引入需要的组件和功能模块
- 懒加载树节点:树形数据较大时使用懒加载
- 避免过度渲染:合理使用
show-overflow属性 - 分页加载:超大数据集使用分页而非一次性加载
<template> <vxe-table border height="600" show-overflow :data="tableData" :scroll-y="{ enabled: true }" :tree-config="{ lazy: true, loadMethod: loadChildrenMethod }" > <vxe-column field="name" title="名称" tree-node></vxe-column> </vxe-table> </template> <script setup> const loadChildrenMethod = ({ row }) => { // 异步加载子节点 return new Promise(resolve => { setTimeout(() => { resolve([ { name: `${row.name}-子节点1` }, { name: `${row.name}-子节点2` } ]) }, 500) }) } </script>八、常见问题与解决方案
8.1 编辑状态不生效
问题:点击单元格没有进入编辑状态
解决:检查是否配置了:edit-config
<vxe-table :edit-config="{ trigger: 'click', mode: 'cell' }">8.2 树形数据不显示
问题:树形结构没有展示出来
解决:确保配置了tree-config和tree-node
<vxe-table :tree-config="{ children: 'children' }"> <vxe-column field="name" title="名称" tree-node></vxe-column> </vxe-table>8.3 下拉选择器选项不显示
问题:select 渲染器的选项列表为空
解决:确保 options 格式正确,必须包含 label 和 value
const options = [ { label: '选项1', value: '1' }, // 正确 { label: '选项2', value: '2' } ]九、总结
vxe-table 是一个功能强大、性能优秀的 Vue 表格组件,特别适合处理复杂的业务场景:
- 支持树形结构和大数据量虚拟滚动
- 内置丰富的表单渲染器(输入框、选择器、开关等)
- 灵活的单元格编辑和数据校验
- 完善的 API 和良好的扩展性
对于需要在表格中插入输入框、选择器等交互元素的场景,vxe-table 提供了开箱即用的解决方案,大大提升了开发效率。
建议大家在实际项目中根据具体需求,合理使用 vxe-table 的各项功能,同时注意性能优化,以获得最佳的用户体验。
参考资料
- vxe-table 官方文档
- vxe-table GitHub
- vxe-table 示例
💡提示:本文基于 vxe-table 4.x 版本编写,如有版本差异请参考官方文档。