Element 2 組件源碼剖析之Tag標籤

這是我參與8月更文挑戰的第9天,活動詳情查看:8月更文挑戰css

0x00 簡介

組件Tag 多用於標記和分類。 本文將深刻分析組件源碼,剖析其實現原理,耐心讀完,相信會對您有所幫助。packages/tag/src/tag.vue 文件是組件源碼實現。 🔗 組件文檔 Tag 🔗 github源碼 tag.vuevue

更多組件剖析詳見 👉 📚 Element 2 源碼剖析組件總覽git

0x01 組件源碼

使用 渲染函數 建立組件,代碼結構以下 👇。github

<script>
  export default {
    name: 'ElTag',
    // 組件 prop
    props: {
      // ...
    },
    methods: {
      // 關閉 Tag 時觸發的事件
      handleClose(event) {
        // ...
      },
      // 點擊 Tag 時觸發的事件
      handleClick(event) {
        // ...
      }
    },
    // 計算屬性
    computed: {
      // 標籤尺寸 
      tagSize() {
        // ...
      }
    },
    // 渲染 標籤 虛擬DOM
    render(h) {
      // ...
    }
  };
</script> 
複製代碼

attributes 屬性

組件定義了8個 propweb

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

proptext 雖然定義了,可是在代碼中沒有被使用。 typesizeeffect 的值若沒有匹配指定的字符串,根據值生成無效的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 未定義賦予空對象防止異常調用。

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個子節點

  1. 默認插槽;
  2. 名爲el-icon-closeIcon圖標,定義了關閉 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,權重較高會覆蓋其餘樣式的顏色。

0x02 組件樣式

src/tag.scss

組件樣式源碼 packages\theme-chalk\src\tag.scss 使用混合指令 bmgenTheme 嵌套生成組件樣式。

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

0x04 關注專欄

此文章已收錄到專欄中 👇,能夠直接關注。

相關文章
相關標籤/搜索