本篇文章记录仿写一个 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>