微信扫描登录
或者
请输入您的邮件地址来登录或者创建帐号
提 交取 消
GITBOOK.CN需要您的浏览器打开cookies设置以支持登录功能

高可定制化的UI框架Yo

在正式分享之前,先做个简要的自我介绍。我叫杜瑶,来自去哪儿网,在去哪儿网担任前端总监,目前是去哪儿网移动前端负责人。加入去哪儿网之前在雅虎,华为,360等公司折腾过。(江湖大家都亲切的称呼瑶姐)

我的一些网络足迹:

  • CSS参考手册 css.doyoe.com
  • CSS探索之旅 blog.doyoe.com
  • Web前端实验室 demo.doyoe.com
  • Github: doyoe
  • Weibo: doyoe

1. 内容概要

今天分享的内容是UI框架Yo:包括在去哪儿网的应用及框架本身的特性。

Yo目前已覆盖了去哪儿网70多个项目,是纯Touch和Hybrid开发的UI框架首选。大幅度的提升了开发效率及风格一致化的统一。

主要分为以下几个部分:

  • Yo简介
  • Yo的设计思路
  • Yo的特性
  • Yo的未来展望

2. Yo简介

Yo是什么?

Yo是一款基于Sass开发的CSS框架,用于快速构建移动项目UI Github: https://github.com/doyoe/Yo
文档:http://doyoe.github.io/Yo/doc/
示例:http://doyoe.github.io/Yo/demo/

3. Yo设计思路

Mobile First

Yo的设计是以Mobile为基准标的的,来看一个预览图:

enter image description here

接下来会讲为什么我采取了这种策略。

Why Mobile First

  • 公司战略转移
  • 生涩的新战场需要有力的框架抹平平台切换所带来的适应过程
  • PC端已有成熟的解决方案,无需推倒重来
  • 专注于某一具体的领域更能变得专业

说到公司战略的转移,我会说一组大概的数据:

去哪儿网在2009年开始移动平台的布局,应该算是国内在线旅行公司涉足该领域最早的几家之一。在这期间,陆续发布了多款细分业务的客户端。到2012年,仅去哪儿旅行客户端这一个应用的独立用户就突破了2000万。

尤其是近2,3年,我们经历过一次大规模的从PC端向移动端过渡的时期,整个公司的酒店及度假类产品在移动端的订单比例高达85%以上;机票来源于移动端的订单量也远超50%。

基于这些订单数据和运营方向,公司战略性的向移动平台投入大量资源,PC端现在基本上只做一定的维护及必备功能的补全需求。

这就意味着我们之前大量奋战在PC端的前端开发需要转战到移动平台上来,前端舞台开始整体转变。

但这同时伴随而来的是,大部分前端开发对移动平台这个新舞台还是比较陌生的。举个例子:高分屏适配,安卓碎片化处理,手机性能问题等等,都是之前很少触及的东西。

Mobile First的好处

  • 可以减少对历史问题的考虑,比如不用考虑IE;
  • 保证交互方式的单一性,不用同时考虑PC上的交互行为;
  • 更及时的应用上新技术;
  • 代码更轻量;

iOS 默认风格

为了取得更佳的用户体验及UI展示,Yo天然的贴近iOS风格,如下图的典型iOS风格效果:

enter image description here

Why iOS

  • 巨量的iOS用户(超过50%)
  • 安卓碎片化过重,不易挑选标的
  • iOS的交互及展现具备普适性

我们经过统计,去哪儿的移动在线用户iOS使用者占比超过50%,接近六成。这是Yo的默认风格会靠近iOS的原因之一。

用过安卓的童鞋,都非常清楚,安卓的碎片化非常严重,不说自身每个大版本之间的差异非常大,就国内各种深度定制的也是一个巨大的问题,所以在安卓上比较难挑选一个合适的标的。

这个选择倒是和现在市面上多数的UI框架类似。

