第一次接触vxe-grid是在去年开发一个后台管理系统时,当时需要展示大量从接口获取的动态数据。传统表格组件在应对动态列、懒加载树形结构这些需求时显得力不从心,而vxe-grid的灵活配置让我眼前一亮。
vxe-grid是基于Vue.js的表格解决方案,相比基础表格组件,它有几个明显优势:
在实际项目中,我发现当遇到这些情况时特别适合使用vxe-grid:
首先需要通过npm安装vxe-table系列组件:
bash复制npm install xe-utils vxe-table@next vxe-table-plugin-antd
在main.js中全局引入基础样式和组件:
javascript复制import VXETable from 'vxe-table'
import 'vxe-table/lib/style.css'
Vue.use(VXETable)
如果是TypeScript项目,还需要添加类型声明:
typescript复制declare module 'vxe-table' {
export interface ColumnConfig {
customProp?: string
}
}
先来看个最简单的例子,渲染静态数据:
html复制<template>
<vxe-grid
:columns="[
{ field: 'id', title: 'ID' },
{ field: 'name', title: '姓名' }
]"
:data="[
{ id: 1, name: '张三' },
{ id: 2, name: '李四' }
]"
/>
</template>
实际项目中,我们更常用的是动态配置方式。比如根据接口返回的字段动态生成columns:
javascript复制async function initTable() {
const res = await fetch('/api/table/columns')
this.columns = res.data.map(item => ({
field: item.fieldName,
title: item.displayName,
width: item.width || 180
}))
}
vxe-grid内置了分页和排序功能,只需要正确配置即可与后端API对接。这是我常用的分页配置方案:
javascript复制<vxe-grid
:pager-config="{
pageSize: 20,
pageSizes: [10, 20, 50, 100],
layouts: ['PrevJump', 'PrevPage', 'Number', 'NextPage', 'NextJump', 'Sizes', 'FullJump', 'Total']
}"
@page-change="handlePageChange"
/>
对应的分页事件处理:
javascript复制methods: {
async handlePageChange({ currentPage, pageSize }) {
const res = await api.getList({
page: currentPage,
size: pageSize
})
this.tableData = res.data.list
this.total = res.data.total
}
}
排序配置也很简单:
javascript复制<vxe-column
field="createTime"
title="创建时间"
sortable
:sort-method="handleSort"
/>
// 排序回调方法
handleSort({ order, property }) {
this.fetchData({
sortField: property,
sortOrder: order === 'asc' ? 1 : -1
})
}
在实际项目中,经常会遇到需要根据用户权限或配置动态显示列的情况。这是我总结的最佳实践:
javascript复制// 获取基础列配置
const baseColumns = [
{ field: 'id', title: 'ID' },
{ field: 'name', title: '姓名' }
]
// 获取动态列配置
async function loadDynamicColumns() {
const res = await api.getDynamicColumns()
return res.data.map(item => ({
field: item.field,
title: item.title,
visible: item.required || this.userPermission.includes(item.field)
}))
}
// 最终合并列配置
this.columns = [
...baseColumns,
...await loadDynamicColumns()
]
对于需要持久化列配置的场景,可以结合localStorage:
javascript复制// 保存列配置
function saveColumnConfig() {
localStorage.setItem('tableColumns', JSON.stringify(this.columns))
}
// 初始化时读取配置
function initColumns() {
const saved = localStorage.getItem('tableColumns')
if (saved) {
this.columns = JSON.parse(saved)
} else {
this.loadDefaultColumns()
}
}
vxe-grid提供了多种编辑模式,我个人最推荐的是行编辑模式:
html复制<vxe-grid
:edit-config="{ trigger: 'click', mode: 'row', showStatus: true }"
:columns="[
{
field: 'name',
title: '姓名',
editRender: { name: 'input' }
},
{
field: 'gender',
title: '性别',
editRender: {
name: 'select',
options: [
{ label: '男', value: 1 },
{ label: '女', value: 2 }
]
}
}
]"
/>
处理编辑完成事件:
javascript复制<vxe-grid @edit-closed="handleEditClosed"/>
methods: {
async handleEditClosed({ row, column }) {
try {
await api.updateRow(row)
this.$message.success('保存成功')
} catch (err) {
this.$refs.xGrid.revert(row) // 回滚修改
}
}
}
处理组织架构等树形数据时,懒加载能显著提升性能:
html复制<vxe-grid
:tree-config="{
lazy: true,
children: 'children',
hasChild: 'hasChildren',
loadMethod: loadChildrenMethod
}"
/>
实现懒加载方法:
javascript复制async loadChildrenMethod({ row }) {
const res = await api.getChildren(row.id)
return res.data.map(item => ({
...item,
hasChildren: item.type === 'department' // 标记是否还有子节点
}))
}
优化小技巧:添加加载状态提示
javascript复制async loadChildrenMethod({ row }) {
this.$set(row, 'loading', true)
try {
const res = await api.getChildren(row.id)
return res.data
} finally {
this.$set(row, 'loading', false)
}
}
当处理万级数据时,这些优化措施很有效:
html复制<vxe-grid
height="600"
:scroll-y="{ enabled: true, gt: 50 }"
/>
javascript复制// 不要一次性加载所有数据
async loadData(page = 1) {
this.loading = true
try {
const res = await api.getBigData({ page })
this.tableData = this.tableData.concat(res.data)
if (res.data.length) {
this.loadData(page + 1) // 递归加载下一页
}
} finally {
this.loading = false
}
}
javascript复制columns: [
{ field: 'id', width: 100, fixed: 'left' },
// 中间列不设固定宽度
{ field: 'description', minWidth: 200 },
{ field: 'action', width: 150, fixed: 'right' }
]
在项目实践中,我遇到过几个典型问题:
javascript复制// 错误做法
this.columns.push(newColumn)
// 正确做法
this.columns = [...this.columns, newColumn]
javascript复制// 强制退出所有编辑状态
this.$refs.xGrid.clearEdit()
// 重置表格状态
this.$refs.xGrid.reloadData(this.tableData)
javascript复制VXETable.renderer.add('MyRender', {
renderDefault(h, params) {
return <span>{params.value}</span>
}
})
javascript复制// 手动控制展开状态
this.$refs.xGrid.setTreeExpand(row, true)
// 刷新树形结构
this.$refs.xGrid.reloadTreeData()
vxe-grid支持多种自定义渲染方式,这是我在项目中常用的几种:
javascript复制columns: [
{
field: 'status',
title: '状态',
cellRender: {
name: 'MyStatusRender'
}
}
]
// 注册渲染器
VXETable.renderer.add('MyStatusRender', {
renderDefault(h, { row }) {
const statusMap = {
1: { text: '进行中', color: 'orange' },
2: { text: '已完成', color: 'green' }
}
return (
<span style={`color: ${statusMap[row.status].color}`}>
{statusMap[row.status].text}
</span>
)
}
})
html复制<vxe-column field="avatar" title="头像">
<template #default="{ row }">
<img :src="row.avatar" class="avatar-img" />
</template>
</vxe-column>
通过toolbar-config可以添加自定义按钮:
html复制<vxe-grid
:toolbar-config="{
slots: {
buttons: 'myButtons'
}
}"
>
<template #myButtons>
<vxe-button @click="exportExcel">导出Excel</vxe-button>
<vxe-button @click="batchDelete">批量删除</vxe-button>
</template>
</vxe-grid>
实现导出功能:
javascript复制async exportExcel() {
const { data } = await api.getAllData()
const columns = this.columns
.filter(col => col.visible !== false)
.map(col => ({
field: col.field,
title: col.title
}))
this.$refs.xGrid.exportData({
data,
columns,
filename: '数据导出'
})
}
在同时使用Element和vxe-grid的项目中,样式兼容很重要:
css复制/* 避免样式冲突 */
.vxe-table--render-default .el-input__inner {
border: none;
padding: 0;
}
javascript复制columns: [
{
field: 'date',
title: '日期',
editRender: {
name: 'MyDatePicker',
props: { /* Element DatePicker的props */ }
}
}
]
// 注册自定义编辑器
VXETable.renderer.add('MyDatePicker', {
renderEdit(h, { row, column }) {
return <el-date-picker v-model={row[column.property]} />
}
})
对于大型项目,建议将表格状态管理到Vuex:
javascript复制// store/modules/table.js
export default {
state: {
columns: [],
data: []
},
mutations: {
SET_COLUMNS(state, columns) {
state.columns = columns
},
SET_DATA(state, data) {
state.data = data
}
},
actions: {
async fetchTableData({ commit }, params) {
const res = await api.getData(params)
commit('SET_DATA', res.data)
return res
}
}
}
组件中使用:
javascript复制computed: {
columns() {
return this.$store.state.table.columns
},
tableData() {
return this.$store.state.table.data
}
},
methods: {
async loadData() {
await this.$store.dispatch('table/fetchTableData', {
page: this.currentPage
})
}
}
虽然vxe-grid主要面向PC端,但通过一些技巧也能在移动端使用:
css复制/* 在小屏幕下调整布局 */
@media (max-width: 768px) {
.vxe-table--body-wrapper {
overflow-x: auto;
}
.vxe-table {
min-width: 800px;
}
}
javascript复制const isMobile = window.innerWidth < 768
this.gridOptions = {
border: isMobile ? false : true,
showHeader: isMobile ? false : true,
columnConfig: {
resizable: !isMobile
}
}
javascript复制// 禁用双击编辑
editConfig: {
trigger: isMobile ? 'click' : 'dblclick'
}
// 增加点击区域
.vxe-cell {
padding: 12px 8px;
}
为vxe-grid组件编写测试用例时,重点验证这些方面:
javascript复制describe('vxe-grid测试', () => {
it('应该正确渲染列', async () => {
const wrapper = mount(MyGrid, {
propsData: {
columns: [{ field: 'name', title: '姓名' }],
data: [{ name: '张三' }]
}
})
expect(wrapper.find('.vxe-header--column').text()).toBe('姓名')
expect(wrapper.find('.vxe-body--column').text()).toBe('张三')
})
it('应该触发分页事件', async () => {
const wrapper = mount(MyGrid, {
propsData: {
pagerConfig: {
pageSize: 10,
currentPage: 1
}
}
})
await wrapper.find('.pager--next').trigger('click')
expect(wrapper.emitted('page-change')[0][0].currentPage).toBe(2)
})
})
开发过程中这些调试命令很有用:
javascript复制// 获取当前表格实例
const grid = this.$refs.xGrid
// 打印当前表格状态
console.log({
data: grid.tableData,
columns: grid.columns,
sort: grid.sort,
filter: grid.filter
})
// 检查虚拟DOM
console.log(this.$refs.xGrid.$el)
在最近的一个CRM系统中,我使用vxe-grid实现了这些复杂功能:
javascript复制async function initTable() {
// 获取用户配置
const config = await api.getUserConfig(this.userId)
// 生成列配置
this.columns = config.fields.map(field => ({
field: field.key,
title: field.label,
visible: field.visible,
width: field.width,
sortable: field.sortable
}))
// 设置默认排序
if (config.defaultSort) {
this.sortConfig = {
field: config.defaultSort.field,
order: config.defaultSort.order
}
}
}
javascript复制columns: [
{
title: '基本信息',
children: [
{ field: 'name', title: '姓名' },
{ field: 'gender', title: '性别' }
]
},
{
title: '联系信息',
children: [
{ field: 'phone', title: '电话' },
{ field: 'email', title: '邮箱' }
]
}
]
javascript复制// 主表格
<vxe-grid
@current-change="handleMainTableChange"
/>
// 从表格
methods: {
handleMainTableChange({ row }) {
this.$refs.subGrid.reloadData(row.children)
}
}
经过多个项目的实践,我总结了这些vxe-grid使用经验:
javascript复制// 统一错误处理
async fetchData() {
try {
this.loading = true
await this.$refs.xGrid.loadData()
} catch (err) {
this.$message.error(err.message)
} finally {
this.loading = false
}
}
想要深入掌握vxe-grid,这些资源很有帮助:
javascript复制console.log(this.$refs.xGrid.getPerformance())
在最近的一个电商后台项目中,我们处理了日均10万+订单数据的展示需求。通过合理配置vxe-grid的虚拟滚动和分页加载,即使在低配电脑上也能流畅操作。特别是在处理促销活动期间的数据高峰时,这种优化带来的性能提升非常明显。