「这是我参与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>