Vue2.0的通用組件


 

 

Element:一套通用組件庫的開發之路

Element 是由餓了么UED設計、餓了么大前端開發的一套基於 Vue 2.0 的桌面端組件庫。今天我們要分享的就是開發 Element 的一些心得。

官網:http://element.eleme.io/#/
github:https://github.com/ElemeFE/element

 

## 設計目的

大部分項目起源都是源於業務方的需求,Element 也是一樣。隨着公司業務發展,內部開始衍生出很多后台系統,UED 部門也接到越來越多的設計需求,分析整個過程,我們發現如下問題:

- 日漸增多的后台產品設計需求
- 設計資源有限,沒辦法支持所有業務線
- 公司內部諸多后台產品使用體驗不一致

於是我們決定:

- 設計一套后台支撐框架,提升后台系統的可用性和一致性
- 套用此框架,即使沒有設計師參與,也能讓產品或開發設計出一套好用的后台系統

 

## 設計階段

下面簡單說一下設計 Element 經歷的幾個階段。

**了解業務並熟悉公司內各后台產品,尋找業務上的共性問題**
設計的目的是為了業務服務。第一步我們從內部系統開始入手,了解公司內部在使用的各種后台系統,將其組件抽象剝離,尋找共性特征。

**專注業務組件設計**
總結了公司不同系統不同組件的使用情況后,我們打算從業務組件入手,因為這部份是由公司特殊需求衍生的解決方案,我們認為解決了這些棘手的問題,也能給其他后台產品帶來好的設計引導。

**尋求開發支持**
到這一步,我們開始尋找公司內部的開發團隊,並在這時才得知不同團隊里使用着不同的前端框架,有 Vue、React、Angular 等等。

**與大前端合作**
大前端作為獨立的前端團隊,有能力開發底層的工具去服務不同業務,並且 Vue 也是一套年輕且發展方向很好的一個技術棧。UED 與大前端的合作一拍即合。

**方向轉變,專注於基礎組件**
跟大前端接觸后,才發現最開始的方向並不正確,因為業務變化過快,即使有通用的業務組件,也很難跟上需求的變化,而基礎組件才是所有開發團隊都需要的通用組件。這時候我們開始把方向調整為基礎組件的設計。

**組件交互完成,進行視覺封裝,並搭建主體網站**
前期的設計工作主要是由交互設計師進行設計,等確認完所有組件的功能和交互后,開始進行視覺階段,這中間包括制定顏色、字體等各類規范,也同時進行主體網站的設計。

輸出 UI Kit 文件,統一設計規范

第一版網站設計,此處的「特殊組件」即業務組件。

**網站二次設計**
第一版網站上線后視覺效果並不好,我們內部進行了調整,再次上線后就是大家現在看到的樣子。

設計過程簡單來說就經歷了這幾個階段,如還有問題可以繼續交流,下面進入開發階段。

 

## 開發目的

- 后台系統缺乏一套完整的基礎組件庫
- Vue 在公司內部是一個比較年輕的技術棧,希望做一些基礎設施的建設
- 提升公司在技術社區的影響力

 

## 開發流程

進入開發階段后,在總體架構方面我們做了一些嘗試,下面按照時間順序分享給大家:
**如何與設計師進行配合**
經過項目初期開發和設計的磨合,我們提煉了一套組件開發流程:

1. 根據交互稿和視覺稿進行開發,期間與設計師保持溝通
2. 開發完成后自測,之后提交設計師驗收
3. 設計師提出修改意見,根據意見進行修改
4. 完成組件開發,為網站編寫例子和文檔

**如何管理多組件項目**
在開發之初,我們就在思考如何降低組件的耦合度,確保組件可以獨立工作。這樣的目的是可以保證組件可以依賴其他組件、讓用戶只加載其中幾個組件甚至在安裝時只安裝需要的組件。最先想到的做法是一個組件單獨一個倉庫,而組件庫項目就是把組件作為依賴引入。

