滴滴開源 Vue 組件庫—cube-ui

cube-ui是滴滴去年底開源的一款基於 Vue.js 2.0 的移動端組件庫,主要核心目標是做到體驗極致、靈活性強、易擴展以及提供良好的周邊生態—後編譯。

自 17 年 11 月開源至今已有 5 個月,在這個過程中 cube-ui 受到了不少的關注,同時從社區中也收到了很多很好的反饋和建議。我們也一直在迭代更新,從最初的 1.0 版本到最近發佈的 1.7 的版本,除了對原有組件做一些增強優化,我們也提供了很多新的組件。此外,周邊後編譯技術生態也做了很多優化,滿足於更多場景需求,官網也做了一次升級。 接下來就重點介紹下 cube-ui 在這個過程中的有哪些成果以及一些設計細節。

關鍵成果

cube-ui 的組件數已經從最初的 14 個增長了 28 個,足足翻了一倍,已有的組件生態:

滴滴開源 Vue 組件庫—cube-ui

除了上述的組件外,cube-ui 還對外暴露了三個模塊: ● style ● create-api ● better-scroll

而且 cube-ui 也已經支持瞭如下特性: ● 自定義主題 ● rem 佈局 ● SSR 支持

此外,cube-ui 的周邊生態也有了進一步豐富: ● vue-cli 腳手架模板 cube-template ● 快速上手教程 cube-application-guide ● 後編譯 webpack 插件 webpack-post-compile-plugin ● 按需引用 webpack 插件 webpack-transform-modules-plugin,依賴 babel 插件 babel-plugin-transform-modules

設計細節

針對於上邊所介紹的關鍵成果,我們來聊聊具體設計上的細節。

組件模塊

● 滾動 & Picker 類組件 在移動端,由於手機尺寸以及交互特性,我們需要處理很多滾動類需求:下拉刷新、上拉更多、輪播等以及 Picker 選擇等場景。cube-ui 底層滾動類組件以及 Picker 類依賴於我們團隊的移動端利器 better-scroll 實現,基於其出色的體驗進而保證了我們上層封裝的滾動類、Picker 類組件的出色的交互體驗。 ● 彈出層類組件 在實際開發中我們會遇到很多彈出層類組件,因為我們設計了一個基礎彈出層組件 Popup,它主要解決移動端最為常見的居中(Tip:文本換行位置也很重要哦)、置底以及是否有蒙層效果,藉助於它來實現絕大多數的彈出層組件。 另一個常見的痛點就是由於彈出類組件往往是全屏的狀態,如果我們按照 Vue 推薦的聲明式的語法在子組件裡使用彈出層組件,由於嵌套層級問題,很容易受到父級元素的樣式影響。為此我們單獨開發了 create-api 模塊,通過 API 的形式將實例化的彈出層組件動態掛載到 Body 元素下,因此擺脫了父級元素樣式的影響,同時會隨著使用它的組件的銷燬而自動銷燬,且為了降低開銷成本,根據需要有些彈出層類組件都被設計成了單例模式。它是一個很通用的能力,我們把這樣的一個便捷的 API 對外暴露出去,開發者也可以根據實際場景將自己開發的組件通過 createAPI 進行註冊,進而也可解決上述痛點。 ● 表單類組件 表單類需求往往特應性比較大,交互也很難做到統一,但是仍然可以有主流的表單設計交互,在 cube-ui 中表單可以設置 layout 來決定樣式甚至是交互,滿足日常場景需求。在表單設計中有兩個很重要的組件:Validator 和 Form。Validator 成為獨立的組件主要基於校驗場景不確定性,同時還需要滿足各種形式的校驗,所以 Validator 就只做了兩件核心的事情,對數據源進行校驗以及對應的錯誤信息的展示。考慮到開發者開發表單的便利性,我們參考 vue-form-generator 的設計,把表單設計成了根據 Schema 配置自動生成表單,這樣開發表單的成本就降低了很多;同時為了兼顧靈活性,也支持通過插槽來自定義開發者需要的結構交互。 後編譯