Pure CSS

Yo是一个纯净CSS框架。之所以不像普遍的UI框架那样将js组件之类的杂合进来,更多的是由于定位上的差异。我更希望Yo只专注在UI展现上,而不是其它。 这能让Yo变成更职能清晰,甚至你可以把它应用在任何移动项目上,它只是一件多彩的外衣而已。

当然,在公司内部,我们有一套依赖于Yo而开发的移动组件库。

分层设计

Yo使用了分层设计策略来保证代码的层次和逻辑关系清晰。 来看一张层级简图:

enter image description here

Yo将不同的功能拆解成结构清晰的类别分散到不同的层去进行处理。 其中lib是Yo的核心代码实现,usage是业务代码的舞台。

分层设计的意义

  • 清晰的依赖
  • 高度解耦
  • 高度复用
  • 高可移植性
  • 结束混乱的文件引用

层级示意图:

enter image description here

单位

需要注意的是Yo使用2种单位来处理UI细节

rem + px

  • 边框厚度使用 px 单位
  • 字体,大小等除边框厚度之外的定义都使用 rem

4. Yo的特性

封装了丰富的常规问题解决方案

Yo将很多常规的应用场景方案封装了起来,大部分时候只需要一行代码就能实现某个具体的功能。

下面会简要列举几个例子:

形状方法

比如我要画一个圆,我只需要这样就行:

.demo {
    // 一行代码画个圆
    @include circle(2rem);
}

circle接受2个参数:一个是$size,用来指定正方形的边长;一个是$radius,用来指定圆角半径,默认值是50%,所以默认只需要传入$size就可以画出来一个圆,当然,如果你只是想让这个正方形有个圆角,可以传入$radius,比如值是.1rem。

由于Yo支持顺序参数和关键字参数2种方式,所以如果想使用关键字参数,上述代码可以改成:

.demo {
    // 一行代码画个圆
    @include circle(
        $size: 2rem
    );
}

我更推荐大家使用关键字参数这种方式,这样就不用关系它们之间的顺序;或者当这个方法发生了变更,增加或者移除了某个参数,会让顺序发生变化。 上述的代码编译后,我们就能得到如下图的一个圆:

enter image description here

当然,还有一些其他的形状方法,这里就不再列举,感兴趣的同学,后续可以去看看文档。

文本方法

对于文本溢出显示省略号的需求,以前在PC端常常会碰到,当然,在移动端也是不可避免的,比如下图这样:

enter image description here

这样的效果,使用Yo,也只需要一行代码即可实现:

.item {
    // 超长文本溢出显示省略号
    @include ellipsis;
}

如果你想限定容器的显示宽度,可以给ellipsis方法传入$width参数:

.item {
    // 约束宽度的超长文本溢出显示省略号
    @include ellipsis(
        $width: 3rem
    );
}

我们能得到下图的效果:

enter image description here

更有意思的是,ellipsis方法不仅可以处理单行文本,还提供了处理多行文本的能力:

.item {
    // 指定多行文本溢出显示省略号
    @include ellipsis(
        $line-clamp: 2
    );
}

只需要指定$line-clamp参数即可,意思是指定具体需要截断的行数,当超过指定行数之后,会在被截断的那行尾加上省略号。

enter image description here

同样的,其他的文本方法这里就不介绍了。

像素边框处理方法

说完文本处理,大家可能还对Yo是如何处理1像素边框的话题感兴趣,接下来,会详细说一下。

Yo专门封装了一个border方法,用来处理1像素边框的问题;

该方法使用伪元素::after来处理边框;通过媒体查询,为拥有不同设备像素比的屏幕自动适配成1px;

为了最贴近原生API的设计,border方法接受和CSS border属性一样的厚度,颜色和样式3个参数,不同的是新增了对圆角的处理的参数;

比如我现在需要给一个矩形添加1像素边框,只需要这样:

