cube-ui 是滴滴去年底开源的一款基于 Vue.js 2.0 的移动端组件库,主要核心目标是做到体验极致、灵活性强、易扩展以及提供良好的周边生态—后编译。

自 17 年 11 月开源至今已有 5 个月,在这个过程中 cube-ui 受到了不少的关注,同时从社区中也收到了很多很好的反馈和建议。我们也一直在迭代更新,从最初的 1.0 版本到最近发布的 1.7 的版本,除了对原有组件做一些增强优化,我们也提供了很多新的组件。此外,周边后编译技术生态也做了很多优化,满足于更多场景需求,官网也做了一次升级。

接下来就重点介绍下 cube-ui 在这个过程中的有哪些成果以及一些设计细节。

关键成果

cube-ui 的组件数已经从最初的 14 个增长了 28 个,足足翻了一倍,已有的组件生态:

除了上述的组件外,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 的项目,这个项目默认跑起来是这样的:

如果我们想要把项目中使用的按钮的背景色该换掉,那么可以修改 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 配置,刷新后的样子为:



这样我们就可以轻松做自己想要的主题定制,所要做的就是修改 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 会朝着如下方面继续前行:

● 丰富组件

● 组件优化

● 文档优化

● 示例优化

● 周边建设

希望感兴趣的同学可以一起共建或者加入我们团队,一起玩技术!

滴滴开源 Vue 组件库— cube-ui的更多相关文章

  1. vue统计组件库和ui框架

    UI组件 element ★13489 - 饿了么出品的Vue2的web UI工具套件 Vux ★8133 - 基于Vue和WeUI的组件库 iview ★6634 - 基于 Vuejs 的开源 UI ...

  2. Vue组件库

    滴滴cube-ui https://didi.github.io/cube-ui/#/zh-CN/docs/quick-start 有赞开源Vant(适合做商城) https://tech.youza ...

  3. ElementUI 不维护了?供我们选择的 Vue 组件库还有很多!

    前文回顾:Vue+Spring Boot 前后端分离的商城项目开源啦! Vue 组件千千万,只要不行咱就换. ElementUI 近况 根据我最近的观察,得知一些关于 ElementUI 维护人员都退 ...

  4. Vue组件库的那些事儿,你都知道吗?

    前段时间一直在研究Vue组件库,终于在组内派上了用场.来给大家贡献一篇关于Vue组件库的相关知识.经验不多,如果有不合理的地方还请多多指出哦--- 回想一下,在你们公司或者你们小组是否有一个以上的项目 ...

  5. 利用webpack打包自己的第一个Vue组件库

    先说一下这篇文章的诞生原因.我们有一个这样的项目,类似或者说就是一个仪表板-Dashboard,其中的各个部分可能不是一个部门写的……我们需要提供拖拽布局(大小和位置)和展示的能力.要实现这样一个功能 ...

  6. 仿ElementUI构建自己的Vue组件库用babel-plugin-component按需加载组件及自定义SASS主题

    最近使用ElementUI做项目的时候用Babel的插件babel-plugin-component做按需加载,使得组件打包的JS和CSS包体积大大缩小,加载速度也大大提升,所有想模仿做一个组件库也来 ...

  7. 一、移动端商城 Vue 组件库

    一.组件库 移动端商城 Vue 组件库

  8. 发布 Vant - 高效的 Vue 组件库,再造一个有赞移动商城也不在话下

    发布 Vant - 高效的 Vue 组件库,再造一个有赞移动商城也不在话下:https://segmentfault.com/a/1190000011377961 vantUI框架在vue项目中的应用 ...

  9. vue组件库用markdown生成文档

    前言: 开发vue组件库需要提供组件的使用文档,最好是有渲染到浏览器的demo实例,既能操作又能查看源代码.markdown作为常用的文档编写载体,如果能在里面直接写vue组件,同时编写使用说明就再好 ...

随机推荐

  1. redis 3.0 集群__安装

    参考文档 http://redis.io/topics/cluster-tutorial http://redis.io/topics/cluster-spec http://redis.readth ...

  2. Squid代理服务器(四)——反向代理

    一.概念 反向代理主要实现的是我们内部网站的加速功能 ,许多大型的门户网站架构中都采用了反向代理加速,使用比较多的是nginx.squid等,通过squid反向代理加速网站的访问速度,可将不同的url ...

  3. leetcode-824-Goat Latin(字符串的处理)

    题目描述: A sentence S is given, composed of words separated by spaces. Each word consists of lowercase ...

  4. CoreImage 图片处理

    CoreImage 是苹果 iOS5新增的一个 OC 框架,提供了强大的图像处理功能, 用于对基于像素的图像进行操作与分析, 提供了很多滤镜(Filter),形成强大的自定义效果 CIImage 类 ...

  5. 04. H5标签有哪些?行内元素有哪些?块级元素有哪些?空(void)元素有哪些?行内元素和块级元素有什么区别?你工作中常用标签有什么?

    4. H5标签有哪些? 2)行内元素有哪些? a - 锚点 em - 强调 img - 图片 font - 字体设定 ( 不推荐 ) i - 斜体 input - 输入框 3)块级元素有哪些? add ...

  6. 【Leetcode】Binary Tree Traversal

    把三个二叉树遍历的题放在一起了. 递归写法太简单,就不再实现了,每题实现了两种非递归算法. 一种是利用栈,时间和空间复杂度都是O(n). 另一种是借助线索二叉树,也叫Morris遍历,充分利用树中节点 ...

  7. JQuery的get、post、ajax方法

    1.jQuery $.get() 方法 $.get() 方法通过 HTTP GET 请求从服务器上请求数据.  jQuery.get( url, [data], [callback] ):   参数: ...

  8. Git远程库

    要关联一个远程主机,使用命令 git remote add origin <url> : 删除远程主机,使用命令 git remote rm origin ; git push 的一般形式 ...

  9. Shell检查程序是否正常,并显示出程序启动时间、执行时间

    项目中多个实例,而每个实例均启动一个程序,本脚本将多个程序统一进行监控,并支持文件配置 #!/bin/sh bin=$(cd ``;pwd) cd ${bin} ### 定义检查函数 chkProgr ...

  10. 用servlet进行用户名和密码校验01

    用servlet进行用户名和密码校验01 编写一个servlet进行用户名和密码校验,获取登录页面的用户名密码,并显示出来 1.工作目录结构 2.首先是一个login.html,包含非常简单的登录表单 ...