封装自己的组件库 -- 第一篇

542 阅读1分钟

「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战

前言

大家在日常的开发中, 会用到很多的通用组件, 比如面包屑导航、按钮、表格、表单等等。并且好用的组件库,不在少数。但是你否想过,这些组件库中的组件是如何封装的呢?笔者最近在使用 vue3.2+ 开发项目,并在项目中封装了很多的通用组件

接下来就让我们入手去开发自己的第一个组件,第二个组件......

面包屑组件

 最初的形态
<div class="bread">
   <div class="bread-item">
     <router-link to="/"> 首页 </router-link>
   </div>
   <i class="iconfont icon-angle-right"></i>
   <div class="bread-item">
     <router-link> 居家 </router-link>
   </div>
   <i class="iconfont icon-angle-right"></i>
   <div class="bread-item">
     <router-link > 茶咖酒具 </router-link>
   </div>
</div>

 最终的形态
<y-breadcrumb>
        <y-breadcrumb-item to="/"> 首页 </y-breadcrumb-item>
        <y-breadcrumb-item> 居家 </y-breadcrumb-item>
        <y-breadcrumb-item> 茶咖酒具 </y-breadcrumb-item>
</y-breadcrumbVue>

因为面包屑组件涉及到了组件之间的嵌套,单纯的用 template 是很难去实现它的完整功能的, 所以这里我们用到了 render函数 和 h函数, h函数可以生成一个 VNode, render 函数可以去渲染 VNode。

y-breadcrumb-item 组件

<template>
  <div class="bread-item">
    <!-- 有 to 属性时 -->
    <template v-if="to">
      <router-link :to="to"><slot /></router-link>
    </template>

    <!-- 无 to 属性时 -->
    <template v-else>
      <span> <slot /></span>
    </template>
  </div>
</template>

<script setup>
import { defineProps } from 'vue'

defineProps({
  to: {
    type: String,
    default: null
  }
})
</script>

<style lang="scss" scoped>
.bread {
  display: flex;
  padding: 25px 10px;
  ::v-deep &-item {
    a {
      color: #666;
      transition: all 0.4s;
      &:hover {
        color: $xtxColor;
      }
    }
  }
  i {
    font-size: 12px;
    margin-left: 5px;
    margin-right: 5px;
    line-height: 22px;
  }
}
</style>

y-breadcrumb 组件

整体的思路是:拿到插槽的内容,进行循环遍历,遍历的过程中插入小箭头标签(最后边不要),得到新的 VNode 数组,render这个数组

<script>
import { useSlots, h } from 'vue'
export default {
  render() {
    const slots = useSlots()
    
    // 匿名插槽的使用
    const defaultSlots = slots.default()
    const renderArr = [] // 最终需要渲染的 VNode 集合

    defaultSlots.forEach((item, index) => {
      renderArr.push(item)
      if (index < defaultSlots.length - 1) {
        renderArr.push(
          h('i', { class: 'iconfont icon-angle-right' })
        )
      }
    })

    return h('div', { class: 'bread' }, renderArr)
  }
}
</script>

<style lang="scss" scoped>
// 面包屑
.bread {
  display: flex;
  padding: 25px 10px;
  ::v-deep &-item {
    a {
      color: #666;
      transition: all 0.4s;
      &:hover {
        color: $xtxColor;
      }
    }
  }
  i {
    font-size: 12px;
    margin-left: 5px;
    margin-right: 5px;
    line-height: 22px;
  }
}
// 面包屑样式结束
</style>

image.png