.demo {
    @include border(
        $border-width: 1px,
        $border-color: red,
        $border-style: solid
    );
}

其中,$border-width的默认值就是1px(回忆我们之前说的,Yo约束使用2种单位,px就用在了这里),所以其实可以省略;包括$border-style的默认值是solid,也可以省略,所以可以写成:

.demo {
    @include border(
        $border-color: red
    );
}

效果如下图:

enter image description here

假设你只需要某个方位上的边线,那么只需要定义$border-width参数即可,和CSS border-width属性相同:

.demo {
    @include border(
        $border-width: 0 0 1px
    );
}

结果如下图:

enter image description here

border方法为什么设计了$radius参数?

我们说过border方法其实是使用了::after来做的,如果只是直接在父元素上设置圆角,那么::after边框的4个角将会被截断,所以border方法的的圆角参数,其实既设置了父元素的圆角,同时也设置了::after的圆角。

来看看对比效果,一个是未使用border方法的圆角参数,一个直接使用border方法$radius: 硬写的:

.demo {
    @include border(
        $border-width: 0 0 1px
    );
    border-radius: .2rem;
}

使用参数的:

.demo {
    @include border(
        $border-width: 0 0 1px,
        $radius: .2rem
    );
}

enter image description here

从图中可以看出,假设不提供该参数,很大概率上会造成一些问题,我们可能会忘记需要多处同时设置圆角,更复杂的是,::after的圆角大小其实不是一个固定值,在不同的dpr是不同的。所以如果需要达到和使用参数的效果,还得手写非常多的媒体查询适配代码。

题外话:以前还怎么做过

border方法是在2.0版本才开始加入的新特性。

在这之前,其实还是用过一种更简易的方式来实现1像素边框的问题:即写一段js,用来获取屏幕的dpr(devicePixelRatio),将该 值动态的添加到root节点上;此时CSS通过媒体查询匹配到不同的dpr,在不同的dpr情况下给root节点定义不同的font-size,然后根据 dpr对viewport进行对应的缩放。

该方案的好处是,业务开发只需要引入这段js即可,不需要修改原有代码;而存在的问题是Android4.3及以下并不支持viewport缩放(initial-scale除1之外的设置);当然还有一个比较严重的问题是,该方案影响响应式设计的实施。

这里只列举以上几个Yo的特性方法,大家可以查看文档上对更多方法的描述。

统一的变量管理

Yo对于变量的处理也是非常有意思的。我们将所有的变量收敛统一管理,通过Map进行分类来组织。这样做的好处是:

  • 分类清晰;
  • 减少全局变量污染;
  • 维护方便;
  • 为配置层提供支持;

如下图所示:

enter image description here

独立的配置层设计

像大多数允许高度自定义的程序一样,Yo可以让开发者在业务代码中根据自己的需求,修改变量定义的初始值。

Yo在lib中通过base和variables这2个文件将所有的变量约束在不同的分类Map中;同时在usage下,有2个用户配置文件extra和config来进行一一对应;

所有base和variables中的定义,都可以在extra和config进行配置,达到更改初始值的目的。

所以,开发者可以在extra和config中按照自己的需求进行配置,比如,因为用户对象不同,可能对浏览器的要求不同,所以可能需要对厂商前缀进行配置,可以在config文件中这么做:

enter image description here

处理厂商前缀

只需要在config中配置$setting Map就行,如下个图,需要-webkit-和-moz-两种厂商的情况:

enter image description here

如果只需要-webkit,移除-moz-即可

enter image description here

假设不想使用厂商前缀,直接将is-vendor-prefix由true改成false就行

enter image description here

灵活配置iconfont

enter image description here

这差不多就是拷贝一个URL的瞬间过程。

开发者可以决定是否使用iconfont,也可以换用不同的字体文件,只需要对上述$ico map进行配置即可。

由于Yo针对的仅仅是移动平台,所以我们只保留woff和ttf2种字体。

