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

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

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

關鍵成果

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

在這裏插入圖片描述

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

並且 cube-ui 也已經支持了以下特性: ● 自定義主題 ● rem 佈局 ● SSR 支持web

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

設計細節

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

組件模塊

● 滾動 & 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 配置自動生成表單,這樣開發表單的成本就下降了不少;同時爲了兼顧靈活性,也支持經過插槽來自定義開發者須要的結構交互。 後編譯api

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

  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 的項目,這個項目默認跑起來是這樣的:
    在這裏插入圖片描述

若是咱們想要把項目中使用的按鈕的背景色該換掉,那麼能夠修改 theme.styl 的文件內容: // 若是你須要使用 cube-ui 自帶的顏色值 須要 require 進來app

@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 配置,刷新後的樣子爲:

在這裏插入圖片描述
這樣咱們就能夠輕鬆作本身想要的主題定製,所要作的就是修改 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 尺寸效果:
    在這裏插入圖片描述
    當尺寸變大,例如爲 iPhone 6 Plus 尺寸時效果:

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

上層擴展

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

在這裏插入圖片描述
咱們基於 cube-ui 提供的能力開發它就很是方便了。首先能夠基於 Popup 組件開發一個 subscribe-dialog.vue 組件:

<template>
  <div class="subscribe-dialog-view">
    <cube-popup ref="popup" @mask-click="hide">
      <div class="subscribe-dialog-wrapper">
        <span class="close" @click="hide"><i class="cubeic-close"></i></span>
        <div class="title">開啓推送通知</div>
        <img src="./subscribe.png">
        <p class="desc">第一時間獲取最新鮮出爐的新聞攻略、賽事諮詢、數據專題、精彩視頻</p>
        <cube-button class="button" @click="start">如今開啓</cube-button>
      </div>
    </cube-popup>
  </div>
</template>

<script>
export default {
  name: 'subscribe-dialog',
  methods: {
    show () {
      this.$refs.popup.show()
      this.$emit('show')
    },
    hide () {
      this.$refs.popup.hide()
      this.$emit('hide')
    },
    // ...
  }
}
</script>
複製代碼

接着使用 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 會朝着以下方面繼續前行: ● 豐富組件 ● 組件優化 ● 文檔優化 ● 示例優化 ● 周邊建設 但願感興趣的同窗能夠一塊兒共建或者加入咱們團隊,一塊兒玩技術!

相關文章
相關標籤/搜索