大家好,我是 前端架构师 - 大卫。
更多优质内容请关注微信公众号 @程序员大卫。
初心为助前端人🚀,进阶路上共星辰✨,
您的点赞👍与关注❤️,是我笔耕不辍的灯💡。
背景
vxe-table
是一个基于 Vue 的 PC 端表格组件,功能非常丰富,并且支持 Vue 2.6+
和 Vue 3.x
最新版本。从它的 issues 可以看出,作者解决了很多问题,但目前看起来是由一个人维护。随着版本功能不断增加,有 issues
反馈性能相较于旧版本有所下降。
下面是支持 Vue2 和 Vue3 的源码地址:
支持
Vue 2.6+
的vxe-table
的源码地址:github.com/x-extends/v...
支持Vue 3.x
的vxe-table
的源码地址:github.com/x-extends/v...
说说 Vue2 的问题
我之前遇到的一些老项目使用的是 Vue2,因为这些项目可能还需要兼容 IE11 浏览器,所以 Vue3 不太合适。而 Vue2 可以说是后端开发者写前端代码的便利框架,有以下几点原因,同时也是 Vue2 的缺点:
Vue2
不强制使用 Typescript,因此写代码的门槛较低,但这也导致后期的重构和维护成本较高。Vue2
可以往Vue.config
和Vue.prototype
添加任意全局配置属性和实例方法,导致全局污染严重;而Vue3
已经不再支持这种做法。
js
Vue.config.productionTip = false
Vue.prototype.$Alert = Alert
Vue2
对子组件的控制权限过大,只要给子组件加上ref
,就可以获取其所有的 data 和 methods,这对项目的维护和重构造成很大困扰,不敢随意改动子组件中的数据和方法。而Vue3
需要使用defineExpose
显式暴露属性和方法,React
则是使用useImperativeHandle
。
html
<template>
<div id="app">
<ChildCom ref="childRef" />
</div>
</template>
<script>
import ChildCom from "./components/ChildCom.vue";
export default {
name: "App",
components: {
ChildCom,
},
mounted() {
console.log(this.$refs.childRef.num);
console.log(this.$refs.childRef.increace);
},
};
</script>
vxe-table 里的结构
vxe-table
一共包含 5 个组件,下面简单介绍它们的作用:
vxe-table
:基础表格组件。虽然v3.9+
版本已将表格与 UI 组件分离,但其dependencies
中仍然依赖vxe-pc-ui
,因为vxe-table
的某些功能仍使用了vxe-pc-ui
。vxe-pc-ui
:一个 PC 端组件库,你可以把它理解成类似element ui
的 UI 库,它的dependencies
中依赖@vxe-ui/core
,vxe-table
的部分功能依赖它。例如在vxe-table 3.15.34
中,如果<vxe-table show-overflow>
组件使用了show-overflow
属性,就会调用vxe-pc-ui
中的vxe-tooltip
组件。@vxe-ui/core
:封装了和表格相关的一些公共方法,比如i18n
、permission
、theme
、VxeGlobalConfig
等。它依赖于dom-zindex
和xe-utils
。dom-zindex
:一个用来简单控制和设置 zIndex 的工具包。xe-utils
:封装了一些常用工具方法,你可以将其类比为lodash
。
⚠️ 注意:
vxe-table v3.9+
已将纯表格和 UI 组件分离,而之前是未分离的。具体可参考:vxetable.cn/v3.8/#/tabl...
vxe-table 里的 5 个组件
最常用的组合是 vxe-table
和 vxe-column
,其他几个组件相对使用频率较低。
1. vxe-table
基础表格功能。
html
<template>
<div>
<vxe-table
:data="tableData">
<vxe-column type="seq" width="70"></vxe-column>
<vxe-column field="name" title="Name"></vxe-column>
<vxe-column field="sex" title="Sex"></vxe-column>
<vxe-column field="age" title="Age"></vxe-column>
</vxe-table>
</div>
</template>
<script>
export default {
data () {
const tableData = [
{ id: 10001, name: 'Test1', role: 'Develop', sex: 'Man', age: 28, address: 'test abc' },
{ id: 10002, name: 'Test2', role: 'Test', sex: 'Women', age: 22, address: 'Guangzhou' },
{ id: 10003, name: 'Test3', role: 'PM', sex: 'Man', age: 32, address: 'Shanghai' },
{ id: 10004, name: 'Test4', role: 'Designer', sex: 'Women', age: 24, address: 'Shanghai' }
]
return {
tableData
}
}
}
</script>
2. vxe-column
表格列的标识,可参考上面 vxe-table
示例。
3. vxe-colgroup
分组表头,用于多级表头展示。
html
<template>
<div>
<vxe-table
border
height="400"
:data="tableData">
<vxe-colgroup title="基本信息">
<vxe-column type="seq" width="70"></vxe-column>
<vxe-column field="name" title="Name"></vxe-column>
</vxe-colgroup>
<!-- other code... -->
</vxe-table>
</div>
</template>
4. vxe-grid
配置式表格:以 JSON 方式调用,适合用于低代码平台,纯 JSON 生成表格。
具体可参考: vxetable.cn/#/component...
5. vxe-toolbar
工具栏。
具体可参考: vxetable.cn/#/global/co...
如何让 vxe-table 3.5.9 和 3.15.34 共存
由于公司老项目使用的是 [email protected]
,它不支持虚拟滚动下的自适应行高,而部分页面组件需要使用该功能。
如果将 vxe-table
从 3.5.9
一次性升级到 3.15.34
,风险较大。下面我们将采用工程化的思路,实现这两个版本在项目中共存。
1. 下载 vxe-table 3.15.34 的代码
访问 vxe-table
官方地址:github.com/x-extends/v...,从 master
分支切换到 v3
分支,下载并解压代码,目录命名为 vxe-table-v3
。
执行以下命令:
bash
npm install --legacy-peer-deps // 安装依赖
npm run lib // 打包
最终会生成三个目录:es
、helper
和 lib
。其中,es
模块的入口文件为 es/index.esm.js
,通常用于按需引入或 ES Module 规范的引用场景。
⚠️ 注意:建议使用 Node.js 版本
18.20.8
(对应的 npm 版本是10.8.2
),不要使用pnpm
安装依赖,该项目较老,使用pnpm
会安装失败。
2. 修改 vxe-table 3.15.34 代码
为了避免和 3.5.9 版本的代码冲突,需要修改以下几点。
1. 修改 packages/table/index.ts
主要修改点在于:将组件注册到 app 时添加版本后缀 3,以避免未来全局注册与原版本发生冲突。
ts
export const VxeTable = Object.assign({}, VxeTableComponent, {
install (app: VueConstructor) {
// ...
app.component(VxeTableComponent.name as string + '3', VxeTableComponent)
}
})
然后删除一些冗余的代码,这些代码是为了调试用途而注入到 Vue.prototype
上的:
ts
if (!Vue.prototype.$vxe) {
Vue.prototype.$vxe = { t: VxeUI.t, _t: VxeUI._t }
} else {
Vue.prototype.$vxe.t = VxeUI.t
Vue.prototype.$vxe._t = VxeUI._t
}
2. 修改 packages/column/index.ts
与上面类似,添加版本标识 3
。
ts
export const VxeColumn = Object.assign({}, VxeColumnComponent, {
install (app: VueConstructor) {
app.component(VxeColumnComponent.name as string + '3', VxeColumnComponent)
}
})
3. 修改 packages/colgroup/index.ts
同样添加版本标识 3
。
ts
export const VxeColgroup = Object.assign({}, VxeColgroupComponent, {
install (app: VueConstructor) {
app.component(VxeColgroupComponent.name as string + '3', VxeColgroupComponent)
}
});
4. 修改 packages/grid/index.ts
继续添加版本标识 3
。
ts
export const VxeGrid = Object.assign({}, VxeGridComponent, {
install (app: VueConstructor) {
app.component(VxeGridComponent.name as string + '3', VxeGridComponent)
}
});
5. 修改 packages/toolbar/index.ts
同理,添加版本标识 3
。
ts
export const VxeToolbar = Object.assign({}, VxeToolbarComponent, {
install (app: VueConstructor) {
app.component(VxeToolbarComponent.name as string + '3', VxeToolbarComponent)
}
});
3. 初始化打包库的代码
创建文件夹 vxe-table-lib
,在其上级目录执行以下 Vite 脚手架命令:
bash
pnpm create vite vxe-table-lib --template vue-ts
cd vxe-table-lib
pnpm install
安装 postcss-prefix-selector
,用于给所有样式类添加前缀,避免与 vxe-table 3.5.9
样式冲突:
bash
pnpm i postcss-prefix-selector @types/postcss-prefix-selector -D
4. 修改打包库的代码
创建文件 lib/main.ts
,内容如下:
ts
import "../../vxe-table-v3/lib/style.css";
export {
VxeColumn as VxeColumn3,
VxeColgroup as VxeColgroup3,
VxeGrid as VxeGrid3,
VxeTable as VxeTable3,
VxeToolbar as VxeToolbar3,
VxeUI as VxeUI3,
} from "../../vxe-table-v3";
修改 vite.config.ts
如下:
ts
import vue from "@vitejs/plugin-vue";
import postcssPrefixSelector from "postcss-prefix-selector";
import { defineConfig } from "vite";
// https://vite.dev/config/
export default defineConfig({
plugins: [vue()],
build: {
lib: {
entry: "./lib/main.ts",
formats: ["es"],
},
commonjsOptions: {
include: [/vxe-table-v3/],
},
rollupOptions: {
external: ["vue", "vue/jsx-runtime"],
output: {
assetFileNames: "style.css",
entryFileNames: "[name].js",
},
},
copyPublicDir: false,
},
css: {
postcss: {
plugins: [
postcssPrefixSelector({
prefix: `.vxe-table-wrapper-v3`,
transform(_prefix, selector, prefixedSelector) {
// 只对 .xxx class 类起作用,而对于 :root, @keyframe, [data-xxx] 不起作用。
if (selector.startsWith(".") || selector.startsWith("#")) {
return prefixedSelector;
}
return selector;
},
}),
],
},
},
});
postcssPrefixSelector
插件的作用是给所有样式添加一个类名前缀 .vxe-table-wrapper-v3
。
注意其中传递了一个 commonjsOptions
属性,它是专门用于处理 CommonJS 模块(CJS)的配置。Vite 会借助该插件将 CJS 模块转换为 ES 模块,从而在构建过程中正常处理。如果不加这个配置,打包会失败,因为 vxe-table-3
中的部分代码导出方式不符合 ES 规范,具体会报如下错误:
vbnet
✗ Build failed in 176ms
error during build:
lib/main.ts (4:2): "VxeColumn" is not exported by "../vxe-table-3/es/index.esm.js", imported by "lib/main.ts".
file: /Users/zhengming/git/wechat-oa/examples/vxe-table-3-build/vxe-table-build/lib/main.ts:4:2
2:
3: export {
4: VxeColumn as VxeColumn3,
^
5: VxeColgroup as VxeColgroup3,
6: VxeGrid as VxeGrid3,
// ...
typescript
error during build:
../vxe-table-3/node_modules/@vxe-ui/core/es/src/config.js (1:7): "default" is not exported by "../vxe-table-3/node_modules/xe-utils/index.js", imported by "../vxe-table-3/node_modules/@vxe-ui/core/es/src/config.js".
file: /Users/zhengming/git/wechat-oa/examples/vxe-table-3-build/vxe-table-3/node_modules/@vxe-ui/core/es/src/config.js:1:7
1: import XEUtils from 'xe-utils';
^
2: import DomZIndex from 'dom-zindex';
3: import { VxeCore } from './core';
// ...
5. 完成打包,生成 dist 文件
执行以下命令,生成文件 dist/main.js
和 dist/style.css
。
bash
pnpm build
6. 验证两个版本的共存
1. 初始化项目代码
创建文件夹 vxe-table-test
,并初始化一个 Vue2
项目:
bash
npm create vue@legacy // 根据提示一步步创建
cd vxe-table-test
pnpm install
bash
pnpm i [email protected]
2. 编写基于 vxe-table 3.5.9
的组件
1.在 main.js
中全局注册 vxe-table 3.5.9
js
import VxeUITable from "vxe-table";
import "vxe-table/lib/style.css";
Vue.use(VxeUITable);
2.在 src/components
目录下新建 VxeTable_3_5_9.vue
文件,并编写如下代码:
html
<template>
<div>
<vxe-table
border
show-overflow
height="300"
:column-config="{ resizable: true }"
:virtual-y-config="{ enabled: true, gt: 0 }"
:data="tableData"
>
<vxe-column type="seq" width="70"></vxe-column>
<vxe-column field="name" title="Name"></vxe-column>
<vxe-column field="role" title="Role"></vxe-column>
<vxe-column field="sex" title="Sex"></vxe-column>
</vxe-table>
</div>
</template>
<script>
export default {
data() {
return {
tableData: [],
};
},
methods: {
// 模拟生成表格数据
loadList(size = 300) {
const dataList = [];
for (let i = 0; i < size; i++) {
dataList.push({
id: 10000 + i,
name: "Test" + i,
role: "Developer",
sex: i === 0 ? "男".repeat(100) : "男",
});
}
this.tableData = dataList;
},
},
created() {
this.loadList();
},
};
</script>
<style>
body .vxe-table .vxe-body--column.col--ellipsis:not(.col--actived) > .vxe-cell {
white-space: normal;
max-height: fit-content;
}
</style>
3. 编写基于 vxe-table 3.15.34
的组件
在 src/components
目录下创建 VxeTable_3_15_34.vue
文件,并引入之前打包生成的 vxe-table-lib/dist
中的组件,并编写如下代码:
html
<template>
<div class="vxe-table-wrapper-v3">
<vxe-table3
border
height="300"
:column-config="{ resizable: true }"
:virtual-y-config="{ enabled: true, gt: 0 }"
:data="tableData"
>
<vxe-column3 type="seq"></vxe-column3>
<vxe-column3 field="name" title="Name"></vxe-column3>
<vxe-column3 field="role" title="Role"></vxe-column3>
<vxe-column3 field="sex" title="Sex"></vxe-column3>
</vxe-table3>
</div>
</template>
<script>
import { VxeTable3, VxeColumn3 } from "../../../vxe-table-lib/dist/main.js";
import "../../../vxe-table-lib/dist/style.css";
export default {
components: {
VxeTable3,
VxeColumn3,
},
data() {
return {
tableData: [],
};
},
methods: {
// 模拟行数据
loadList(size) {
setTimeout(() => {
const dataList = [];
for (let i = 0; i < size; i++) {
dataList.push({
id: i,
name: "Test" + i,
role: "Developer",
sex:
i === 0
? "男".repeat(100)
: "男",
});
}
this.tableData = dataList;
}, 100);
},
},
mounted() {
this.loadList(300);
},
};
</script>
4. 在 App.vue
中引入两个版本的组件
html
<script setup>
import VxeTable_3_15_34 from "./components/VxeTable_3_15_34.vue";
import VxeTable_3_5_9 from "./components/VxeTable_3_5_9.vue";
</script>
<template>
<div id="app">
<VxeTable_3_15_34 />
<div style="height: 40px"></div>
<VxeTable_3_5_9 />
</div>
</template>
5. 启动项目
执行以下命令以本地启动项目:
bash
pnpm dev
6. 最终效果验证
启动成功后,页面上可以同时看到两个版本的 vxe-table
渲染结果:
[email protected]
支持不定高的虚拟滚动;[email protected]
则不支持该特性。
另外如果给 [email protected]
添加 show-overflow
属性时,控制台会提示如下警告:
css
main.js:3649 [vxe table v3.15.33] 缺少 "vxe-tooltip" 组件,请检查是否正确安装。
这是因为 vxe-tooltip
是 vxe-pc-ui
包中的组件,当前项目中未安装该依赖。因此,在未安装 vxe-pc-ui
的情况下,应避免使用 show-overflow
属性。