你可以在任何需要使用iconfont图标的地方,加上.yo-ico类即可,然后把图标对应的编码写上即可。当然,你也可以在伪元素里去定义这些特定的图标class,然后直接使用,根据实际情况自行选择吧。

响应式方案

Yo只是希望提供响应式的能力,并不打算把stop-points写死,这样的话,业务开发可以随意定义。

enter image description here

所有的响应式断点都定义在$media-types Map中,同时我们构建了一个responsive方法来与之配合,stop-points可以根据具体的需求,自行修改配置,甚至是新增新的stop-point。

上述代码编译结果如下:

enter image description here

丰富的元件

除了上述这些特性之外,Yo还提供了30多种元件。比较有意思的是,Yo将所有所有自提供的有形代码片段称之为元件,这有点类似flash中的元件。

强大的扩展能力

Yo有非常多的相较于其它UI框架的独特设计,包括它的扩展能力。 Yo基本上不会预设(内置)多种形态的某个元件,比如说:它不会预设多种大小或者颜色的某个元件,只有该元件的default风格,即其基本形态,如果业务中有其它形态的需求,都可以通过扩展来实现,这将大大的减少的冗余和无效的代码。

举个例子:

某个项目中,需要5个按钮,这5个按钮可能只是大小和颜色有差异,但它们不会同时出现在一个页面。

你会用什么样的方式来处理呢?

通常可能是这样做:将这5个按钮定义放在一起,比如都在一个btn的文件里;当某个页面需要用到btn时,直接引入这个文件;比如大家较为熟悉的Bootstrap就是采用这种实现。我截了段Bootstrap对按钮是现在的代码如下图:

enter image description here

可以看到的是Bootstrap将btn实现独立抽离出来了,这类似Yo的分层概念。不同的是Bootstrap自身做了预设,它默认提供了6种名称固定的按钮,对于其它元件的处理,Bootstrap也是这样。

这种做法的优点在于按钮风格可以通过变量修改;但问题更大的是:

  • 不够灵活,按钮数量及形式被固定,对于复杂的项目场景难以应付
  • 冗余,所有按钮都会被编译,无法完全做到按需加载(假如有个页面需要用其中的1个按钮,就得引入这个btn文件,但其余5个按钮在这个页面都是冗余的)

Yo是怎样来处理这种问题的

在这点上,Yo与其它框架都有非常大的不同,我将用yo-badge元件来解释Yo是怎么做的,先来看看这个元件的基本形态:

enter image description here

图中红框区域是yo-badge的基本形态,这算是App中经常会使用到的元件了,常用于消息提醒之类的。

然而在Yo里,它可以有任意多种形态的变种,比如:

enter image description here

我们为所有的元件都提供了一个与元件名同名的扩展方法,比如,我想实现上图中的灰色badge,可以通过yo-badge()方法来进行扩展。 扩展:

@include yo-badge(
    $name: normal,
    $border-color: gray,
    $bgcolor: gray
);

所有的同名元件扩展方法,都接受$name参数,用以指定新扩展的元件名,这个名字通过连接线-紧跟在元件名之后,如:yo-badge-normal。 扩展之后如何应用?

<span class="yo-badge yo-badge-normal">9</span>

$name之外的参数,大家可以通过文档查阅。Yo对参数的设计原则遵循的是,只对那些扩展几率高的提供接口,比如:color, border-color, bgcolor, radius等等。

Yo通过元件扩展的方式来解决这种问题。非常好的控制了代码冗余及灵活度,包括清晰的依赖链路,其实都有体现。

现在你可以在需要使用到灰色badge的页面中,通过上述的方法来扩展,至于红色,绿色,黄色的那些,似乎都和你无关了。同时,假设这个灰色的badge是通用的,会在多个页面出现,你可以在将它扩展在某个单独的文件里,然后供需要的页面来引用。

超扩展

