元件化是長期開發過程中一個提煉精華的過程,目的主要是以下幾點:
- 提高複用性
- 解耦
- 提升未來的開發效率
那麼如何達到這樣的效果呢,我們可以分幾步來循序漸進地完成。
下文主要是思路,想直接獲取程式碼可轉戰ElementUI的Github去看原始碼
一 元件的定義
元件從大類上可以分為兩種:
- 基礎元件:例如ElementUI
- 業務元件:通過基礎元件或者業務元件組合而成,與業務強相關甚至強繫結
二 元件的顆粒度
基礎元件的顆粒度爭議並不大,就是Button,Table等等 業務元件的顆粒度最大可以為一個feature,一個feature就是一個可以獨立上線特性,例如文章的評論點贊功能。我們可以想象有一個開關,開啟就有這個feature,關閉就沒有,且不會造成聯動的影響。
三 元件的介面
- Vue的元件基本都是通過屬性來進行配置,進而控制元件的功能變化。因此開發元件之前我們就得明確定義變數是什麼,明確元件需要開放的介面
- 我們需要理解每一個元件的核心功能是什麼,可通過單一職責這樣的設計模式來考慮。元件的核心功能是不能發生變化的,這也是為了介面的向後相容
- 對於業務元件,我們可否直接把一些常年不發生變化的資料和UI繫結在一起,從而刪減部分介面。大家不必擔心耦合性,解耦的前提是有被解耦的需求。
四 開發元件
1 UI規範
UI規範是元件開發的物理依據,你得知道要做成什麼樣子,你才能做。UI規範要對整套元件的各個視覺元素(長,寬,padding,margin,圓角,顏色,字型,字號,邊框,圖示,陰影)有語意明確的定義和友好的標註,這樣前端工程師們才能做到有法可依。
2 開發
舉例:
1.sass或less來寫樣式
2.重置樣式檔案和樣式的公共變數檔案(將視覺元素翻譯成程式碼中的常量形成的檔案),這是根據自己的UI規範來決定的
3.ES6的哪些特性不使用,用哪一個stage的babel來翻譯
4.需要引入的第三方包有哪些,這個也決定了最後打包出來元件js的大小
5.工程的目錄結構
複製程式碼
3 打包
對於Vue,我們通常使用的是webpack。具體配置這裡不詳細講解,可以參考入門Webpack,看這篇就夠了,還有一個快速方法就是通過Vue-cli生成的模板工程來進行更改。 接著我們要定義清楚我們的打包策略:
舉例:
1.所有的樣式打到一個檔案
2.有fonts則單獨打出來
3.元件JS和VueJS不打在一起
4.非通用的第三方包需和元件打在一起
複製程式碼
五 最佳實踐
以上四點是正式編寫元件程式碼的前置工作。現在我們通過elementUI的原始碼來看一個最佳實踐,我們的例子是比較簡單的麵包屑,先看一下怎麼去使用的這個元件:
然後我們來看一下程式碼如何實現的<template>
<span class="el-breadcrumb__item">
<span class="el-breadcrumb__inner" ref="link" role="link">
<slot></slot>
</span>
<i v-if="separatorClass" class="el-breadcrumb__separator" :class="separatorClass"></i>
<span v-else class="el-breadcrumb__separator" role="presentation">{{separator}}</span>
</span>
</template>
<script>
export default {
name: 'ElBreadcrumbItem',
props: {
to: {},
replace: Boolean
},
data() {
return {
separator: '',
separatorClass: ''
};
},
inject: ['elBreadcrumb'],
mounted() {
this.separator = this.elBreadcrumb.separator;
this.separatorClass = this.elBreadcrumb.separatorClass;
let self = this;
if (this.to) {
let link = this.$refs.link;
link.setAttribute('role', 'link');
link.addEventListener('click', _ => {
let to = this.to;
self.replace ? self.$router.replace(to)
: self.$router.push(to);
});
}
}
};
</script>
複製程式碼
props中就是ElBreadcrumbItem暴露出來的兩個介面,也就是上文第三點提到的內容,介面的值是從父元件傳過來的。我們再看一下ElBreadcrumb,也就是父元件的程式碼實現。
這裡簡單解釋一下inject:inject和provide是成對出現的,是vue@2.2.0的新特性。通過此種方法變可以直接呼叫提供provide的元件中的屬性了,總結就是依賴注入(DI)。
<template>
<div class="el-breadcrumb" aria-label="Breadcrumb" role="navigation">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'ElBreadcrumb',
props: {
separator: {
type: String,
default: '/'
},
separatorClass: {
type: String,
default: ''
}
},
provide() {
return {
elBreadcrumb: this
};
},
mounted() {
const items = this.$el.querySelectorAll('.el-breadcrumb__item');
if (items.length) {
items[items.length - 1].setAttribute('aria-current', 'page');
}
}
};
</script>
複製程式碼
slot其實是專門留給ElBreadcrumbItem的插槽,實際上ElBreadcrumb不涉及什麼UI,它也通過props暴露出來了兩個介面,這個兩個的值是使用者傳入的。我們可以看到,預設的分隔符是'/',如果你在ElBreadcrumbItem中通過屬性傳入的分隔符是'+',那麵包屑每一級中的分隔符也會是'+',注意一下程式碼中的provide和上面的inject相對應。最後就是讓元件可註冊
import ElBreadcrumb from './src/breadcrumb';
/* istanbul ignore next */
ElBreadcrumb.install = function(Vue) {
Vue.component(ElBreadcrumb.name, ElBreadcrumb);
};
export default ElBreadcrumb;
複製程式碼
通過給元件新增install方法,讓元件可被Vue.use方法在全域性註冊。可參考Vue官方文件API
總結
本文簡單梳理了一下元件開發的思路,重點在於元件開發的這些前置條件:
- 定義元件
- 劃分顆粒度
- 理清元件的核心介面
- 如何定義打包策略和UI規範
完成這幾點,從程式碼層面只是一小部分工作,但若把整個元件作為一個產品來看,就已經完成了一半了。