关于element-ui:elementui源码学习之仿写一个elbread

58次阅读

共计 4608 个字符,预计需要花费 12 分钟才能阅读完成。

本篇文章记录仿写一个 el-bread 组件细节,从而有助于大家更好了解饿了么 ui 对应组件具体工作细节。本文是 elementui 源码学习仿写系列的又一篇文章,后续闲暇了会不断更新并仿写其余组件。源码在 github 上,大家能够拉下来,npm start 运行跑起来,联合正文有助于更好的了解。github 仓库地址如下:
https://github.com/shuirongsh…

什么是面包屑

直观来说,面包屑其实就相当于一个导航跳转的快捷操作形式。那么为什么这么叫呢?源自格林童话:

面包屑导航 (BreadcrumbNavigation) 这个概念来自童话故事“汉赛尔和格莱特”,当汉赛尔和格莱特穿过森林时,不小心迷路了,然而他们发现沿途走过的中央都撒下了面包屑,让这些面包屑来帮忙他们找到回家的路。所以,面包屑导航的作用是通知访问者他们在网站中的地位以及如何返回。

源自百度百科:https://baike.baidu.com/item/…

组件需要剖析

对于 bread 面包屑组件,次要是用于展现以后页面所在的层级地位,告知用户在哪里,且可能点击面包屑做路由跳转(返回),咱们剖析一下面包屑组件的需要,大抵有以下:

  • 面包屑分隔内容需要

    • 默认分隔内容,比方饿了么 UI 的面包屑导航默认就是以 斜杠 / 分隔的
    • 如果咱们感觉默认分隔斜杠 / 不难看,也可本人传递分隔内容,比方以 >> 分隔
  • 跳转性能需要

    • 比方 push 跳转,即:this.$route.push(...)
    • 或者 replace 跳转,即:this.$route.replace(...)

整顿来说,这两个需要都是挺简略的,不过咱们再看下方封装的代码之前须要温习一下组件中用到的常识:provide 和 inject

provide、inject 的知识点温习

一言以蔽之:先人组件 provide 提供数据,后辈组件(蕴含子组件)inject 接收数据

对于 provideinject咱们能够这样的类比了解:

  • 父传子,是父组件应用 冒号: 绑定传递,子组件应用props 接收数据
  • 而先人传后辈,先人应用provide 绑定传递(提供),后辈应用inject 接收数据(注入)

只说文字,有点不太直观,所以咱们看一下上面这个案例就了解了

小案例

此案例是分为三个组件,别离是 one 组件、two 组件、three 组价,one 组件是 two 组件的父组件、two 组件是 three 组件的父组件。即关系为:one、two、three 三个组件形成了爷、父、子这样关系的先人组件和后辈组件

one 组件中有 name 和 age 两个字段的数据,须要提供到 two 组件和 three 组件中应用

案例代码图示剖析

案例效果图

了解了这个小案例,再看下方的代码会更好清晰思路

为甚要提到 provideinject呢?因为在封装面包屑组件的时候,官网是分为两个组件,el-breadcrumbel-breadcrumb-item;在el-breadcrumb 组件中应用到了 provide 提供默认分隔内容 斜杠 /和分隔内容的 图标 class 类名 给后辈组件 el-breadcrumb-item 应用。

接下来咱们看一下仿写封装的组件代码

封装代码

封装的效果图

应用组件的代码

相似官网的面包屑组件代码,这里咱们也用两个面包屑组件代码,为:my-bread组件和 my-bread-item 组件(先人后辈关系)

<template>
  <div>
    <my-divider content-position="left"> 默认颜文字分隔 </my-divider>
    <my-bread>
      <my-bread-item> 外层 </my-bread-item>
      <my-bread-item> 中层 </my-bread-item>
      <my-bread-item> 内层 </my-bread-item>
    </my-bread>
    <my-divider content-position="left"> 自定义分隔内容 </my-divider>
    <my-bread customDivide=">">
      <my-bread-item> 外层 </my-bread-item>
      <my-bread-item> 中层 </my-bread-item>
      <my-bread-item> 内层 </my-bread-item>
    </my-bread>
    <my-divider content-position="left"> 应用饿了么 UI 的图标做分隔 </my-divider>
    <my-bread elementIconClassDivide="el-icon-wind-power">
      <my-bread-item> 外层 </my-bread-item>
      <my-bread-item> 中层 </my-bread-item>
      <my-bread-item> 内层 </my-bread-item>
    </my-bread>
    <my-divider content-position="left"> 可跳转 </my-divider>
    <my-bread elementIconClassDivide="el-icon-location-outline">
      <my-bread-item :to="{path:'/myTag'}">myTag 跳转 </my-bread-item>
      <my-bread-item replace :to="{path:'/myBadge'}">myBadge 跳转(replace)</my-bread-item>
      <my-bread-item> 当下 </my-bread-item>
    </my-bread>
  </div>
</template>

my-bread 组件代码

<template>
  <div class="breadWrap">
    <slot></slot>
  </div>
</template>

<script>
export default {
  name: "myBread",
  props: {
    // 应用饿了么 UI 的图标 icon 类名进行分隔
    elementIconClassDivide: {
      type: String,
      default: "",
    },
    // 自定义分隔内容,用户填写什么,就以什么为分隔
    customDivide: {
      type: String,
      default: "→_→", // 如这里,默认以颜文字为默认分隔。饿了么是斜杠默认分隔 /
    },
  },
  /**
   * 父组件 provide 注入一个本身实例 this 给到子组件 myBreadItem,不便子组件拜访
   * 父组件的分隔内容变量 defaultDivide 或 customDivide 或 elementIconClassDivide
   * 因为子组件须要去渲染出对应的分隔内容是啥
   *
   * 其实这里不应用 provide 注入的形式也是能够的。子组件 myBreadItem 拜访父组件 myBread
   * 也能够应用 this.$parent.defaultDivide 这样的模式去拜访父组件的数据内容。不过
   * 这里只是做一个分隔内容的展现,不牵涉到响应式数据处理,所以应用 provide 更优雅
   * */
  provide() {
    return {fatherInstance: this, // 提供本身实例,不便子组件应用,子组件需 inject 申明注入接管应用};
  },
};
</script>

<style lang="less" scoped>
.breadWrap {
  font-size: 14px;
  // 第一个面包屑的文字加粗
  /deep/ .breadItem:first-child .breadItemWords {font-weight: 700;}
  // 最初一个面包屑的小图标暗藏
  /deep/ .breadItem:last-child .breadItemDivide {display: none;}
}
</style>

my-bread-item 组件代码

<template>
  <div class="breadItem">
    <!-- 面包屑文字局部(点击文字跳转)-->
    <span ref="link" :class="['breadItemWords', to ?'isLink':'']">
      <slot></slot>
    </span>
    <!-- 面包屑图标局部 -->
    <!-- 应用饿了么 UI 的图标做分隔 -->
    <i
      v-if="elementIconClassDivide"
      class="breadItemDivide"
      :class="elementIconClassDivide"
    ></i>
    <!-- 自定义分隔 二者只留一个即可 -->
    <span v-else class="breadItemDivide">{{customDivide}}</span>
  </div>
</template>

<script>
export default {
  name: "myBreadItem",
  inject: ["fatherInstance"], // 要申明承受当前能力应用,留神名字要和父组件 provide 的保持一致
  props: {
    // 跳转的 path
    to: {},
    // 默认不做 replace 跳转
    replace: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      elementIconClassDivide: "", // 应用饿了么图标做分隔的类名
      customDivide: "", // 自定义分隔内容
    };
  },
  mounted() {
    /**
     * 先祖组件 provide 提供数据,后辈组件 inject 注入承受数据(能够 inject 多个实例,故为数组)* 这里是以父子组件为例,子组件拜访父组件的值(也可思考应用 this.$parent.xxx 形式)*
     * 渲染分隔符
     * */
    // console.log(
    //   "父组件提供数据,子组件注入本身当前便可拜访",
    //   this.fatherInstance
    // );
    // 拜访并再存一份去应用,从而渲染分隔内容
    this.elementIconClassDivide = this.fatherInstance.elementIconClassDivide;
    this.customDivide = this.fatherInstance.customDivide;
    /**
     * 跳转性能
     * */
    // 获取组件实例
    const link = this.$refs.link;
    // 绑定监听点击,点击跳转
    link.addEventListener("click", (_) => {
      // 没有传递 to 就不做跳转
      if (!this.to) return;
      // 当 replace 为 true 的时候,才做 replace 跳转(默认还是 push 跳转)this.replace ? this.$router.replace(this.to) : this.$router.push(this.to);
    });
  },
};
</script>

<style lang="less" scoped>
.breadItem {
  display: inline-block;
  .breadItemWords {font-weight: 400;}
  .isLink {font-weight: 700;}
  .isLink:hover {color: #409eff; cursor: pointer;}
  .breadItemDivide {margin: 0 8px; color: #999;}
}
</style>

总结

集体愚见 provideinject次要应用的场景还是组件封装这一块,貌似在业务代码中应用的少

正文完
 0