前面在 vue2中如何使用render函数 有提到,vue 官网给出的 render 函数的例子只能体现 render 函数的优雅的一方面,却不能看出其扩展性,今天就来封装一个体现其扩展性的组件。
需求
后台管理中常常有如下布局的数据展示需求:
像表格又不是表格,像表单又不是表单,实际上样子像表格,呈现的数据是一个对象,和 form 的绑定的值一样,我将其称为表单式表格。
样式深的列是标题,浅的列是标题对应的取值,数据往往是服务器返回的,标题往往是定宽的,取值可能各种各样,比如显示一张图片,值为 01,需要显示是与否,有时候需要添加一个修改按钮,让用户能修改某些值,还需要设置某一列跨越几列。
先来看看一个基于 element ui 的实现
不好的实现
在接手的项目看到一个实现,先看使用方式
<FormTable :data="lessonPackageArr" :fleldsInfo="lessonPackageInfo" :maxColumn="3" label-width="120px">
<template #presentedHours="{ data }">
<div class="flex-box between">
<span>{
{ data.presentedHours }}</span>
<span class="column-btn" @click="editPresentedHours(data)">修改</span>
</div>
</template>
<template #gifts="{ data }">
<div class="flex-box between">
<span>{
{ data.gifts }}</span>
<span class="column-btn" @click="editPresentedHours(data)">修改</span>
</div>
</template>
</FormTable>
lessonPackageInfo
对象如下结构:
// 一个对象,用于配置标题列和标题列对应的字段
// type 指定值的类型,现在组件内部设置可能显示哪些类型的值了
// 对于服务其返回 1 0 需要显示 是否的数,提供一个 map_data 来映射
// column 属性设置跨列
// 需要自定义显示内容 提供 slot
lessonPackageInfo: {
orderType: {
type: 'option', desc: '课时包类别', map_data: {
1: '首单', 2: '续费', 5: '赠课' } },
combo: {
type: 'text', desc: '套餐名称' },
presentedHours: {
type: 'text', desc: '赠送课时', slot: true },
price: {
type: 'text', desc: '标准价格' },
gifts: {
type: 'text', desc: '赠送礼物', column: 3, slot: true },
}
存在什么问题?
- props 不够直观,配置项多
- 不是完全数据驱动
为何组件的配置项多不好?
对于这种需求很固定,组件的输入即 props
应该要最小化,组件功能要最大化,尽量给 props 提供默认值,这样才能提高团队的开发效率。
为何不是完全的数据驱动不好?
这个组件不是完全数据驱动的,需要自定义显示列是,需要编写模板。
如果需要自定义的列很多,就要写很多模板代码,想要再提取,只能再次封装组件,不提取,模板代码可能会膨胀,你可能经常看到动辄 500 行一行的 template ?而膨胀的模板代码,让组件维护变得困难,需要 template 和 js 代码之间来回切换。再者,增加一列自定义的数据,起码要修改两个地方。
为何需要完全的数据驱动?
虽然有 slot 来扩展组件,但是我们在写业务组件时候应该少用,而是尽量使用数据驱动模板。因为数据是 js 代码,当组件代码膨胀时,很容易把 js 代码提取成单独的文件, 而想要提取 slot 的代码,只能再封装组件。
说明
三大前端框架的设计理念都是数据驱动模板,这是它们区别于 jQuery 的重要特征,也是我们封装业务组件时优先遵循的原则。
看了组件使用的问题,再看组件的代码:
<