This article records and imitates the details of an el-divider 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 warehouse address is as follows: https://github.com/shuirongshuifu/elementSrcCodeStudy

Component requirements analysis

Regarding the components of the dividing line, the commonly used scenarios are:

  • The horizontal division of the dividing line (eg: the division between paragraphs)
  • Horizontal dividing line plus text description (the text description position is generally located on the left, center, and right side of the dividing line)
  • Vertical division of division type (horizontal division between inline text and text)
  • Splitter style type (solid splitter or dashed splitter or dotted splitter)

    Because the vertical split distance is not enough, there is no text description for the vertical split

Knowledge points used in components

functional component

We know that a function is actually a processing pipeline. We enter the parameters, and after the function is processed, the result of the return processing is obtained.

For a component, it can also be regarded as a function (functional component), we pass parameters, the component props are received, and then the component renders the corresponding style effect according to the different parameters passed.

This method is actually similar to the original way we use components, but it is more streamlined . After all, only the received parameters are considered, and there is no life cycle, this instance and the like. Therefore , the rendering efficiency of functional components will be higher than that of ordinary components .

So in some cases, it is more efficient to use functional components

Two ways to define functional components

For example, to render text by passing parameters, there are two ways:

Method 1: render function definition

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

Method 2: template definition

 <!-- 模板标签上加上functional关键字表明是函数式组件 -->
<template functional>
  <div>
    <h3>{{ props.bookName }}</h3>
  </div>
</template>

<script>
  export default {
    props: {
      bookName: {
        type: String,
        default: "斗破苍穹",
      },
    },
  }
</script>
For the specific details of functional components, see the official documentation for details: https://cn.vuejs.org/v2/guide/render-function.html#%E5%87%BD%E6%95%B0%E5%BC% 8F%E7%BB%84%E4%BB%B6

Why mention functional components here? Because el-divider components are encapsulated with functional components (because the components are only for displaying dividing lines, so there is no need for this instance, life cycle, etc.)

Therefore: In order to improve rendering efficiency, functional components are used here

The situations in which functional components are used are roughly as follows:

  • Only display components, after all, there is no logical operation related
  • Operation of higher order components
  • You can also consider using
  • Overall, functional components are more flexible

Solve the problem that one pixel is too thick

Take a look at the problem diagram

Just scale the corresponding dom element a little bit:

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

Component packaging

el-divider The overall component is relatively simple, mainly the details of the parameter usage of the functional component

Rendering of component packaging

Component encapsulated code

code used

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

encapsulated code

 <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.95倍可以解决一像素问题(一像素看着比实际粗)横线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.95倍可以解决一像素问题(一像素看着比实际粗)竖线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>

水冗水孚
1.1k 声望585 粉丝

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