本篇文章记录仿写一个el-bread组件细节,从而有助于大家更好了解饿了么ui对应组件具体工作细节。本文是elementui源码学习仿写系列的又一篇文章,后续闲暇了会不断更新并仿写其余组件。源码在github上,大家能够拉下来,npm start运行跑起来,联合正文有助于更好的了解。github仓库地址如下:
https://github.com/shuirongsh...
什么是面包屑
直观来说,面包屑其实就相当于一个导航跳转的快捷操作形式。那么为什么这么叫呢?源自格林童话:
面包屑导航(BreadcrumbNavigation)这个概念来自童话故事“汉赛尔和格莱特”,当汉赛尔和格莱特穿过森林时,不小心迷路了,然而他们发现沿途走过的中央都撒下了面包屑,让这些面包屑来帮忙他们找到回家的路。所以,面包屑导航的作用是通知访问者他们在网站中的地位以及如何返回。
源自百度百科:https://baike.baidu.com/item/...
组件需要剖析
对于bread
面包屑组件,次要是用于展现以后页面所在的层级地位,告知用户在哪里
,且可能点击面包屑做路由跳转(返回),咱们剖析一下面包屑组件的需要,大抵有以下:
面包屑分隔内容需要
- 默认分隔内容,比方饿了么UI的面包屑导航默认就是以 斜杠
/
分隔的 - 如果咱们感觉默认分隔斜杠
/
不难看,也可本人传递分隔内容,比方以>>
分隔
- 默认分隔内容,比方饿了么UI的面包屑导航默认就是以 斜杠
跳转性能需要
- 比方push跳转,即:
this.$route.push(...)
- 或者replace跳转,即:
this.$route.replace(...)
- 比方push跳转,即:
整顿来说,这两个需要都是挺简略的,不过咱们再看下方封装的代码之前须要温习一下组件中用到的常识:provide和inject
provide、inject的知识点温习
一言以蔽之:先人组件provide提供数据,后辈组件(蕴含子组件)inject接收数据
对于provide
和inject
咱们能够这样的类比了解:
- 父传子,是父组件应用
冒号:绑定传递
,子组件应用props接收数据
- 而先人传后辈,先人应用
provide绑定传递
(提供),后辈应用inject接收数据
(注入)
只说文字,有点不太直观,所以咱们看一下上面这个案例就了解了
小案例
此案例是分为三个组件,别离是one组件、two组件、three组价,one组件是two组件的父组件、two组件是three组件的父组件。即关系为:one、two、three三个组件形成了爷、父、子这样关系的先人组件和后辈组件
one组件中有name和age两个字段的数据,须要提供到two组件和three组件中应用
案例代码图示剖析
案例效果图
了解了这个小案例,再看下方的代码会更好清晰思路
为甚要提到provide
和inject
呢?因为在封装面包屑组件的时候,官网是分为两个组件,el-breadcrumb
和el-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>
总结
集体愚见provide
和inject
次要应用的场景还是组件封装这一块,貌似在业务代码中应用的少