【vue3】封装UI库,透传 slot —— 低代码的基础操作

988 阅读2分钟

封装一个UI库,加点自己的东东,是不是就可以变成自己的UI库呢?
好吧这不是重点,重点是封装之后呢,遇到的一个问题就是,原来UI库自带的插槽怎么透露出来?总不能一个一个定义吧。其实不难,只需要一个 v-for 就可以搞定。

普通插槽

以 el-input 为例来看看如何透传。

封装一下 el-input

做一个组件 my-input.vue

<template>
  <el-input v-model="model" placeholder="">
    <!--遍历 $slots 获取插槽名称 -->
    <template
      v-for="(item, key) in $slots"
      v-slot:[key]
    >
      <!--通过 slot 获得父组件传入的插槽内容-->
      <slot :name=key> </slot>
    </template>
  </el-input>
</template>

不需要写js代码,仅仅在 template 里面 v-for 一下即可。

使用方式

先 import 组件,然后按照 el-input 的插槽设置即可:

  import myInput from './slot-son.vue'
  const person = reactive({
    name: 'jyk'
  })

按照官网的例子,设置一个 suffix 插槽:

  <myInput v-model="person.name">
    <template #suffix>
      <el-icon class="el-input__icon"><calendar /></el-icon>
    </template>
  </myInput>

当然还可以设置其他插槽,就像使用 el-input 那样。

  <myInput v-model="person.name">
    <template #suffix>
      <el-icon class="el-input__icon"><calendar /></el-icon>
    </template>
    <template #prefix>
      <el-icon class="el-input__icon"><search /></el-icon>
    </template>
    <template #prepend>Http://</template>
    <template #append>
      <el-select v-model="person.name" placeholder="Select" style="width: 115px">
        <el-option label="Restaurant" value="1" />
        <el-option label="Order No." value="2" />
        <el-option label="Tel" value="3" />
      </el-select>
    </template>
  </myInput>

input的插槽.png

作用域插槽

这个有点绕圈圈,其实只需要加了一个属性绑定即可。

封装一下 el-table

input 没有作用域插槽,所以这次我们使用 el-table 来举例:

  <el-table style="width: 100%">
    <el-table-column v-for="(key) in labels" :label="key" width="180">
      <template v-if="$slots[key]"  #default="scope">
        <slot :name="key" v-bind="scope"></slot>
      </template>
    </el-table-column>
  </el-table>

table-column 只有一个匿名插槽,也就是默认插槽,对于 column 来说这就可以了,但是封装后我们只想暴露table,所以还需要定义具名插槽,就以字段名作为插槽名称。

所以我们定义一个数组来存放字段名称,这里简化设置。

  type TProps = {labels: string[]}
  const props = defineProps<TProps>()
  • 我们用 v-for 的方式遍历出来列表的列,也就是说,我们可以定义一个JSON,然后由JSON创建一个列表。
  • v-if 判断应该加载哪个插槽,然后按照要求设置一个匿名插槽。
  • slot 里面使用 v-bindscope 传递出去。

这样就完成了一个作用域插槽的透传。

使用方式

我们先定义一个字段数组,以及模拟数据:

  import mytable from './slot-table.vue'

  interface User {
    date: string
    name: string
    address: string
  }

  const labels = [
    '日期',
    'name',
    'address',
    'Operations'
  ]

  const tableData: User[] = [
    {
      date: '2016-05-03',
      name: 'Tom1',
      address: 'No. 1, Grove St, Los Angeles',
    },
    ...
  ]

然后我们先设置一个 日期 的插槽:

  <mytable :data="tableData" :labels="labels">
    <template #日期="scope">
      <div style="display: flex; align-items: center">
        <el-icon><timer /></el-icon>
        <span style="margin-left: 10px">{{ scope.row.date }}</span>
      </div>
    </template>
  </mytable>
  • data 属性:我们并没有定义 data 属性,而是通过 $attrs 透传 给 el-table 的。
  • 字段名作为插槽名称,传给组件,组件内部通过 v-if 判断,在对应的 column 里面加载插槽。

我们还可以设置其他插槽:(代码来自于官网实例)

  <mytable :data="tableData" :labels="labels">
    <template #日期="scope">
      <div style="display: flex; align-items: center">
        <el-icon><timer /></el-icon>
        <span style="margin-left: 10px">{{ scope.row.date }}</span>
      </div>
    </template>
    <!---->
    <template #name="scope">
      <el-popover effect="light" trigger="hover" placement="top" width="auto">
        <template #default>
          <div>name: {{ scope.row.name }}</div>
          <div>address: {{ scope.row.address }}</div>
        </template>
        <template #reference>
          <el-tag>{{ scope.row.name }}</el-tag>
        </template>
      </el-popover>
    </template>
    <template #address="scope">
      {{ scope.row.address }}
    </template>

    <!--操作按钮列-->
    <template #Operations="scope">
      <el-button size="small" @click="handleEdit(scope.$index, scope.row)">
        Edit
      </el-button>
      <el-button
        size="small"
        type="danger"
        @click="handleDelete(scope.$index, scope.row)"
      >
        Delete
      </el-button>
    </template>  
  </mytable>

小结

这样做有何意义?这是低代码的一种实现方式,借用成熟的UI库,实现基础功能,然后用JSON绑定普通的字段,最后用插槽搞定特殊的字段。

这样可以实现既方便,又灵活的效果。

和原本 el-table 有何区别?这区别可就多了。

首先这个列是由 v-for 遍历出来的,也就是说,如果想要改变列的前后顺序,那么我们不需要修改 template,只需要修改数组里的元素的先后顺序即可。

还有其他各种好处,先不说了,做过低代码的都懂。