騰訊 Web UI 解決方案 QMUI Web —— 探索與沉澱

騰訊開源發表於2017-09-08

作者簡介:
李浩成(Kayo),騰訊廣州研發部 UI 工程師。

經過長時間的打磨迭代,QMUI Web 作為騰訊廣研 QMUI 團隊的一個開源專案,正式釋出到 Tencent Github。QMUI Web 是一個 Web UI 的解決方案,從零開始,由編碼規範,到元件和工具方法的製作,再到工作流的整合,不斷在迭代,也不斷在優化,走過了不少的路。趁著釋出的機會,我們正好回顧這一路的探索過程,分享其中的點滴,也希望能借此讓大家更瞭解 QMUI Web。

背景

2014 年中,QMUI 團隊支援的主要專案是 QQ 郵箱,Web 端的郵箱是個龐大的專案,但其並沒有統一的 UI 基礎庫,多年的高速迭代使得專案的 UI 程式碼變得混亂,各個模組之間各自開發,除了在程式碼層面表現出混亂和不可控之外,表現層面也並沒有很好地統一起來。因此,專案急需一套統一的團隊編碼規範以及一個 UI 基礎庫。
恰好,這個時候 Sass 等 CSS 前處理器已經發展成熟,自動化工作流的工作模式也日趨完善,因此,我們決定基於這些技術製作一套通用於不同專案的 Web UI 框架。框架的場景定位很明確:需要控制整體樣式,並且可以適應頻繁迭代打磨的大型專案。所以,這套即將誕生的 Web UI 框架的特性也很明確:需要方便地控制專案的整體樣式,應對頻繁的介面變動,並保持專案質量穩健。
此後經過三年的發展,QMUI Web 最終發展為包含編碼規範、樣式工具方法與樣式管理、內建工作流,配套的 GUI 桌面 App,以及擁有完整文件的解決方案。

設計理念

在製作框架的過程中,我們把框架需要的特性進行整理和思考,形成了一套對於該框架的設計理念,在這些設計理念之中,最核心的關鍵為通用於多個專案高效迭代保持程式碼穩健,框架的設計也遵循這三個核心點,體現在框架上,具體就是:

  • 框架和元件需要剝離業務。作為 UI 框架,框架內整合的元件和樣式必須有能力剝離業務,才能跨專案使用。
  • 能輕易控制整體樣式。需要高效地迭代專案,樣式的整體控制必不可少。
  • 保持程式碼穩健。

而具體到程式碼層面,則可以歸納為兩個方面:

  • Class-name 命名規範。
  • 基礎樣式配置與半封裝元件。

Class-name 命名規範

作為一個 Web UI 框架,編寫程式碼主要是 CSS 與 HTML,而提到 CSS 與 HTML 的編寫,首先要處理的是 Class-name 的命名,在過往的開發中,Class-name 的命名並沒有固定的規範,開發人員各自進行開發,一個專案經過長時間的迭代後,經常會遇到如命名衝突,命名混亂等問題,這使得專案的迭代變得笨重,也不好維護。因此,我們需要一套具有如下特點的 Class-name 命名規範:

  • 命名有跡可循,容易編寫。
  • 避免命名衝突,包括內部多人協作命名衝突,以及外部庫引入時的被動汙染。
  • 命名具有語義,能晰地描述整個頁面,方便理解上下文。

因此,最終 QMUI Web 制定了一套以名稱空間為核心的命名方式,這個命名方式主要由“名稱空間”,“業務與元件的拆分”,“精確表達 View”三個部分構成。

名稱空間

一個 QMUI Web Class-name 應該包含一個名稱空間,也是 Class-name 的開頭,如果是業務,則以業務內容為名稱空間,如果是公共元件,則全域性使用專案的名字(或縮寫)為名稱空間。如一個名為 Demo 的專案,專案縮寫可以是 dm,那麼該專案下的專案元件和公共類可以這樣命名: dm_btn(按鈕)、dm_icon(圖示)、dm_ipt(輸入框)、dm_toolbar(工具欄)。
邏輯模組命名以具體業務作為字首,如簡歷(resume)功能裡面的非公共元件部分,以 resume_ 作為字首(resume_modresume_textresume_list),個人資訊(profile)頁面的非公共元件部分,則可以以 profile_ 作為字首(profile_statgeprofile_stage_title)。
名稱空間作為一種基礎的隔離,把元件與業務,以及不同的業務之間的 Class-name 命名隔離開來,避免衝突,而後父子元素之間逐級展開編寫,保證了專案內多人協助不易衝突,同時命名帶有語義,也方便理解和閱讀。

