这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战css
组件Tag
多用于标记和分类。 本文将深刻分析组件源码,剖析其实现原理,耐心读完,相信会对您有所帮助。packages/tag/src/tag.vue
文件是组件源码实现。 🔗 组件文档 Tag 🔗 github源码 tag.vuevue
更多组件剖析详见 👉 📚 Element 2 源码剖析组件总览 。git
使用 渲染函数 建立组件,代码结构以下 👇。github
<script>
export default {
name: 'ElTag',
// 组件 prop
props: {
// ...
},
methods: {
// 关闭 Tag 时触发的事件
handleClose(event) {
// ...
},
// 点击 Tag 时触发的事件
handleClick(event) {
// ...
}
},
// 计算属性
computed: {
// 标签尺寸
tagSize() {
// ...
}
},
// 渲染 标签 虚拟DOM
render(h) {
// ...
}
};
</script>
复制代码
组件定义了8个 prop
。web
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个):element-ui
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
type | 类型 | string | success/info/warning/danger | — |
closable | 是否可关闭 | boolean | — | false |
disable-transitions | 是否禁用渐变更画 | boolean | — | false |
hit | 是否有边框描边 | boolean | — | false |
color | 背景色 | string | — | — |
size | 尺寸 | string | medium / small / mini | — |
effect | 主题 | string | dark / light / plain | light |
prop 中 text
虽然定义了,可是在代码中没有被使用。 type
、size
、effect
的值若没有匹配指定的字符串,根据值生成无效的class(样式规则未定义)。gulp
tagSize
根据size
、$ELEMENT
动态计算标签的尺寸。浏览器
组件定义了prop size
, 则tagSize
值为 size
定义值。若 size
值为 undefined
、''
, tagSize
值为由全局配置 $ELEMENT
决定。sass
若 $ELEMENT
对象不为空且包含 size
属性, tagSize
值为对象的属性值 $ELEMENT.size
;不然 tagSize
值为 undefined
,markdown
// 标签尺寸
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
未定义赋予空对象防止异常调用。
点击 Tag 时触发 click
事件。
// 点击 Tag 时触发的事件
handleClick(event) {
// 触发当前实例上的事件
this.$emit('click', event);
}
复制代码
关闭 Tag 时触发 close
事件。
// 关闭 Tag 时触发的事件
handleClose(event) {
// 阻止捕获和冒泡阶段中当前事件的进一步传播。
event.stopPropagation();
// 触发当前实例上的事件
this.$emit('close', event);
},
复制代码
组件将建立一个 <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
。
'el-tag'
组件默认样式。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
是无效的样式,未定义。hit && 'is-hit'
边框描边效果 。color
属性定义组件内联样式 backgroundColor
,权重较高会覆盖其余样式的颜色。
组件样式源码 packages\theme-chalk\src\tag.scss
使用混合指令 b
、 m
、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 {
// ...
}
}
}
复制代码
前文可知使用 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 {
// ...
}
复制代码
“stopPropagation”,MDN
“mix funciton color”,sass
此文章已收录到专栏中 👇,能够直接关注。