我们说了Yo遵循的参数设计原则是只对扩展几率高的提供接口。然后在实际的业务中,这往往可能还不够,你可能还想拥有更为丰富的参数,进行更为深度的自定义扩展。

是的,Yo考虑了你的这种需求,并且设计了能够完全满足这种需求的解决方案。

对于所有的元件同名扩展方法,都拥有超扩展的能力,这是非常酷的一个特性。仍然拿上面那个扩展灰色badge的例子来讲述,假设yo-badge()方法并没有提供扩展margin、font-weight之类的参数,而你的需求要求改变它们,我相信,你会爱上超扩展的:

@include yo-badge(
    $name: normal,
    $border-color: gray,
    $bgcolor: gray
) {
    margin: .1rem;
    font-weight: bold;
}

直接在方法之后加上大括号,然后就可以像写原生CSS一样任性的书写了。 是的,所有的元件都有与之同名的扩展和超扩展方法,是不是很酷。

动画

Yo设计了60多种动画,基本上覆盖了业务对常规动画的需求。这些动画目前大约分成8大类: fade、elastic、flip、roll、speed、rotate、zoom、other 可能大家对Animate这个热度较高的动画库比较熟悉,这样的话那就更好,Yo的动画类型比这个还丰富了一些。

Yo给我们带来了什么?

  • 更低的开发成本
  • 更Cool、更高效的开发模式
  • 统一的代码风格
  • 样式工程化

我们粗放的统计过,使用Yo来开发,大约能提升30-50%的效率,并且大幅降低了出错几率,甚至让我们的JS开发工程师也能愉快的开发页面了。

写在最后

如上述所说,Yo是一个迥异于现在流行框架的。所以,实际上我们并不计划对外提供打包好的 yo.min.css 来供使用,而是推荐直接在 Yo 框架体系中下进行开发。这样你将能体会到上述所说的众多功能和方法为开发所带来的便利,并感受到它的魅力。

更多的特性,离你很近,你只需要用它来写一个简单的应用,胜我千言万语。 开始你的Yo旅程吧。

Thank You

Q&A

问:对google material怎么看?它有可能解决安卓碎片化问题吗?yo有没有计划支持material风格?

杜瑶:其实我不太知道material这个东西,但Yo暂时还不会考虑。因为Yo的设计理念是覆盖大部分,为大部分用户的覆盖做更多的事。后续这个东西覆盖度上来了的话,可以考虑。

问:多行文字省略号怎么用css实现的?

杜瑶:对,这个其实就是-webkit-的一个私有属性实现。

问:相比较js,html和css的发展空间是什么样子的?

杜瑶:这个问题,可能会涉及到大家对职业的规划,所以我会慎重回答,大家请仅做参考。其实,就我本身而言,我并不会去特别强调说哪个最NB,然后就去做哪个。就像我一直和童鞋们说,其实我并不希望大家去研究火箭回收的技术,因为,那对我们来讲,并没有什么用。我更希望大家研究的是如何可以让业务开发更有效率和质量,所以不论是js,css,html或者其他的什么,只要最终是可以有价值的,我都会让大家去做。

问:现在有很多css新特性,如何在移动端使用的时候进行取舍?主要还是兼容问题。

杜瑶:不用一位的追求新特性,要追求的是如何优雅的把问题解决,假设新特性更靠谱,那么就想办法降级或者渐进增强。

问:想问下关于yo的设计思路都参考哪些主流的框架呢,除了bootstrap之外,这个框架大约用了多久成型,多少人维护呢?

杜瑶:其实并没有参考市面上主流的框架,所以才会有很多很有意思的设计。但是有很多启迪是来源于这些流行框架的,我把它们没有解决的痛点,作为了核心要点来设计。

问:可以自定义更细分的宽度来处理响应式吗,而不是yo提供的那五种?

杜瑶:可以,Yo的设计理念就是,不约束用户,只提供能力,所以,只需要在config新增你需要的stop-point就行。

问:在业务诸多的情况下,并且原来就有一套规范或者说规则存在的时候,推广新的一套是否存在阻碍?一般会是怎么样的阻碍,你又如何推广的?是否依赖其他JS框架库,如果依赖是否会导致对方框架库需要重构?

杜瑶:推行新的东西,一定会有障碍,不论是来自于业务上的,还算是来自于领导或者自身的。所以,你索要推行的东西,不能抱着让别人做小白鼠的心理去做,一定要是自己先做过了小白鼠。Yo并没有依赖其他的js框架,但是在去哪儿内部,有一套基于Yo开发的移动组件库。

问:is-vendor-prefix是否增加厂商前缀,是在决定某个项目确定什么使用浏览器的情况下,才能确定出来?,有些项目实际上并不知道最终被那些厂商的浏览器所使用。

杜瑶:这个,其实还是比较好确定,因为Yo基本上是面向mobile和高级浏览器的,所以你假设有疑虑,尽可以把-webkit,-moz-都写上,不过,通常情况下,你的用户群体,你还是知道的,毕竟有用户调研之类的。

问:yo放手给业务定制的地方很多,看上去感觉是个脚手架,提供了很多完善的支持,不过最近组件化浪潮来袭,个人觉得约束性越强可维护越高,开发人员做出业务定制样式重新开发就不需要在意任何细节。以后yo是否会走bootstrap强约束路线?

杜瑶:其实,我所说的不限定,更多指的是不限定用户的扩展,但是其实包括书写和样式的工程化,其实都有很高的约束了,举个例子,在yo里面,所有的元件或者方法,连接线都是“-”,在你使用同名方法扩展后,生成的新元件,默认就也是-了。

问:你们的字体大小是如何处理的?前面你提到根据dpr来设置,那不同的dpr是根据什么来设置,会不会出现不同尺寸大小的兼容性问题

杜瑶:这个其实可以使用Yo提供的响应式来去做,因为Yo其实定义了这些高分屏的断点,但其实,默认的情况下,Yo并没有处理,因为,我们觉得在不同的手机上,字号一致,其实也没太大问题,可以看看我的客户端,都是这样的。

问:提高业务开发效率,是专门团队维护基础组件库,业务线拼装快,还是提供这种规范让开发人员学习并且自己拓展?人的能力有很大区别,就算用了规范代码差异也很大,所以让业务团队只做流水线拼装,样式从已有的选择,感觉也是维护性非常好,业务线代码会很统一,这个瑶姐怎么看?

杜瑶:嗯,其实是的,就像我之前说的,Yo提供了很多方法和元件,基本上大家只需要拼积木就行,如果发现自己的业务和默认的元件不一致,通过元件同名方法进行扩展,但其实都是传参,开发人员甚至都不用写具体的CSS代码,所以基本上可维护性是很好的,并且很统一。我们现在的情形是,使用了Yo之后,基本上介入成本都下降了,因为大家的代码都非常接近。

问:同名扩展函数的超扩展能力是怎么利用sass实现的?

杜瑶:具体可以看一下Yo的源码,主要是利用sass本身的@content特性。

问:一个框架功能越强大,扩展性越好,往往意味着学习成本也越高,yo的学习成本在我看来不能算低,需要非常熟悉sass,掌握二十多个原件的扩展方法估计也不是非常简单的,yo后续发展方向是什么?

杜瑶:Sass只是一个工具,我相信大家都有轻松掌握一个工具的能力。至于说丰富的元件学习成本高,其实还好,因为基本上所有的同名扩展方法的API都差不多,比如:假设是改变前景色,那么都是 $color。

问:yo会支持元件的分开打包吗?小项目其实用不到所有的原件…

杜瑶:只有你用到的文件,才会被编译打包。所以基本上只有你需要的代码,才会被打包进去。