這是我參與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
此文章已收錄到專欄中 👇,能夠直接關注。