後編譯是 cube-ui 的一個重要的生態,藉助於後編譯,整個的 web 應用的開發都可以直接基於 ES2015+ 進行開發,而項目依賴的一些 NPM 包也是可以直接使用 ES2015+ 進行開發,並且無需編譯可直接發佈到 NPM 平臺上(也可以是自己 NPM 私服)。這樣,這些組件庫或者工具就可以有更多的想象空間、可以做更多有意思的事情。 cube-ui 支持的兩個特性自定義主題以及 rem 佈局都是基於我們主推的後編譯技術實現。 接下來一起來看下這兩個特性實現的細節。 自定義主題 一般而言,組件庫都是有默認主題的,而往往還會搭配有多套主題(PC 類組件庫比較常見)。現在藉助於 CSS 預處理器,我們可以給組件定義一些變量(一般都是顏色值),然後在組件對應的樣式中使用。 對於自定義主題這種需求,主流的做法有:樣式覆蓋和修改變量。

  1. 樣式覆蓋 樣式覆蓋是最古老的做法,但是缺點也很明顯,第一就是樣式冗餘問題,默認主題樣式是一直存在的;第二就是開發者需要確切的知道樣式對應的優先級去覆蓋,要麼是同級的優先級樣式後置,要麼就是提升自身覆蓋的樣式優先級。 當然,樣式覆蓋的做法也是有優點的,那就是對於多主題同時存在,自由切換場景會比較合適。
  2. 修改變量 現在有很多的 CSS 預處理器可以選擇,每一種 CSS 預處理器都提供了變量功能,藉助於變量,我們可以很容易創建一個主題文件,裡邊包含組件依賴的變量定義。要實現自定義主題,開發者需要在自己項目下創建一個單獨的樣式文件,定義賦值變量,同時引入組件庫自身源碼下的主題文件。 本質上也是一種後編譯做法,這個編譯是利用 CSS 預處理器自身的變量能力達到目的。對於 Vue 組件庫而言,主流的也是推薦的做法是把樣式寫在 .vue 文件中,這樣便於維護,比較符合組件化開發思維;但是為了方便的使用預處理器實現自定義主題,通常都會把樣式單獨拿出來,一般的做法是創建一個樣式文件夾,裡邊包含所有組件樣式,而在 .vue 文件中則是沒有樣式的。
  3. cube-ui 做法 核心點就是藉助於後編譯,我們可以按照原有我們習慣的方式去書寫組件,即在 .vue 文件中包含模板、腳本和樣式。如果需要自定義主題,就在自己項目下創建一個主題文件,裡邊定義變量,這個做法和一般的修改變量做法一樣,但是不需要引入所有樣式入口文件,因為也不存在這樣的一個文件;同時藉助於 webpack,我們完全可以做到在不侵入源碼的情況下,做到主題定製。 接下來就看下具體做法,如果是新創建的項目,那麼推薦使用 Vue-cli + cube-template 模板生成;而如果是現成的項目,則具體參考官方文檔 - 主題 中配置。主要有兩個核心點: ● 創建主題文件 theme.styl,一般放在 src/ 目錄下 ● 修改 webpack 中關於 stylus-loader 的配置項:添加 import 字段用於依賴自定義主題文件 接下來就看一個簡單項目演示,假設創建了一個 demo 的項目,這個項目默認跑起來是這樣的:
滴滴開源 Vue 組件庫—cube-ui


如果我們想要把項目中使用的按鈕的背景色該換掉,那麼可以修改 theme.styl 的文件內容: // 如果你需要使用 cube-ui 自帶的顏色值 需要 require 進來

@require "~cube-ui/src/common/stylus/var/color.styl"
// button
$btn-bgc := #409eff
$btn-bdc := #409eff
$btn-active-bgc := #66b1ff
$btn-active-bdc := #66b1ff

配合我們的 webpack 配置,刷新後的樣子為:

滴滴開源 Vue 組件庫—cube-ui

這樣我們就可以輕鬆做自己想要的主題定製,所要做的就是修改 cube-ui 已經定義好的變量值即可。對於 cube-ui 組件庫自身,則不會有任何修改,且對於應用開發者而言,用不用自定義主題,本身的源代碼不用修改,只需要創建一個主題文件(無需手工引入)配合 webpack 插件配置即可。

其實對於主題定製,還可以更進一步,未來 cube-ui 會考慮藉助於 CSS 自身支持的變量(自定義屬性)達到主題定製的目的,例如可以把處理器變量改為原生的變量,編譯的話可以通過 post-css-variables 插件把默認變量值做替換,可以實現和現有編譯後功能相同的效果,同時在後編譯的情況下不失原生 CSS 變量的動態優勢。這樣,不僅可以做到主題定製,也可以做到多主題的自由切換,因為 CSS 原生變量可以直接修改變量值而不需要通過事先寫死然後切換 class 覆蓋的方式做多主題切換。

rem 佈局

在移動端還是有很多設計師、產品或者開發者偏愛用縮放來達到不同尺寸屏幕適配目的,而縮放的實現一般都是採用 rem 進行佈局,業內比較出名的方案就是手機淘寶前端團隊開源的 lib-flexible。 現在其實是不推薦使用 rem 進行佈局的,如果真的要縮放的效果,可以考慮 vw vh 等 CSS 單位來實現。 rem 佈局有兩個核心的點:

  1. 在運行時動態根據視口寬度更新 rem 的值,即修改根元素 HTML 的 font-size 的值
  2. 在編譯時(或開發時)需將設計稿的 px 單位轉換為 rem 單位 對於組件庫而言,如果想要同時做到即支持普適的 px 又支持 rem 這種方式的話,社區貌似還沒見到。和後編譯搭配,則比較容易實現,在 cube-ui 中,已經提供了 rem 支持,主要採取的方案:
  3. 可選的 amfe-flexible, 也就是 lib-flexible 動態計算更新 rem 的值(注 2.x 版本)
  4. 選擇了 postcss 的插件 postcss-px2rem 作為將 px 轉換為 rem 的庫 這其實是對組件庫本身有了一定要求,和尺寸相關的儘量要用樣式控制,這樣才能通過處理工具 postcss-px2rem 將 px 單位處理成 rem 單位,進而實現動態縮放需求。 來看下 cube-ui 使用 rem 的效果,默認 iPhone 5 尺寸效果:
