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

组件需要剖析

对于分割线的组件,个别应用的场景有:

  • 分割线之程度宰割(如:段落与段落之间分隔)
  • 程度分割线加上文字描述(文字描述地位个别是位于分割线的左侧、居中、右侧)
  • 宰割类型之垂直宰割(横向的行内文字与文字之间的宰割)
  • 分割线款式类型(solid分割线或dashed分割线或dotted分割线)

    因为垂直宰割间隔不够,所以垂直宰割没有文字描述

组件中用到的知识点

函数式组件

咱们晓得,一个函数,其实就是一个加工流水线,咱们入参,通过函数的加工当前,失去return加工后的后果。

对于组件而言,其实也能够当做一个函数(函数式组件),咱们传递参数,组件props接管,而后组件再依据传参的不同,渲染出对应的款式成果。

这种形式和原来的咱们应用组件的形式其实差不多,只不过更加精简,毕竟只思考接管参数,没有生命周期、this实例之类的。所以函数式组件的渲染效率会比一般组件高

所以有些状况下,应用函数式组件效率会更高

函数式组件的两种定义形式

比方,通过传参形式去渲染文字,能够有以下两种形式

形式一:render函数定义

<script>export default {  functional: true, // 肯定要指明是函数式组件  props: {    bookName: {      type: String,      default: "斗破天穹",    },  },  // context上下文,有点像this的意思。但不一样  render: function (createElement, context) {    // 创立一个h3标签,标签内容是上下文对象中的props中的bookName字段      return createElement("h3", {}, context.props.bookName);  },};</script>

形式二:template定义

<!-- 模板标签上加上functional关键字表明是函数式组件 --><template functional>  <div>    <h3>{{ props.bookName }}</h3>  </div></template><script>  export default {    props: {      bookName: {        type: String,        default: "斗破天穹",      },    },  }</script>
对于函数式组件的具体细节,详情见官网文档:https://cn.vuejs.org/v2/guide...

这里为什么要提到函数式组件呢?因为el-divider组件就是应用函数式组件进行封装的(因为组件只是做宰割线条的展现,所以不须要有this实例,生命周期之类的)

故:为了晋升渲染效率,这里应用函数式组件

应用函数式组件的状况大抵有以下:

  • 只做展现的组件,毕竟没啥逻辑操作相干
  • 高阶组件的操作
  • 循环中也能够思考应用
  • 整体来说,函数式组件还是比拟灵便的

解决一像素太粗的问题

先看一下问题图

只须要给对应dom元素略微缩放一下即可:

transform: scaleY(0.95);transform: scaleX(0.95);

组件封装

el-divider组件整体还是比较简单的,次要是函数式组件的参数应用细节

组件封装的效果图

组件封装的代码

应用的代码

<template>  <div class="wrap">    <h3>实线solid靠左</h3>    <my-divider class="staticClass_must_hava" content-position="left"      >早上好</my-divider    >    <h3>实线solid居中</h3>    <my-divider content-position="center">中午好</my-divider>    <h3>实线solid靠右</h3>    <my-divider content-position="right">下午好</my-divider>    <h3>虚线dashed靠左</h3>    <my-divider lineType="dashed" content-position="left">早上好</my-divider>    <h3>虚线dashed居中</h3>    <my-divider lineType="dashed" content-position="center">中午好</my-divider>    <h3>虚线dashed靠右</h3>    <my-divider lineType="dashed" content-position="right">下午好</my-divider>    <h3>圆点dotted靠左</h3>    <my-divider lineType="dotted" content-position="left">早上好</my-divider>    <h3>圆点dotted居中</h3>    <my-divider lineType="dotted" content-position="center">中午好</my-divider>    <h3>圆点dotted靠右</h3>    <my-divider lineType="dotted" content-position="right">下午好</my-divider>    <h3>竖向分割线</h3>    <span>早上好</span>    <my-divider direction="vertical"></my-divider>    <span>中午好</span>    <my-divider direction="vertical"></my-divider>    <span>下午好</span>  </div></template><script>import myDivider from "./myDivider.vue";export default {  components: { myDivider },};</script><style>.wrap { padding: 24px; }.staticClass_must_hava { border-bottom: 1px solid #baf !important; }</style>

封装的代码

<template functional>  <!-- functional关键字加上后,即代表为函数式组件,就没有生命周期、this实例等了,故渲染更快,性能更高 -->  <!-- v-bind和v-on这里不加也行。毕竟函数式组件集体愚见简洁倡议一些,attrs寄存传递来的数据,listeners传递任何事件监听器 -->  <!-- data.staticClass须要加上,否则外层给组件加的class属性在审查元素的时候不会呈现的,相当于没加   -->  <div    v-bind="data.attrs"    v-on="listeners"    :class="[data.staticClass, `${props.direction}`, `${props.lineType}`]"  >    <!-- 当分割线为程度的时候,且插槽有内容的时候,才渲染对应数据 -->    <!-- slots()办法返回插槽数据对象,default为默认插槽;相当于this.$slots.default。本系列文章中仿写一个el-tabs 也提到了此知识点  -->    <div      v-if="props.direction === 'horizontal' && slots().default"      :class="['text', `${props.contentPosition}`]"    >      <slot />    </div>  </div></template><script>export default {  name: "myDivider",  props: {    // 分割线的方向,默认程度horizontal(垂直vertical可选)    direction: {      type: String,      default: "horizontal",      validator(val) {        return ["horizontal", "vertical"].includes(val);      },    },    // 文本内容地位,默认居中(仅当分割线为程度方向时,才渲染对应内容)    contentPosition: {      type: String,      default: "center",      validator(val) {        return ["left", "center", "right"].includes(val);      },    },    // 分割线线条类型,默认实线,可选虚线,圆点线    lineType: {      type: String,      default: "solid",      validator(val) {        return ["solid", "dashed", "dotted"].includes(val);      },    },  },};</script><style lang="less" scoped>// 程度款式.horizontal {  width: 100%;  border-bottom: 1px solid #333; // 默认solid分割线款式  display: block;  margin: 12px 0;  position: relative;  // 缩放0.85倍能够解决一像素问题(一像素看着比理论粗)横线Y缩放  transform: scaleY(0.95);}// dashed分割线.dashed {  border-bottom: 1px dashed #333;}// dotted分割线.dotted {  border-bottom: 1px dotted #333;}// 垂直款式.vertical {  display: inline-block;  width: 1px;  height: 1em;  margin: 0 8px;  background-color: #333;  vertical-align: middle;  position: relative;  top: -1px; // 微调一下地位  // 缩放0.85倍能够解决一像素问题(一像素看着比理论粗)竖线X缩放  transform: scaleX(0.95);}// 文本款式.text {  position: absolute;  padding: 0 12px;  color: #333;  font-size: 14px;  transform: translateY(-50%); // y轴挪动本身的一半,使其垂直方向居中  background-color: #fff; // 通过背景色和padding去盖住程度宰割线条}// 文本靠左.text.left {  left: 36px;}// 文本靠右.text.right {  right: 36px;}// 文本居中.text.center {  left: 50%;  // 居中的话,别忘了要回正。即XY轴方向都平移本身的一半  transform: translateX(-50%) translateY(-50%);}</style>