image_1bouo6vnb1jsg1q31af418351e129.png-85.3kB
image_1bouo6vnb1jsg1q31af418351e129.png-85.3kB

image_1bouo7q7t1lsi1f7kqkktsk1tn9m.png-61.8kB
image_1bouo7q7t1lsi1f7kqkktsk1tn9m.png-61.8kB

業務與元件的拆分

接著,QMUI Web 中把專案的程式碼劃分為通用元件(跨專案的元件),專案全域性元件(適用於某個具體專案),業務元件(適用於某個業務),以及業務邏輯程式碼,這樣區分出4個顆粒度可以使得程式碼更容易被組織和複用,一個模組隨著設計元素迭代,也可以在這4個顆粒度之間進行迭代,從而使得模組在迭代時會更加穩健。而 QMUI Web 框架中的元件應該只收納通用元件,即跨專案元件。

精準表達 View

精準表達 View 是指在命名 DOM 節點時要明確這是一個怎樣的 View,這裡的 View 指的就是 UI 層面上這個元素表示的含義,常見的場景是,一個命名為 resume_head 的元素,在經歷多次迭代後實際在程式碼中卻充當了頁尾,這樣的命名在多人協作時很容易給後面的開發者造成困擾,而精準表達 View 則要求我們明確一個 UI 元素的含義,並在命名時準確地表達。

基礎樣式配置與半封裝元件

前面的“Class-name 命名規範”主要是在規範層面上去實踐 QMUI Web 的核心理念,而接著更多地就是在程式碼層面上去實踐了,主要包括三點:

  • 半封裝元件,即面向專案的元件。
  • 使用組合而不是繼承。
  • 顆粒度的把控。

半封裝元件即面向專案的元件

前文提到,QMUI Web 把元件劃分為通用元件,專案全域性元件,業務元件三種元件,而 QMUI Web 框架收納的則是通用元件,也是跨專案的元件,但每個專案的 UI 表現並無關聯,如何處理跨專案元件就成為了一個問題。為此,QMUI 在處理元件時採取的是“半封裝”的處理方式,QMUI 框架封裝的是程式碼,所謂半封裝,即封裝那些與專案具體 UI 表現沒有必然聯絡的程式碼。例如按鈕元件,QMUI Web 中只封裝了文字居中對齊,滑鼠手型,瀏覽器樣式重置,低版本 IE 相容性處理等程式碼,而常用的樣式如邊框、背景、字型表現等,都抽取成變數控制,這些元件的變數最終都彙集到一個配置表 Sass 檔案中,配合全域性的顏色變數、字型變數等變數,就可以做到跨專案抽取元件,每個新專案只需要關注具體 UI 表現而無需再處理各種常見的 UI 問題,同時方便地通過調整這些變數的值而快速修改整個專案的樣式。

image_1boupcrpp1okoc05117ar1g1g9h13.png-224.3kB
image_1boupcrpp1okoc05117ar1g1g9h13.png-224.3kB

組合而不是繼承

在處理元件時,繼承的方式是指一個元件類承擔複雜的功能,而組合的方式則是把元件類拆分成一個基類,以及多個子類,每個子類承擔的功能不重複,對於我們的主場景——頻繁迭代,保持穩健,顯然組合會更加適合,這種方式避免了在頻繁的迭代中需要不斷修改元件類,每次迭代只需要修改對應的子類即可。

顆粒度

對於元件的抽取,時常要考慮顆粒度的劃分,顆粒度本身就是一個比較開放性的問題,在這裡與大家分享一些沉澱的經驗:

  • 抽取元件以 UI 表現為區分,例如一個刪除按鈕,是以刪除 icon + 刪除文案作為內容的,但在表現上它就是一個帶 icon 的文字按鈕,因此就抽取出一個支援 icon 的文字按鈕,而不用只侷限於按“刪除”這個業務來命名元件。
  • 抽取元件可以選擇較大的顆粒度,也可以選擇較小的顆粒度。顆粒度較大的元件實現複雜,能對應複雜的場景,但擴充套件性也會因此下降,而顆粒度較小的元件則實現簡單,能輕鬆實現一個主場景,但又方便擴充套件,能靈活地應對變化。因此建議是像按鈕、輸入框、下拉選單這類通常位於頁面 DOM Tree 末端的元素可以抽取成儘量簡單的元件,同時通過擴充套件的方式去處理各種場景差異。而其他複雜的元件則可以專注於一個業務,不必過多地考慮不同的場景,否則元件很容易變得難以維護。

以上便是 QMUI Web 具體的設計理念,通過命名規範、基礎樣式配置與半封裝元件來保證多人協作時的高效率與可維護性,也使得一個 UI 框架能為不同的專案服務。