滴滴開源 Vue 組件庫—cube-ui


  1. 當尺寸變大,例如為 iPhone 6 Plus 尺寸時效果:
滴滴開源 Vue 組件庫—cube-ui


可以看出整體的效果,當尺寸較小時,Button 和 Toast 都是比較小的,而當尺寸比較大時,相對應的都會更大,達到了縮放的目的。

上層擴展

這裡上層擴展主要是指基於組件庫進行二次封裝,例如在滴滴內部,我們的很多業務組件庫就是在開源的 cube-ui 組件庫之上做增強而來的。 這個能力是非常重要的,因為移動端組件庫和 PC 組件庫最大的區別是移動端多是 to C 的業務場景,不同的業務場景下的設計是不一樣的,所以 cube-ui 專注於通用組件和基礎能力的建設,並不會在佈局和業務組件方面大做文章;而 PC 組件庫一般都是用於 to B 的場景,如內部 MIS 類的系統,對於設計的要求並沒有特別苛刻,所以基礎的樣式,組件都是可以統一的。因此 cube-ui 的定位並不是要提供一個“大而全”的組件庫,而是提供了二次擴展的能力,目標是任何移動端的業務場景都可以基於 cube-ui 提供的能力做二次擴展。 以我們的快速上手教程為例,我們要開發如下圖的彈窗組件。

滴滴開源 Vue 組件庫—cube-ui

我們基於 cube-ui 提供的能力開發它就非常方便了。首先可以基於 Popup 組件開發一個 subscribe-dialog.vue 組件:




接著使用 createAPI 模塊把它變成一個 API 式的組件:

import SubscribeDialog from './components/subscribe-dialog/subscribe-dialog'
createAPI(Vue, SubscribeDialog, [], true)

然後調用它就非常方便了:

this.subscribeDialog = this.$createSubscribeDialog()
this.subscribeDialog.show()

周邊生態周邊生態有兩個核心:後編譯 + 按需引入。為此,我們開發了兩個 webpack 的插件來幫助我們更好的去使用、開發。 ● 後編譯 webpack 插件 webpack-post-compile-plugin ● 按需引用 webpack 插件 webpack-transform-modules-plugin webpack-post-compile-plugin 這個插件主要是讀取應用 package.json 中的 compileDependencies 字段的值(用於指定應用需要後編譯哪些依賴包),而且還能解決嵌套後編譯包的問題,因為開發者只需要關注自己依賴需要後編譯的包,而不需要關注依賴的依賴包,這樣就能構成一條生態鏈。

為什麼不是一個 NPM 包自己聲明需不需要後編譯,而是由使用者去聲明? 主要考慮整個 NPM 生態,例如 lodash-es 並不在我們控制範圍之內,為了更好的使用整個 NPM 生態圈的包,我們決定由使用者去聲明需要後編譯的 NPM 包。 webpack-transform-modules-plugin 這個插件主要解決更方便、友好地使用按需引入的問題,為了更好的統一應用使用後編譯和不使用的情況,我們在原本 babel-transform-imports 的基礎上做了升級優化產出了 babel-plugin-transform-modules 插件,但是和後編譯的場景類似,這個是不能解決後編譯場景下 NPM 包嵌套按需引入的問題的,為此才開發了 webpack-transform-modules-plugin 這個插件,和 compileDependencies 字段類似,我們新增了 transformModules 字段來聲明按需引入的 NPM 包的的轉換規則,例如:

"transformModules": {
"cube-ui": {
"transform": "cube-ui/src/modules/${member}",
"kebabCase": true
}
}

當然在後編譯的場景下,我們藉助於 webpack 4 Tree shaking 中新增的 side-effects 也可以達到目的,這個是未來我們去優化的方向。

腳手架 & 教程任何的技術都是有成本的,我們新增了 webpack 插件,也有一些需要配合的改動,所以為了降低開發者成本,我們提供了適用於 vue-cli 腳手架的模板 cube-template,當然對應的也會新增一些配置項,感興趣的可以瞭解下cube-template wiki。 同時為了初次使用 cube-ui 的開發者快速上手,我們還有一個簡單的上手教程 cube-application-guide。

展望cube-ui 目前還處於初步的階段,還缺少很多組件,但是我們一直在努力,希望在很快的未來可以提供更多更好用的組件。不僅如此,我們希望的是除去組件庫本身,額外還會豐富周邊的整個生態建設,給開發者一個良好的生態環境,進一步提升開發體驗,提升應用性能等。當然,我們也希望社區的小夥伴也能參與進來,一塊共同建設,共同進步。 未來 cube-ui 會朝著如下方面繼續前行: ● 豐富組件 ● 組件優化 ● 文檔優化 ● 示例優化 ● 周邊建設 大家一起玩技術!


分享到:


相關文章: