This article records and imitates the details of an el-bread component, which will help you better understand the specific working details of the corresponding component of Ele.me ui. This article is another article in the elementui source code learning and imitation series. I will continue to update and imitate other components when I am free. The source code is on github, you can pull it down, npm start to run, and comments are helpful for better understanding. The github repository address is as follows:
https://github.com/shuirongshuifu/elementSrcCodeStudy

what are breadcrumbs

Intuitively, breadcrumbs are actually equivalent to a shortcut for navigation and jumping. So why is it called that? From Grimm's Fairy Tales:

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

From Baidu Encyclopedia: https://baike.baidu.com/item/%E9%9D%A2%E5%8C%85%E5%B1%91%E5%AF%BC%E8%88%AA/358283

Component requirements analysis

About bread Breadcrumb component, mainly used to display the hierarchical position of the current page, 告知用户在哪里 , and can click on the breadcrumbs to make routing jumps (return), let's analyze the breadcrumbs The requirements of the components are roughly as follows:

  • Breadcrumb separated content requirements

    • The default content is separated, for example, the breadcrumb navigation of the Ele.me UI is separated by a slash / by default.
    • If we think the default delimited slash / is not good-looking, we can also pass the delimited content by ourselves, for example, delimited by >>
  • Jump function requirements

    • For example, push jump, that is: this.$route.push(...)
    • Or replace jump, ie: this.$route.replace(...)

In terms of finishing, these two requirements are quite simple, but before we look at the code encapsulated below, we need to review the knowledge used in the components: provide和inject

Review of knowledge points of provide and inject

In a nutshell: ancestor components provide data, and descendant components (including child components) inject receive data

Regarding provide and inject we can understand this analogy:

  • The parent component uses 冒号:绑定传递 , and the child component uses props接收数据
  • And ancestors pass to descendants, ancestors use provide绑定传递 (provided), descendants use inject接收数据 (injection)

It's a bit unintuitive to only talk about words, so let's take a look at the following case to understand

Small case

This case is divided into three components, namely the one component, the two component, and the three component. The one component is the parent component of the two component, and the two component is the parent component of the three component. That is, the relationship is: one, two, three three components constitute the ancestor components and descendant components of the relationship between father, father and son

There are two fields of name and age data in the one component, which need to be provided to the two components and the three components for use

Graphical analysis of case code

Case renderings

After understanding this small case, it will be better to look at the code below to clarify the idea

Why mention provide and inject ?因为在封装面包屑组件的时候,官方是分为两个组件, el-breadcrumb el-breadcrumb-itemel-breadcrumb 090b0d9d64f9404c5c15e9e9d543d8a0---组件中使用到了provide default delimited content 斜杠/ and delimited content 图标class类名 for descendant components el-breadcrumb-item use.

Next, let's take a look at the component code of the imitation package

package code

Package renderings

code using components

Similar to the official breadcrumb component code, here we also use two breadcrumb component codes, which are: my-bread component and my-bread-item component (ancestor descendant relationship)

 <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 component code

 <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 component code

 <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>

Summarize

In my humble opinion provide and inject the main use scenario is the component encapsulation, which seems to be less used in business code


水冗水孚
1.1k 声望585 粉丝

每一个不曾起舞的日子,都是对生命的辜负