接前两篇 《项目创建与初始配置》 与 《国际化配置》 继续搭建我们的后台项目。
后台管理项目中大量使用的组件之一就是表格了,在 Element Plus 中,表格组件大概是这样使用的:
<!-- 代码片段一 -->
<template>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="date" label="Date" width="180" />
<el-table-column label="Name" width="180">
<template #default="scope">
<el-tag>{{ scope.row.name }}</el-tag>
</template>
</el-table-column>
</el-table>
</template>
只要把从接口获取的数组数据 tableData
传给 el-table 的 data
,然后表格的每一列要显示什么数据就通过
el-table-column 的 prop
指定,如果需要对数据进行特殊处理可以使用插槽的形式,与之前 vue2 项目中我大多使用的 element ui 的写法一脉相承。
数据表格
Naive UI 则提供了数据表格组件 <n-data-table>
来显示数据。当我第一次阅读其演示代码时,却发现其写法与 Element Plus 大相径庭,虽然表格数据同样是通过 data
属性接收,但需要展示的列的显示则是直接通过另一个属性 columns
来定义:
<!-- 代码片段二 -->
<template>
<n-data-table
:columns="columns"
:data="data"
/>
</template>
columns
的值需要是一个类型为 Array<DataTableColumn>
的数组,以我项目中的代码为例,大概是下面这样的:
// 代码片段三
import { h } from 'vue'
import { NTag } from 'naive-ui'
import type { DataTableColumn } from 'naive-ui'
const columns: DataTableColumn<any>[] = [
{
title: '快递单号',
key: 'trackingNumber'
},
{
title: '运费',
key: 'isFeePaid',
render(row) {
return h(
NTag,
{
type: row.isFeePaid ? 'success' : 'error',
bordered: false
},
{
default: () => {
return row.isFeePaid ? '已支付' : '未支付'
}
}
)
}
},
// ...
]
在需要对某列的数据做特殊处理时,使用到了渲染函数 render
,其返回值为 h()
。这种不是在 <template>
内来编写 html,而是在 ts 中使用渲染函数来生成表格的写法,就需要我们对渲染函数和 h 函数有一定的了解,下面就做个简单的介绍。
渲染函数
h() 函数
我们编写的 <template>
中的 html,会经过编译生成渲染函数,然后执行生成对应的 VNode。在选项式 API 中,vue 其实也提供了渲染函数 render()
,在 render()
内,我们可以通过 vue 提供的另一个函数 h()
,来生成需要返回的 VNode,也就是 Naive UI 文档 中 taTableColumn Properties 里指明的返回值 VNodeChild
:
h 是 hyperscript —— 一个基于 js 编写模板的工具 ——的简称,其实更准确的名称应该是 createVnode()
,只不过用 h 更简便。
注意,如果是以选项式 API 的这种方式使用 render 函数,在 .vue 文件中就不要再有 <template>
的代码了,不然看不到渲染结果。
使用
h()
函数接收 3 个参数,除第一个外都为可选:
第一个参数是 tag,可以是一个 html 标签名或一个组件(可以是异步组件或函数式组件);
第二个参数是 props,可以是 attribute 或 property 或者事件对应的对象。如果没有可以不传,但容易引起歧义,所以如果没有 props 可以传 null
;
第三个参数是 children,可以是使用 h()
构建的子 VNode,或者是文本字符串,或者是有插槽的对象。
比如我们将上面的代码片段三里使用到渲染函数的部分为例:
render(row) {
return h(
NTag,
{
type: row.isFeePaid ? 'success' : 'error',
bordered: false
},
{
default: () => {
return row.isFeePaid ? '已支付' : '未支付'
}
}
)
}
第一个参数传的就是另一个组件 NTag
(标签);
第二个参数里面都是 NTag
对应的属性;
而第三个参数则是使用到了插槽,default
代表是传递给子组件默认插槽的,值为一个函数,其接收从子组件传递过来的数据作为参数(虽然上例中没有用到,但这也可以帮助理解作用域插槽功能的实现)。插槽中的内容则是使用三元运算符做了个判断。
渲染效果