Element 系列的组件库自 Vue2 出现以来,就已经成为大部分开发者的首选 UI 组件库,直至今天,Element UI 跟 Element Plus 的周下载量都维持在 10w 以上 ,Element 系列组件库相信是大部分 Vue 初学者的入坑首选工具,7 年前,Element-UI 发布了他的第一个版本,但在 7 年后的现在,在搭建新项目时,Vue3 开发者或许应该对 Element Plus 说再见了。
项目背景
Element UI 组件如此受欢迎,我觉的有三点因素
- 背靠饿了么大厂,有专业团队支持维护
- 花裤衩大佬 Vue-admin 的开源项目相互成就
- 培训班教学项目以及大学生课程作业的广告效应
因此,当 Vue 发布了新版本 Vue3 时,为了选取配套的组件库,Element Plus 成为了大部分人的首选。事实上,饿了么官方并没有 Vue3 版本的组件库计划,后续社区内展开了 Element3 的开源教培项目,20 年 Element-Plus 官方版本正式发布,后续由饿了么与开源工作者共同维护,但 Element Plus 的组件一定程度上借鉴了 Element UI 的设计与思路,以至于后续维护上都存在许多问题。
事实上,我个人对 Element Plus 有着较为复杂的情感,一方面社区大量开源开发者参与其中,推动了 Vue3 生态的发展,相信很多开发者跟我一样,在 GitHub 上的 第一个 PR 是通过 Element Plus 完成的,但另一方面当 Element Plus 落地到公司项目生产,业务对外交付时,很难说它是一个优秀的框架。
项目问题
- 审美疲劳的 UI 设计
上图是我选取的 Github 上 start 星数比较多的模板项目,不能说这些项目有什么问题,只能说就是那个味。最近的一个月内,我发现了 chrome,网易云都重构了应用的 UI 设计,同样的,或许你也应该给你的项目换换衣服了。
- 社区大量的 issue
截止今天,Element Plus 仍有 1.3k 的 issue,这些 issue 中有除去建议、讨论跟功能请求,粗略估计还有几百个 bug 仍未解决。
- 违背常理的默认行为
这里就以我最近遇到的问题为例
1、懒加载树表格全选反选逻辑只针对第一层节点有效
2、 树节点拖拽到树区域外部,drop 事件依然生效
这些问题虽然都不影响组件主流程的运行,但是解决起来很是麻烦,要不阅读源码,要不曲线救国,费一番心思。
- 积重难返的数据展示组件
直至今天,Element-Plus 的虚拟化功能还不能完全面向生产,同时,我比较想吐槽的一点就是虚拟化表格组件被官方命名 el-table-v2
,既然有了 v2 是不是后续还会出现 v3、v4、v5?个人只能猜测表格组件为了解决性能问题并不能在原来的组件上很好的兼容向后开发,只能进行 copy 然后 rewrite,同时组件在命名上并没有经过太多的考量而被命名为 v2。
事实上大部分Element Plus 的大部分数据展示组件都会遇到性能问题,比如选择框的下拉选项,树形选择,树表格,这些组件设计之初并没有对性能问题进行规划,之后为了解决这些问题,没有选择在原有的组件添加 prop 去开关虚拟化进行兼容,而是对外开放新的组件。我虽然不可能要求这些数据展示组件能尽可能的满足业务开发需求,但是仅仅作为数据展示组件,这些组件的开发体验仍是不尽人意。
- 插槽与 JavaScript 之间的割裂感
这点说是 Element-Plus 的问题,倒不如说是使用模板 DSL 的通病,就以表格为例,如何渲染自定义化的 column,用插槽,但是表格列巨多呢,我们就既需要维护模板中的自定义化插槽,又需要维护JavaScript中的业务逻辑,这种割裂感真的十分破坏开发体验。
为此 大部分 Vue 组件库使用 colums 配置 table prop 的方法(这也是大部分开源项目二开 el-table 的思路),减轻这种割裂感
1、 第一种写法,引入 Vue 编译时,使用字符串模板
<a-table
:columns="columns"
/>
const columns = computed(() => {
return [
{
title: order,
render({
rowIndex,
}: {
record: TableData;
column: TableColumnData;
rowIndex: number;
}) {
const tmp = `<span>${rowIndex + 1}</span>`;
return h(compile(tmp));
},
},
{
title:cover,
render({
record,
}: {
record: TableData;
column: TableColumnData;
rowIndex: number;
}) {
const tmp = `<div class='data-statistic-list-cover-wrapper'>
<img src=${record.cover} />
${renderTag(record.status)}
</div>`;
return h(compile(tmp));
},
},
{
title: name,
dataIndex: 'name',
},
{
dataIndex: 'duration',
title: duration,
},
{
dataIndex: 'id',
title: id,
},
];
});
2、 另外一种指定插槽名,然后在模板中自定义渲染列内容
<a-table :columns="columns" :data="data">
<template #name="{ rowIndex }">
<a-input v-model="data[rowIndex].name" />
</template>
<template #province="{ rowIndex }">
<a-select v-model="data[rowIndex].province" @change="()=>handleChange(rowIndex)">
<a-option v-for="value of Object.keys(options)">{{value}}</a-option>
</a-select>
</template>
<template #city="{ rowIndex }">
<a-select :options="options[data[rowIndex].province] || []" v-model="data[rowIndex].city" />
</template>
</a-table>
const columns = [{
title: 'Name',
dataIndex: 'name',
slotName: 'name'
}, {
title: 'Province',
dataIndex: 'province',
slotName: 'province'
}, {
title: 'City',
dataIndex: 'city',
slotName: 'city'
}, {
title: 'Email',
dataIndex: 'email',
}];
但事实上,字符串根本不适合进行虚拟DOM 的描述,模板与渲染逻辑的割裂感也不能完全解决,这就是我最近尝试 Vue3 tsx 开发的原因。
工具选取,社区共建
跑远了,无论怎样,我个人都不建议新开项目的时候,将 Element Plus 作为组件工具的首选项了。那么如何寻找替代品呢?我个人内心一直期望 Vue 生态上有一个 ant-design 类似的地位的组件库,无论是 umi 还是 ant-design-pro, 真的让我酸的很。
但没有就是没有,没有只能自己造轮子,Vue 生态下也不少工具值得我们发掘与维护, ant-design-vue 一直在模仿、跟随老大哥的脚步,naive ui 在不少开发者之间广为流传,有了不错的口碑,arco-design 背靠字节系,vuetify 国外团队维护,周下载量能上 50w+,这些工具都具备成为中后台完整工具链的潜质,但在发展的过程中,少不了我们的关注与支持,同时,我个人最近也在尝试将 Vue3 中后台开发中的最佳实践进行总结,期望与大家分享。
如若你不赞同我的说法,也欢迎你加入到 Element Plus 的社区共建之中。
感谢您阅读本文,希望对您有所帮助。如果您觉得本文对您有价值,请点赞并收藏,以便日后查阅。如果您对我的观点有不同的看法或意见,欢迎与我进行交流,谢谢。