具體組成

作為一個框架,QMUI Web 主要提供了四種能力來提升 UI 開發的效率與質量,對應前文提到的框架設計理念,QMUI Web 提供的這些功能都是為了幫助開發者方便地控制專案整體樣式,應對頻繁變動,同時保持程式碼穩健。

基礎配置與元件

前文提到,框架中會有一份配置表,是各種 Sass 的變數,這些變數控制了一個網頁基本的字型樣式,連結顏色,通用元件的樣式配置等基礎樣式,在建立一個新專案時,應該先根據設計稿配置好這些資訊,當這些資訊配置完成,那麼一個專案的基本樣式就可以快速實現了。例如下圖中這些配置屬於 QMUI 通用配置,通過修改這些配置則可以快速修改專案的字型策略、正文字型大小,連結顏色等 UI 常用的 CSS 屬性。

image_1bouqhakqcnr163ruqeubm1n8d1g.png-449.6kB
image_1bouqhakqcnr163ruqeubm1n8d1g.png-449.6kB

內建工作流

QMUI 中包含一個基於 gulp 的內建工作流,用於快速解決大量重複勞動力的工作,從而提升效率。QMUI 的 gulp 中預先實現了監控 Sass 檔案並自動編譯和優化,雪碧圖處理,模板 include 能力(可以傳參和使用條件判斷),瀏覽器自動重新整理,圖片壓縮,檔案清理,檔案合併以及自動變更等能力。

image_1bour0k6d13ff1o8k170hed81o8v1t.png-256.4kB
image_1bour0k6d13ff1o8k170hed81o8v1t.png-256.4kB

Sass 增強支援

QMUI 中提供了大量基於 Sass 的 CSS 預處理的方法,包括 CSS Reset,一些常見的 CSS 類(例如清除浮動),計算長度值的簡便方法(例如獲取 padding 在某個方向的值,計算兩個長度值的中間值),快速實現一些樣式效果的工具方法(例如實現 border 三角形,適應多倍螢幕的 1px 邊框等),這些都是用於提高樣式開發的效率和質量。

擴充套件元件

擴充套件元件並不是由 QMUI Web 的主原始碼提供,而是由 Demo 提供,通常是因為這類元件結構較複雜,因此業務性無法很好地剝離,從而不能抽取成公共元件,因此這類元件就放在一個 Demo 頁,以參考元件的形式幫助開發。

GUI

我們提供了一個用於管理 QMUI Web 專案的桌面 App,在程式碼層面它獨立於 QMUI Web 的原始碼。它通過 GUI 介面處理 QMUI Web 的服務開啟/關閉,並提供了編譯提醒,出錯提醒,程式關閉提醒等額外的功能,在處理多專案,多分支時能更方便地進行開發。

image_1boura6cn1qm81sqj1hlev127tj2a.png-192.8kB
image_1boura6cn1qm81sqj1hlev127tj2a.png-192.8kB

優化和開源

在經歷較長時間的迭代後,QMUI Web 也逐漸完善起來,此時我們也開始將 QMUI Web 進行開源。開源意味著 QMUI Web 會進入更加全面的環境中去打磨,在框架的非主體內容如程式碼規範、註釋、文件上面也需要更費心思,考慮的點也需要更加周全。這對團隊來說無疑是個很好的機會,可以有更多的渠道審視框架,吸收建議,持續進行優化。

在加入開源的大環境後,我們從 Github、社群論壇中都獲取了不少建議,除了 bug 的反饋外,也指出了一些待完善的地方和提出一些優化的解決方案,從而使得 QMUI Web 注入了更多活力,因此我們也逐步進行了如“自動化測試用例”、“gulp 結構化”,“引入 SassDoc 自動化生成文件”,“編譯 Sass 時引入增加更新”等優化,其中不少優化點我們也在專案的 Github Wiki 中進行了詳細的分享,有興趣的使用者可以自行瀏覽。

總結與展望

至此,QMUI Web 發展為現在這套完整的方案,也終於開源到 Github Tencent 與大家分享,我們期望通過開源與大家進行更多的交流,也使得 QMUI Web 進入更加全面的環境中去打磨,形成對程式碼規範、註釋、專案文件感謝公司與部門給我們提供了一個平臺,可以在大型專案中經歷迭代和沉澱。開源只是一個開始,我們後續仍會不斷進行探索和優化,期待更好的 QMUI Web。

關注 QMUI Web, 來 Github 給我們 star 吧!

github.com/Tencent/QMU…

檢視 QMUI Web 專案原始碼,請點選[閱讀全文]。

相關文章