但是由於人手不足,這樣的機制導致開發太耗時間,每個組件都需要單獨維護和打包,同時還要維護組件庫項目的各依賴的版本號。我們只能另尋方案。后來參考了 [babel](https://github.com/babel/babel) 項目的管理方式:所有子項目放在 `packages/` 目錄里,一個子項目可以當作一個獨立的倉庫。通過 [lerna](https://github.com/lerna/lerna) 來管理子項目的依賴和發布。

結合自身項目的特點以及 babel 的這套機制,我們重構了目錄結構:組件可單獨作為一個項目放在 `packages/`,共用函數放在 `src/` 里。最后的打包結果會將整個組件打包成一個文件、組件分別打包成獨立文件,同時發布時還將發布組件庫和獨立組件,滿足不同用戶的使用需求。

**如何解決自定義主題**
開發一套組件庫就離不開定制主題的需求。類名要足夠友好,盡量避免存在樣式層級嵌套,這樣在直接覆蓋樣式或者單獨寫一套主題都會方便許多。所以我們采用 BEM 的方式管理類名,同時盡可能將屬性值用變量代替,維護一份變量文件便於直接修改變量就能定制一套主題。

考慮到不同用戶的使用習慣,我們沒有選用 Less 或 Sass 之類的有各自風格的預處理器,而是選用了更接近未來標准的 CSS4 風格的語法,用 PostCSS 和整合了 postcss-bem 和 postcss-cssnext 等插件的 [postcss-salad](https://github.com/ElemeFE/postcss-salad) 開發。

為了降低用戶自定義主題的上手成本,我們還提供了命令行工具指導用戶快速自定義一套主題。

**如何提供一份直觀的文檔**
文檔不僅是讓用戶看起來直觀,也要讓編寫者寫起來直觀。所以最簡單的方式是用 Markdown 寫文檔。但是就會產生另一個問題:如何在文檔里寫可運行的示例?常規的做法是把文檔寫在 Vue 文件里,這樣就可以在里面調用其他組件,但是這樣就違背了寫「直觀」文檔的初衷。

經過幾番嘗試,結合 Vue 的特點。我們寫了一套處理 Markdown 文件的 webpack loader,可以將 Markdown 轉成 Vue 文件,不僅降低了文檔的維護成本,同時也將文檔里運行組件示例變成可能。

**多語言官網如何配置和管理**
Element 在立項之初其實並沒有考慮國際化的問題。項目開源之后,我們陸續收到了一些外國開發者的反饋,希望能夠增加英文文檔。不久之后,國內的一個翻譯團隊主動聯系到了我們,為 Element 貢獻了整套英文文檔。

有了英文文檔就需要有英文網站,這就需要對官網的現有結構進行修改和升級;同時為了面向未來,需要官網能夠兼容除英語外的其他多語言。為此我們做了以下工作:

1. 路由

官網的路由是根據一個記錄了導航信息的 `json` 文件自動生成的。因此需要在這個 `json` 文件中添加對應於其他語言的字段,並且根據新的數據結構修改路由生成的邏輯。

2. 頁面

官網中除了文檔外,還有一些介紹性質的頁面。這些頁面中文字比較多,如果人工管理每種語言的頁面,若需要修改則必須去每個頁面相應的位置進行編輯,有些繁瑣。我們的做法是:每個頁面對應一個模板,模板中的文字全部抽取到一個語言配置文件中,並且寫了一個腳本生成最終的頁面。這樣在需要修改時,只需在語言配置文件中編輯對應的字段即可。

3. 網站組件

對於 `header` 、`footer` 等通用的頁面組件,我們采取了和上面類似的策略。但由於組件內的文字較少,於是沒有再使用模板,而是通過路由判斷應該顯示何種語言。

中英文網站的顯示效果



至此,我們也逐漸完善了技術棧。用 ES2015 和 CSS4 作開發語言、Lerna 負責管理組件、用 Karma 搭配 Mocha 和 Chai 等工具在 Travis CI 里做持續集成測試,最后用 Markdown 結合 Vue 寫文檔。我們甚至還在 CI 里實現了自動部署網站和推送主題倉庫代碼等功能,提升了不少開發效率。

**開發過程中遇到的問題**

具體到組件層面,在開發的過程中不可避免地會遇到一些問題。下面是我們的一些應對策略,希望能夠拋磚引玉,引發大家的思考和討論。

- 如何應對父子組件間事件派發機制的改變

在 Vue 2.0 中,用於父子組件間事件通信的 `$dispatch` 和 `$broadcast` 被移除了。官方的考慮是,基於組件樹結構的事件流方式讓人難以理解,並且在組件結構擴展的過程中會變得越來越脆弱。但是類似 Element 這樣的組件庫有幾個特點:首先,父子組件間互相通信的場景非常常見,比如在一個帶有驗證功能的表單里,每個表單項在 `change` 或 `blur` 時需要通知表單組件進行校驗;其次,組件的結構相對來說比較固定。

出於以上考慮,我們實現了簡化版的 `dispatch` 和 `broadcast` ,並把它們包裝成了一個 `mixin` ,方便在需要時調用。其中的 `dispatch` 代碼如下:


dispatch(componentName, eventName, params) {
var parent = this.$parent || this.$root;
var name = parent.$options.componentName;
while (parent && (!name || name !== componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.componentName;
}
}
if (parent) {
parent.$emit.apply(parent, [eventName].concat(params));
}
}

可以看出,我們的實現需要在調用時傳入 `componentName` (在各個組件中定義),這樣就確保了事件只會在正確的組件中觸發。

- 是否需要為用戶代理自定義組件上的原生事件

在 Vue 2.0 中的自定義組件上使用 `v-on` 只會監聽自定義事件(即組件用 `$emit` 觸發的事件)。如果要監聽原生事件,必須使用 `.native` 修飾符:


<mt-component @click.native="handleClick"></my-component>

這樣一來,很多不太熟悉 Vue 2.0 語法的用戶會發現給 Element 的組件綁定原生事件總是不生效。事實上,我們從開源以來收到的 issue 里被問得最多的一個問題是:如何給 `Button` 組件綁定 `click` 事件?

事實上我們只需要添加一行代碼就能解決問題,但是關於是否需要讓用戶可以直接監聽原生事件這件事在我們內部有兩種不同的觀點:一邊認為應該遵循 Vue 的設計思想,原生事件要加 native;另一邊認為 button 最常用的就是 click 事件,幫助用戶做這件事可以降低學習成本。后來我們專門咨詢了尤雨溪本人,他的觀點是,對於一些組件的常用事件,可以允許用戶直接監聽原生事件,同時在文檔中說明哪些事件可以直接監聽,哪些事件需要加 `.native` 修飾符。最后我們決定從易用性的角度出發,讓用戶在使用 `Button` 組件時可以監聽原生 `click` 事件,因為對於桌面端來說,`Button` 在絕大部分場景下都是需要監聽點擊事件的。 現在的 `Button` 支持以下兩種寫法:


<el-button @click.native="handleClick">Click Me!</el-button>
<el-button @click="handleClick">Click Me!</el-button>

- 版本迭代的過程中,若 API 發生變化,如何友好地提示用戶

在歷次迭代中,我們會盡量保持 API 的一致。但是在一些萬不得已的情況下,需要對 API 作出一些更新。對於老版本的用戶而言,如果使用了被移除的 API,升級到新版后會出現一些意料之外的報錯信息。為了友好地幫助用戶盡快找到報錯的來源,我們編寫了一個 `mixin` ,當組件的 API 發生變化時,在組件中引入這個 `mixin` 並列出變化前后的字段名即可。

`mixin` 的核心代碼為:


const { props, events } = this.getMigratingConfig();
const { data, componentOptions } = this.$vnode;
const definedProps = data.attrs || {};
const definedEvents = componentOptions.listeners || {};
for (let propName in definedProps) {
if (definedProps.hasOwnProperty(propName) && props[propName]) {
console.warn(`[Element Migrating][Attribute]: ${props[propName]}`);
}
}
for (let eventName in definedEvents) {
if (definedEvents.hasOwnProperty(eventName) && events[eventName]) {
console.warn(`[Element Migrating][Event]: ${events[eventName]}`);
}
}

引用了這個 `mixin` 的組件需要在 `methods` 中添加一個名為 `getMigratingConfig` 的方法,返回一個包含發生變化的 API 字段名和對應提示信息的對象:


getMigratingConfig() {
return {
props: {
'selection-mode': 'Table: selection-mode has been removed.'
},
events: {
cellclick: 'Table: cellclick has been renamed to cell-click.'
}
};
}

 

## issue 處理方式

我們選擇使用 Tower 來配合 GitHub 進行 issue 的追蹤和處理。首先在 Tower 上建立幾個清單:Plan、Design、Develop 和 Release。隨后具體的操作流程如下:

- 從各渠道收集反饋
- 若不需設計,則由開發回復
- 若需設計跟進,則在 GitHub 上添加標簽 `design` ,並在 Tower 的 Plan 清單中添加相應任務
- 開始處理任務后,為 GitHub 的對應 issue 添加 `working in progress` 標簽,同時把任務拖進 Tower 的 Design 清單
- 設計完成后,開發接手,同時把任務拖進 Develop 清單
- 開發完成,經過設計師驗收通過后將改動推送至 GitHub 倉庫,關閉相應 issue,最后將任務拖進 Release 清單

 

## 總結

Element 從立項至今已經走過了五個月的時間。總的來說,這段時間就是一個不斷發現問題和解決問題的過程,也是每個參與者自身成長的過程。開發時 Vue 2.0 正處於 RC 階段,我們隨着它的版本迭代踩到了不少坑,同時也給 Vue 提了一些 issue,並且都得到了 Vue 團隊的處理。在此向Vue 團隊的專業精神表示感謝。
自從 9 月開源以來,在社區的幫助下,Element 逐漸成熟,我們也在今天發布了它的第一個正式版本。希望越來越多的人能夠參與進來,和我們一起把 Element 做得更好。

 

## 參考資料

- [https://github.com/babel/babel](https://github.com/babel/babel)
- [https://github.com/lerna/lerna](https://github.com/lerna/lerna)
- [https://github.com/ElemeFE/postcss-salad](https://github.com/ElemeFE/postcss-salad)
- [https://github.com/QingWei-Li/vue-markdown-loader](https://github.com/QingWei-Li/vue-markdown-loader)
- https://github.com/ElemeFE/cooking

 

分享會提問&賓回答問題

問題1:官網是多少?
問題1:官網是element.eleme.io

問題2:如何修改樣式?
回答2:簡單的樣式可以通過覆蓋來修改;對於大規模的自定義,我們提供了一套自定義主題的工具,文檔看這里:https://github.com/ElemeFE/element/blob/master/custom-theme.md 。簡單來說,通過修改樣式變量、編譯主題、引入主題,就可以實現自定義主題。

問題3:Vue的作者給出了一套學習Vue路徑,那Element是否有閱讀源碼的路徑呢,怎么樣才可以較為方便的理解源碼,並且在基礎組件不能滿足自己業務的時候寫出自己的組件呢?
回答3:閱讀源碼的話,可以先 clone 項目后,先試試用 npm run new 指令創建一個新組件,看看我們的一個組件包含了哪些東西。要理解源碼的話就自己邊改代碼邊測試效果吧。

問題4:怎么看待vue添加redux,而又保留雙向綁定的數據方式?
問題 4:不太明白想問什么。

問題5:國際化網站怎么做的,是每種語言對應一個頁面嗎,還是統一的一個頁面
回答5:Element 的主頁是一個 SPA,每種語言對應了一個 .vue 文件,而這些 .vue 文件是通過一個統一的模板和語言配置文件生成出來的。

問題6:分享圖中有一個文件夾 Loacel,是否是 locale 的筆誤?
回答6:抱歉,我們的失誤,圖中確實應該是 locale。

問題7:請問會推出專門針對移動端的Vue2組件庫嗎?
回答7:回答7:目前沒有將 Element 移植到移動端的計划。不過,我們已經有一套移動端組件庫了:https://github.com/ElemeFE/mint-ui ,它有兩個版本,分別兼容 Vue 1.x 和 2.0。

問題8:寫在 vue 文件中的 markdown 輸出在哪里?這並不是現實效果的一部分呀?
回答8:Vue 和 Markdown 的結合,我們是自己做了一個 vue-markdown-loader, 作用是將 Markdown 文件轉成 Vue 組件(生成的文件在插件的 .cache 目錄里),最后通過 vue-loader 處理。可以去看看這個 loader 的源碼就明白了。

問題9:既然是基於vue了,那么element還有繼續的必要嗎?而且目前框架那么多,vue也得到了人的認可。element以后得生態如何保證?畢竟還只有目前餓了么一個團隊在用。
回答9: 不太清楚“element還有繼續的必要”是什么意思,據我們在gitter對用戶的了解,現在已經有不少用戶將 element 實用到她們公司的產品開發里。element 的生態發展除了我們團隊本身以外還需要依靠開源的力量來進行優化和發展。

問題10:在組件開發中,有對復用性很高的業務組件做過積累嗎?如果做過~是怎么維護這些業務組件的?也是同邏輯組件的維護方法一樣嗎?
回答10: 組件開發中會不斷收到各種的功能需求的反饋,通過github倉庫issue來推動我們組件的功能更新和維護。不太清楚“邏輯組件”的含義,element 里除了按鈕這樣特殊的組件外都是帶有邏輯的。

問題11:請問將常見的jq插件或者說jq動效寫成vue組件的過程中有什么不順暢的地方嗎?
回答11: 在組件開發過程中沒有參考任何jq插件的動效。實際上基於mvvm框架下的組件庫開發相對於jq是輕松很多的,因為你不需要手動地處理事件綁定和視圖的更新。

問題12:分享的文檔是否有些陳舊沒有更新,vue 目前已經不是 rc 階段了
回答12:文檔中說「開發時」Vue 還處於 RC 階段,主要指的是今年的八九月份,那時 Vue 每更新一個 RC 版本,我們就跟着做一遍測試,然后發現幾個 bug 的情景還歷歷在目。Vue 2.0 是上個月正式發布的,我們也在今天發布了 Element 的正式版。

問題13:有沒有模板可以參考?
回答13:有,看這里:https://github.com/ElementUI/element-starter。如果熟悉 cooking 或 laravel,我們也提供了相應的模板:https://github.com/ElementUI/element-cooking-starter 、 https://github.com/ElementUI/element-in-laravel-starter 。

問題14:element的開發者都是餓了么前端團隊的嗎,團隊外的開發者占多少?
回答14:是的。不過自從 Element 開源以來,社區出現了一批熱心用戶,他們也貢獻了自己的代碼。所有貢獻者可以從這里看到:https://github.com/ElemeFE/element/graphs/contributors 。

問題15:幾年前,不計算Gzip 90kb的jQuery遭到了人們的嫌棄:太大了,還是用原生吧。 幾年后,開啟Gzip后 仍有300kb的基於React的項目,人們覺得:區區300kb而已,算很小了。巨型庫的概念流行后,人們似乎忘了啪啪啪在臉上有多疼。
你怎么看?
回答15:這幾年網速已經提升不少,同時前端項目的復雜度也變得越來越高。比起庫的體積,可能現在開發效率才是開發者所關心的。

問題16:Element這套組件和Ant Design感覺有點類似,是否有借鑒過他們的設計?
回答16:有借鑒過,我們不僅借鑒過 AntDesign,國內外大大小小的 Design Language 都有借鑒學習。很多信息前人已經總結過,我們希望可以快速的獲得這些知識,以便更快的走到前方去探索更前沿的設計。

問題17:感謝分享。目前有沒有優秀應用案例可以分享?
回答17:目前我們還沒有精力去收集整理使用了 Element 的項目,不過按照最近一段時間在 Gitter 里討論看到了一些開發者分享的項目鏈接,完成質量還是挺高的。過段時間我們會在 issue 里開始征集大家使用 Element 組件庫的作品鏈接做分享。

問題18:如何定制css,是覆蓋還是改源碼?改了源碼如果element版本更新之后 樣式就沒了?
回答18:可以用 element-theme 主題自定義工具,或者直接下載 element-theme-default 主題包自己修改主題。如果只是簡單的修改,建議直接覆蓋樣式。

問題19:您好,我是個初學者,看不大懂編程,初學要做前端,應該先從哪開始入手學習
回答19:回答19:freecodecamp 和 codecademy 都是很好的入門途徑。

問題20:在技術選型的時候,基於什么考慮,選擇了Vue,而不是React
回答20:因為和 react相比,我們公司前端使用 Vue 的更多。為了照顧到大多數人,我們選用 Vue 作為 Element 的框架。

問題21:我在寫 vue 的組件通信中,也發現事件比較好用但不容易被控制,因此一般是給自定義事件加上命名空間,如 $dispatch('AComponent::rotate', 90),Element 團隊有這樣的實踐嗎?
回答21:在 element 的組件中對組件通信這一塊並沒有用到全局的 event bug,而是通過dispatch和broadcast來進行相互依賴的組件間的通信。雖然vue2.0里棄用了$dispatch和$broadcast的api,但我們自己在組件庫中封裝了一遍。

問題22:Element 團隊使用 Vuex 時的一些具體情景是什么?
回答22:22. element 沒有用到vuex的情景

問題23:后續是否會開放一些常見業務組件,比如城市選擇等
回答23:與后台業務關聯很大的組件應該不會直接開放,不過這個組件如果能解決類似的其他問題,我們會抽離業務屬性將之作為「基礎組件」開放出來。城市選擇只用了比較基礎的 Select 組合而已。

問題24:element 在做動畫效果這個方向上有些什么成績呢
回答24:動畫效果方面我們還沒有人力去探索和研究,主要精力還是放在業務系統的搭建上

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM