vxe-table 在项目中的实践!【附源码】

大家好,我是 前端架构师 - 大卫

更多优质内容请关注微信公众号 @程序员大卫

初心为助前端人🚀,进阶路上共星辰✨,

您的点赞👍与关注❤️,是我笔耕不辍的灯💡。

背景

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.xvxe-table 的源码地址:github.com/x-extends/v...

说说 Vue2 的问题

我之前遇到的一些老项目使用的是 Vue2,因为这些项目可能还需要兼容 IE11 浏览器,所以 Vue3 不太合适。而 Vue2 可以说是后端开发者写前端代码的便利框架,有以下几点原因,同时也是 Vue2 的缺点:

  • Vue2 不强制使用 Typescript,因此写代码的门槛较低,但这也导致后期的重构和维护成本较高。
  • Vue2 可以往 Vue.configVue.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/corevxe-table 的部分功能依赖它。例如在 vxe-table 3.15.34 中,如果 <vxe-table show-overflow> 组件使用了 show-overflow 属性,就会调用 vxe-pc-ui 中的 vxe-tooltip 组件。
  • @vxe-ui/core:封装了和表格相关的一些公共方法,比如 i18npermissionthemeVxeGlobalConfig 等。它依赖于 dom-zindexxe-utils
  • dom-zindex:一个用来简单控制和设置 zIndex 的工具包。
  • xe-utils:封装了一些常用工具方法,你可以将其类比为 lodash

⚠️ 注意:vxe-table v3.9+ 已将纯表格和 UI 组件分离,而之前是未分离的。具体可参考:vxetable.cn/v3.8/#/tabl...

vxe-table 里的 5 个组件

最常用的组合是 vxe-tablevxe-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>

具体可参考:vxetable.cn/#/component...

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-table3.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 // 打包

最终会生成三个目录:eshelperlib。其中,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.jsdist/style.css

bash 复制代码
pnpm build

6. 验证两个版本的共存

1. 初始化项目代码

创建文件夹 vxe-table-test,并初始化一个 Vue2 项目:

bash 复制代码
npm create vue@legacy // 根据提示一步步创建
cd vxe-table-test
pnpm install

安装 [email protected]

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] 添加 show-overflow 属性时,控制台会提示如下警告:

css 复制代码
main.js:3649 [vxe table v3.15.33] 缺少 "vxe-tooltip" 组件,请检查是否正确安装。

这是因为 vxe-tooltipvxe-pc-ui 包中的组件,当前项目中未安装该依赖。因此,在未安装 vxe-pc-ui 的情况下,应避免使用 show-overflow 属性。

源码地址

github.com/zm8/wechat-...

相关推荐
又又呢44 分钟前
前端面试题总结——webpack篇
前端·webpack·node.js
dog shit2 小时前
web第十次课后作业--Mybatis的增删改查
android·前端·mybatis
我有一只臭臭2 小时前
el-tabs 切换时数据不更新的问题
前端·vue.js
七灵微2 小时前
【前端】工具链一本通
前端
Nueuis3 小时前
微信小程序前端面经
前端·微信小程序·小程序
_r0bin_5 小时前
前端面试准备-7
开发语言·前端·javascript·fetch·跨域·class
IT瘾君5 小时前
JavaWeb:前端工程化-Vue
前端·javascript·vue.js
zhang98800005 小时前
JavaScript 核心原理深度解析-不停留于表面的VUE等的使用!
开发语言·javascript·vue.js
potender5 小时前
前端框架Vue
前端·vue.js·前端框架
站在风口的猪11086 小时前
《前端面试题:CSS预处理器(Sass、Less等)》
前端·css·html·less·css3·sass·html5