0x00 简介
组件Tag
多用于标记和分类。 本文将深入分析组件源码,剖析其实现原理,耐心读完,相信会对您有所帮助。packages/tag/src/tag.vue
文件是组件源码实现。 🔗 组件文档 Tag 🔗 github 源码 tag.vue
更多组件剖析详见 👉 📚 Element 2 源码剖析组件总览 。
0x01 组件源码
使用 渲染函数 创建组件,代码结构如下 👇。
<script>
export default {
name: 'ElTag',
// 组件 prop
props: {
// ...
},
methods: {
// 关闭 Tag 时触发的事件
handleClose(event) {
// ...
},
// 点击 Tag 时触发的事件
handleClick(event) {
// ...
}
},
// 计算属性
computed: {
// 标签尺寸
tagSize() {
// ...
}
},
// 渲染 标签 虚拟DOM
render(h) {
// ...
}
};
</script>
attributes 属性
组件定义了 8 个 prop
。
props: {
text: String, // 代码中没有被使用
closable: Boolean, // 是否可关闭
type: String, // 类型
hit: Boolean, // 是否有边框描边
disableTransitions: Boolean, // 是否禁用渐变动画
color: String, // 背景色
size: String, // 尺寸
// 主题
effect: {
type: String,
default: 'light',
validator(val) {
return ['dark', 'light', 'plain'].indexOf(val) !== -1;
}
}
},
prop
详细描述如下(只有 7 个):
prop 中 text
虽然定义了,但是在代码中没有被使用。 type
、size
、effect
的值若没有匹配指定的字符串,根据值生成无效的 class(样式规则未定义)。
计算属性
tagSize
根据size
、$ELEMENT
动态计算标签的尺寸。
组件定义了 prop size
, 则tagSize
值为 size
定义值。若 size
值为 undefined
、''
, tagSize
值为由全局配置 $ELEMENT
决定。
若 $ELEMENT
对象不为空且包含 size
属性, tagSize
值为对象的属性值 $ELEMENT.size
;否则 tagSize
值为 undefined
,
// 标签尺寸
tagSize() {
return this.size || (this.$ELEMENT || {}).size;
}
前文可知,组件入口文件中 install
方法中定义了对象 $ELEMENT
全局配置。其中组件的默认尺寸 size
值为''
。
// src/index.js 组件入口文件
const install = function (Vue, opts = {}) {
// ...
// 全局配置 组件的默认尺寸size 弹框的初始z-index
Vue.prototype.$ELEMENT = {
size: opts.size || "",
zIndex: opts.zIndex || 2000,
};
// ...
};
完整引入组件时,使用 vue.use()
注册组件,会执行 install
方法,声明了全局属性 $ELEMENT
。
import Element from "element-ui";
Vue.use(Element);
// 手动设定 组件默认size
Vue.use(Element, {
size: "small",
});
按需引入时, 需要手动配置 Vue.prototype.$ELEMENT = { size: 'small', zIndex: 3000 };
,若不配置 $ELEMENT
未定义值为 undefined
,若此调用 $ELEMENT.size
则会抛出异常Uncaught ReferenceError: $ELEMENT is not defined
。
import { Tag } from "element-ui";
// 全局配置
Vue.prototype.$ELEMENT = { size: "small", zIndex: 3000 };
// 引入组件
Vue.use(Tag);
表达式 (this.$ELEMENT || {})
防止$ELEMENT
未定义赋予空对象防止异常调用。
events 事件
点击 Tag 时触发 click
事件。
// 点击 Tag 时触发的事件
handleClick(event) {
// 触发当前实例上的事件
this.$emit('click', event);
}
关闭 Tag 时触发 close
事件。
// 关闭 Tag 时触发的事件
handleClose(event) {
// 阻止捕获和冒泡阶段中当前事件的进一步传播。
event.stopPropagation();
// 触发当前实例上的事件
this.$emit('close', event);
},
render() 渲染函数
组件将创建一个 <span>
元素 VNode,动态添加 class,使用内联样式设置背景色,定义了组件点击事件。 <span>
元素包含 2 个子节点
名为el-icon-close
的Icon
图标,定义了关闭 Tag 时 click 事件。当 closable
值为true
时才会渲染。
当disableTransitions
值为true
时,使用内置组件 transition
包裹 <span>
元素实现缩放zoom-in-center
效果。
// 渲染 标签 虚拟DOM
render(h) {
const { type, tagSize, hit, effect } = this;
// 动态添加class
const classes = [
'el-tag',
type ? `el-tag--${type}` : '',
tagSize ? `el-tag--${tagSize}` : '',
effect ? `el-tag--${effect}` : '',
hit && 'is-hit'
];
// tag 元素
const tagEl = (
<span
class={ classes }
style={{ backgroundColor: this.color }}
on-click={ this.handleClick }>
{ this.$slots.default }
{
this.closable && <i class="el-tag__close el-icon-close" on-click={ this.handleClose }></i>
}
</span>
);
// 组件VNode
return this.disableTransitions ? tagEl : <transition name="el-zoom-in-center">{ tagEl }</transition>;
}
根据组件 prop 动态添加 class
。
type ? 'el-tag--${type}' : ''
设置组件不同类型颜色。若 type
值不是以下success/info/warning/danger
其中一个,设置无效(生成无效的 class)。
tagSize ? 'el-tag--${tagSize}' : ''
设置组件不同尺寸。若 tagSize
值不是以下medium/small/mini
其中一个,设置无效(生成无效的 class)。
effect ? 'el-tag--${effect}' : ''
设置组件不同主题。若 effect
值不是以下dark/light/plain
其中一个,设置无效(生成无效的 class)。 effect
默认值为 light
,对应的 el-tag--light
是无效的样式,未定义。
color
属性定义组件内联样式 backgroundColor
,权重较高会覆盖其他样式的颜色。
0x02 组件样式
src/tag.scss
组件样式源码 packages\theme-chalk\src\tag.scss
使用混合指令 b
、 m
、genTheme
嵌套生成组件样式。
genTheme() 组件主题
混合指令 genTheme
用于生成组件的主题样式。
@mixin genTheme(
$backgroundColorWeight,
$borderColorWeight,
$fontColorWeight,
$hoverColorWeight
) {
background-color: mix(
$--tag-primary-color,
$--color-white,
$backgroundColorWeight
);
// ...
// 生成 &.is-hit
@include when(hit) {
// ...
}
.el-tag__close {
// ...
&:hover {
// ...
}
}
// info / success / warning / danger 结构相同
&.el-tag--info {
// ...
// 生成 &.is-hit
@include when(hit) {
// ...
}
.el-tag__close {
// ...
&:hover {
// ...
}
}
}
// success
// warning
// danger
}
Mix
函数是将两种颜色根据一定的比例混合在一起,生成另一种颜色。 前两个参数是想混合的颜色(可以使用颜色变量、十六进制、RGBA、RGB、HSL 或者 HSLA 颜色值),第三个参数是第一种颜色的比例值。
mix($color1, $color2, $weight: 50%) //=> color
组件主题默认样式直接在 el-tag
下生成 。
// 生成 .el-tag
@include b(tag) {
// 主题规则 默认light
@include genTheme(10%, 20%, 100%, 100%);
// ...
// 生成 .el-tag--dark
@include m(dark) {
// 主题规则
@include genTheme(100%, 100%, 0, 80%);
}
// 生成 .el-tag--plain
@include m(plain) {
// 主题规则
@include genTheme(0, 40%, 100%, 100%);
}
}
结合上述规则生成的主题样式如下
// el-tag 可以替换为 el-tag--dark el-tag--plain
.el-tag {
// ...
}
.el-tag.is-hit {
// ...
}
.el-tag .el-tag__close {
// ...
}
.el-tag .el-tag__close:hover {
// ...
}
// info / success / warning / danger 结构相同
.el-tag.el-tag--info {
// ...
}
.el-tag.el-tag--info.is-hit {
// ...
}
.el-tag.el-tag--info .el-tag__close {
// ...
}
.el-tag.el-tag--info .el-tag__close:hover {
// ...
}
// success ...
// warning ...
// danger ...
组件样式
组件样式逻辑如下。
// 生成 .el-tag
@include b(tag) {
// 默认light样式
// ...
// 生成 .el-tag .el-icon-close
.el-icon-close {
// ...
// 生成 .el-tag .el-icon-close::before
&::before {
// ...
}
}
// dark样式 ...
// plaink样式 ...
// 生成 .el-tag--medium
@include m(medium) {
// ...
// 生成 .el-tag--medium .el-icon-close
.el-icon-close {
// ...
}
}
// 生成 .el-tag--small
@include m(small) {
// ...
// 生成 .el-tag--small .el-icon-close
.el-icon-close {
// ...
}
}
// 生成 .el-tag--mini
@include m(mini) {
// ...
// 生成 .el-tag--mini .el-icon-close
.el-icon-close {
// ...
}
}
}
lib/tag.scss
前文可知使用 gulpfile.js
编译 scss
文件转换为CSS
,经过浏览器兼容、格式压缩,最后生成 packages\theme-chalk\lib\tag.scss
,内容格式如下。
.el-tag {
// ...
}
.el-tag .el-tag__close {
// ...
}
.el-tag .el-tag__close:hover {
// ...
}
/* theme start -- el-tag / el-tag--dark / el-tag--plain
-------------------------- */
.el-tag.is-hit {
// ...
}
.el-tag .el-icon-close {
// ...
}
.el-tag .el-icon-close::before {
// ...
}
/* type -- info / success / warning / danger
-------------------------- */
.el-tag.el-tag--info {
// ...
}
.el-tag.el-tag--info.is-hit {
// ...
}
.el-tag.el-tag--info .el-tag__close {
// ...
}
.el-tag.el-tag--info .el-tag__close:hover {
// ...
}
// success ...
// warning ...
// danger ...
/* el-tag--dark
-------------------------- */
// ...
/* el-tag--plain
-------------------------- */
// ...
/* theme end -- el-tag / el-tag--dark / el-tag--plain
-------------------------- */
/* size -- medium / small / mini
-------------------------- */
.el-tag--medium {
// ...
}
.el-tag--medium .el-icon-close {
// ...
}
0x03 📚 参考
“stopPropagation”,MDN
“mix funciton color”,sass