做者:Horace
介紹:個人 Element-ui 源碼學習,持續不定時更新~
ps:新人小白一枚,若有錯誤,歡迎指出~css
筆者如今是一個大三快要實習的學生,最近在學習 Vue 的時候感受本身學了不少的東西可是沒有什麼實質性的進步,感受達到了一個瓶頸,但自認爲尚未達到能夠深挖 Vue 源碼的功力,因此打算開始剖析 Element-ui 的源碼,但願能夠藉此深刻 Vue,後面我也會開始 Vue 的源碼學習。vue
由於筆者剛開始 Element-ui 的源碼學習不是好久,本文會從 <el-alert>
這個組件開始,而且會以一個小白的角度儘量地把每個細節都摳出來,因此在大佬看來可能會以爲很簡單,文章可能不適合大佬食用~git
筆者的 Element-ui 源碼的解析不會涉及它的 css 樣式,這裏我在項目中下載了 Element-ui,只引入了它的樣式,其他的部分由本身來寫,而後將組件引入到本身的頁面中。程序員
我會更加的專一於其中應用到的 Vue 語法、代碼風格以及其中的小技巧,以達到更好的增強本身的 Vue 和 JS 功底,爲未來解構 Vue 的源碼作一個緩衝~github
<el-alert>
在開始以前看看效果web
組件設計的精髓中複用性是不可不提的,Element-ui 的 <el-alert>
組件能夠說是複用性能很好,可以適用於大部分的消息提示場景。接下來咱們就來解構它的源碼,經過源碼來增強對 Vue 的一些學習。element-ui
組件都是有一個通用的結構,經過 Vue 的動態綁定和 slot 等等語法達到千人千面,實現組件複用的效果,咱們先來解析它的模板結構數組
<template>
<div>
<!-- ElAlert -->
<transition name="el-alert-fade">
<div
class="el-alert"
:class="[ typeClass, center ? 'is-center' : '', 'is-' + effect ]"
v-show="visible"
>
<i class="el-alert__icon" :class="[ iconClass, isBigIcon ]" v-if="showIcon"></i>
<div class="el-alert__content">
<span class="el-alert__title" :class="[ isBoldTitle ]" v-if="title || $slots.title">
<!-- {{title}} -->
<!-- 屬性和具名插槽兩種方式顯示 -->
<slot name="title">{{title}}</slot>
</span>
<!-- 經過 slot:default 顯示 description -->
<p class="el-alert__description" v-if="$slots.default && !description"><slot></slot></p>
<!-- 經過屬性顯示 description -->
<p class="el-alert__description" v-if="!$slots.default && description">{{ description }}</p>
<!-- <p class="el-alert__description" v-if="description || $slots.default">{{ description }}</p> -->
<!-- 關閉按鈕 -->
<i class="el-alert__closebtn" :class="{ 'is-customed' : closeText!== '', 'el-icon-close': closeText === '' }" v-show="closable" @click="close()">{{ closeText }}</i>
</div>
</div>
</transition>
</div>
</template>
複製代碼
在代碼之中的一些可能會有點複雜的地方我也寫上了詳細的註釋,下面咱們一塊兒來看看可以從這裏能學到些什麼東西。app
首先第一個,拋開全部的語法,咱們能夠學到的就是一個代碼的書寫習慣和風格,要適當的用空格和回車,可使代碼更加的清晰,對本身和別人都更友好。編輯器
其次就是類名的命名規範,儘量的清晰明瞭,Element-ui 的源碼中 css 方面使用了 BEM 的命名規範---Block(塊)、Element(元素)、Modifier(修飾符)這樣有助於咱們更好的去理解每一個類名的做用。
這裏用到了比較多的動態類名,平時咱們可能寫動態類名更多的是直接使用:class=""
這樣的一個形式,可是這裏有幾個地方可能對於新手來講有點疑惑的地方,好比:
:class="[ typeClass, center ? 'is-center' : '', 'is-' + effect ]"
:class="[ isBoldTitle ]" v-if="title || $slots.title"
這兩個地方的動態類名使用了 [] 和 {},緣由是由於要綁定多個動態類名,而且可能要對類名進行必定的修改和根據命名規範格式化,使用 [] 和 {} 能夠一次性的作完這些事情,更加的方便。這在咱們平時寫 demo 之類的情境下可能用的會稍微少一點,因此我也拎出來說一下。
在 Element-ui 中動態類名還有一種巧妙的用法,後面我會講到。
Element-ui 之因此這麼受歡迎,在看完源碼以後我以爲除了它很好用以外,還有一點就是它對於用戶比較友好,給用戶提供了多種選擇。咱們從源碼這兩個地方來看看:
<span class="el-alert__title" :class="[ isBoldTitle ]" v-if="title || $slots.title">
<!-- 屬性和具名插槽兩種方式顯示 -->
<slot name="title">{{title}}</slot>
</span>
複製代碼
<!-- 經過 slot:default 顯示 description -->
<p class="el-alert__description" v-if="$slots.default && !description"><slot></slot></p>
<!-- 經過屬性顯示 description -->
<p class="el-alert__description" v-if="!$slots.default && description">{{ description }}</p>
複製代碼
這裏我在代碼中也給出了詳細註釋,title 和 description 經過 v-if 的判斷,能夠實現屬性傳值和插槽填充兩種方式顯示到頁面上,這對於使用它的人來講比較便利。
可能有人看到了我在 desctiption 的顯示後面有一段註釋的代碼:
<!-- <p class="el-alert__description" v-if="description || $slots.default">{{ description }}</p> -->
複製代碼
這段代碼是我在看到 title 的顯示方式控制只用一個 v-if 判斷就作到了,因此想本身嘗試也只用一個 v-if 來實現效果,惋惜的是這條語句並不能達到想要的效果,緣由是 description 的插槽是默認插槽,經過 $slots.default 並不能判斷到它,可是若是你換成下面這樣的格式就能夠實現了:
<p class="el-alert__description" v-if="description || $slots.description"><slot name="description">{{ description }}</slot></p>
複製代碼
這樣雖然能夠實現效果,可是你增長了一個具名插槽,Element-ui 官方不這麼作確定是有本身相應的考慮吧。
看完告終構,下面來看數據和方法部分的設計。老規矩,先上代碼看看:
<script>
// 常量
const TYPE_CLASS_MAP = {
'success': 'el-icon-success',
'warning': 'el-icon-warning',
'error': 'el-icon-error'
}
export default {
name: 'ElAlert',
props: {
type: { // 類型
type: String,
default: 'info' // 默認值
},
title: { // 標題
type: String,
default: ''
},
description: {
type: String,
default: ''
},
center: Boolean,
effect: { // 提供的主題
type: String,
default: 'light',
validator: function(value) { // 驗證 effect 的值必須二選一
// 其中之一
return ['light', 'dark'].indexOf(value) !== -1;
}
},
showIcon: Boolean,
closeText: {
type: String,
default: ''
},
closable: {
type: Boolean,
default: true
}
},
data() {
return {
visible: true
}
},
computed: {
// 計算屬性
typeClass() {
return `el-alert--${ this.type }`
},
iconClass() {
return TYPE_CLASS_MAP[this.type] || 'el-icon-info'
},
isBigIcon() {
return this.desciption || this.$slots.default ? 'is-big' : ''
},
isBoldTitle() {
return this.description || this.$slots.default ? 'is-bold' : ''
}
},
methods: {
close() {
this.visible = false;
// 給父組件傳遞一個 close 方法,便於在用戶關閉提示的時候進行一些操做
this.$emit('close')
}
}
}
</script> 複製代碼
先來補上以前留下的動態類名的坑,在這裏有四個類名是經過 computed 屬性來動態肯定的:
typeClass
、iconClass
、isBigIcon
、isBoldTitle
computed 屬性的使用場景大體是下面這樣:
使用 computed 來計算類名的好處是能夠不用在 data 中添加相應的數據,而且能夠根據實際狀況來改變類名的形式,進行動態改變或是根據規範格式化。固然這裏不能直接放在 :class 中,由於判斷比較複雜,並且 :class 也沒法徹底知足這裏的需求。
在這裏的數據有一個比較巧妙地設計,那就是常量。
// 常量
const TYPE_CLASS_MAP = {
'success': 'el-icon-success',
'warning': 'el-icon-warning',
'error': 'el-icon-error'
}
複製代碼
由於這裏 icon 地類名是固定不變的,它是一個可遍歷的同一類型的數據,而且要在多個地方使用,在使用過程當中也不會去改變它,因此能夠考慮做爲常量來使用。這裏和大文件上傳過程當中將文件上傳狀態做爲常量對象來使用是一樣的道理,能夠增強代碼的可維護性,作到一改全改。
咱們在業務要求不是很高的狀況下父子組件用 props 通訊傳值,通常是寫成數組的格式,而這裏使用了對象的形式,在這裏使用對象形式能夠實現這些方面的功能:
使用數組的那種方式只能是知足一半的業務場景接收數據,比較的粗線條,並不能對數據進行太多的校驗。
methods: {
close() {
this.visible = false;
// 給父組件傳遞一個 close 方法,便於在用戶關閉提示的時候進行一些操做
this.$emit('close')
}
}
複製代碼
在這裏的數據源中還設置了一個 visible 屬性,方便用戶能夠關閉這個提示框組件。這裏方法的設計考慮的也是很全面,在點擊關閉的時候使用 $emit 給父組件提交一個 close 事件,父組件就能夠經過 @close 進行綁定一個事件,實現用戶在關閉提示的時候進行一些業務操做。
在本身寫 Element-ui 組件的時候若是是和我同樣在 App.vue 中使用必定要注意把 #app 的樣式去掉,否則會有樣式衝突致使樣式顯示不正確。
正所謂不看源碼的程序員不是好程序員,若是感受本身達到了一個瓶頸階段,看源碼是一個很好的選擇。本文可能對於不少人來講可能比較的基礎,技術含量也不是很高,可是我會不斷的深刻 Element-ui 的源碼學習,把其中更多的知識點挖掘出來,以便也能使本身有所提高。這個系列我也會持續的更新下去,若是你以爲對你有幫助的話,歡迎點贊和在評論區和我交流~
我把個人學習記錄都記錄在了個人 github 而且會持續的更新下去,有興趣的小夥伴能夠看看~
github