餓了麼基於Vue2.0的通用組件開發之路(分享會記錄)

 

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

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

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

 

## 設計目的

大部分項目起源都是源於業務方的需求,Element 也是同樣。隨着公司業務發展,內部開始衍生出不少後臺系統,UED 部門也接到愈來愈多的設計需求,分析整個過程,咱們發現以下問題:vue

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

因而咱們決定:react

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

 

## 設計階段

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

**瞭解業務並熟悉公司內各後臺產品,尋找業務上的共性問題**
設計的目的是爲了業務服務。第一步咱們從內部系統開始入手,瞭解公司內部在使用的各類後臺系統,將其組件抽象剝離,尋找共性特徵。git

**專一業務組件設計**
總結了公司不一樣系統不一樣組件的使用狀況後,咱們打算從業務組件入手,由於這部份是由公司特殊需求衍生的解決方案,咱們認爲解決了這些棘手的問題,也能給其餘後臺產品帶來好的設計引導。github

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

**與大前端合做**
大前端做爲獨立的前端團隊,有能力開發底層的工具去服務不一樣業務,而且 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:動畫效果方面咱們尚未人力去探索和研究,主要精力仍是放在業務系統的搭建上

相關文章
相關標籤/搜索