封装一个高复用的面包屑组件,适用于多级场景

在封装面包屑组件发现,简单封装的面包屑组件的时候如果最后一个选项后不需要箭头,还需要单独进行样式更改,并且如果需要在中间的某一个选项需要进行特殊样式,则更加复杂,这种只适用于两级场景,多级场景下就不能使用。

 

 

因此选择封装一个高复用的面包屑组件,能适用于多级场景,用到了 render 选项和 h 函数。

参考element的面包屑组件,不需要对不同要求再进行专门设计,这正是我需要的。


 

通过bread和bread-item两个组件进行演示:(以下基于vue3.0,所有组件都定义为全局组件)

1.bread-item.vue

<template>
  <div class="bread-item">
    <RouterLink v-if="to" :to="to"><slot /></RouterLink>
    <span v-else><slot /></span>
  </div>
</template>

<script>
export default {
  name: 'BreadItem',
  props: {
    to: {
      type: [String, Object],
      default: ''
    }
  }
}
</script>

<style></style>

此处并不需要加入箭头,如果在此处加上则最后会多出一个不需要的箭头,将箭头在render中动态渲染就好了

2.bread.vue

<script>
import { h } from 'vue'
export default {
  name: 'Bread',
  render() {
    const items = this.$slots.default()
    const dymanicItems = []
    items.forEach((item, i) => {
      dymanicItems.push(item)
      if (i < items.length - 1) {
        dymanicItems.push(h('i', { class: 'arrow' }))
      }
    })
    return h('div', { class: 'bread' }, dymanicItems)
  }
}
</script>

<style lang="less">
.bread {
  display: flex;
  padding: 25px 10px;
  &-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>

此处样式没有scoped,是为了里面的bread-item插槽也能使用 

index.vue

<template>
  <div class="top-category">
    <div class="container">
      <!-- 面包屑 -->
      <Bread>
        <BreadItem :to="{ path: '/' }">首页</BreadItem>
        <BreadItem to="/category">页面1</BreadItem>
        <BreadItem>页面2</BreadItem>
      </Bread>
    </div>
  </div>
</template>

<script>
export default {
  name: 'TopCategory'
}
</script>

<style></style>

最后在使用的时候就和element上的面包屑组件一样,需要跳转就加一个to


 

分析一下bread.vue中的代码

<script>
import { h } from 'vue'
export default {
  name: 'Bread',
  render() {
    const items = this.$slots.default()
    const dymanicItems = []
    items.forEach((item, i) => {
      dymanicItems.push(item)
      if (i < items.length - 1) {
        dymanicItems.push(h('i', { class: 'arrow' }))
      }
    })
    return h('div', { class: 'bread' }, dymanicItems)
  }
}
</script>

这里用到了creatElement函数和render选项

在Vue中指定组件显示的内容:new Vue({选项}),有三种方式

  • el 选项,通过一个选择器找到容器,容器内容就是组件内容
  • template 选项,<div>组件内容</div> 作为组件内容
  • render选项,它是一个函数,函数回默认传入createElement函数(h函数),这个函数用来创建结构,再render函数返回渲染为组件内容。它的优先级更高。

vue官方对render选项的解释是:字符串模板的代替方案,允许你发挥 JavaScript 最大的编程能力。该渲染函数接收一个 createElement 方法作为第一个参数用来创建 VNode。Vue 选项中的 render 函数若存在,则 Vue 构造函数不会从 template 选项或通过 el 选项指定的挂载元素中提取出的 HTML 模板编译渲染函数。

render函数的返回值就是html结构

在大多数情况下,我们可以使用模板创建HTML,但一些特殊情况下使用模板较为繁琐,可以使用渲染函数来解决

h函数有三个参数 1.节点名称 2.属性|数据 (对象) 3. 子节点,例如:h('div', { class: 'bread' }, "例子"),生成的就是 <div class="bread">例子</div>

在上述代码中,items是通过this.$slots.default()获取的所有插槽组成的数组,dymanicItems就是动态渲染的箭头图标

通过遍历items得到每一个插槽将其与箭头图标依次组合,并通过if判断去掉最后一个箭头,就完成了 首页>页面1>页面2 这样的结构

更多内容和例子可以参考vue官方文档

posted @ 2021-10-23 20:31  mmsmd  阅读(226)  评论(0编辑  收藏  举报