这篇文章翻译自Bootstrap作者之一:Mark的博文。Mark目前在GitHub工作,他在这篇文章中分享了GitHub在CSS方面使用的相关技术。如果你对CSS感兴趣,但还不熟悉它,推荐看看这篇文章《快速学习和实践CSS/CSS3的在线教程》。另外,Mark在文章中提到了很多工具和技术概念,并直接链接到官网。在译文中,我修改了部分链接,指向了中文介绍页面(含官网地址),方便大家快速地了解。这篇文章涉及到很多方面,如果你发现翻译不恰当的地方,请在评论中指正。谢谢!
以下是译文正文
对其他产品的开发细节,我一直都非常感兴趣,尤其是他们的样式指南和使用CSS的方式。所谓己所欲施于人,于是我决定分享一些关于GitHub CSS方面的东西。
GitHub CSS 概览
- 预处理器选择了SCSS
- 远超过100多个的独立样式源文件,这些源文件编译后再发布到生产环境。
- 为了规避IE<10的样式规则限制(详见补充1),编译后的CSS分割成2个样式文件。
- 两个样式文件总共的大小在90KB左右。
- 没有用到特别的“CSS 架构”。
- 使用px(像素)作为单位,同时有些地方也用到了些 em。
- 使用 Normalize.css 和 我们自定义的CSS重置样式。
预处理器
前面已经提到了,GitHub使用SCSS作为预处理器。早在我加入GitHub之前就选择了它,尽管Boostrap目前使用的是LESS,我对SCSS也觉得Ok(译注:本文的作者Mark Otto是Bootstrap的作者之一)。我们的SCSS使用了Rails asset pipeline编译,同时也用上了Sprockets来检查包含文件的依赖关系。(译注:如果你对Bootstrap感兴趣,这里关联推荐一门免费的在线课程《玩转Bootstrap》)
那么LESS,Stylus,或者其他的预处理器呢?我并不认为LESS之前在GitHub作为预处理器的一个备选,但我确实不好去评价这个。目前来看,我们可能也不会去切换,毕竟没有什么显而易见的益处。
为什么要用预处理器?我们内部的框架包含了一系列相关的变量(例如:font stacks, brand colors)和 混合(mixins),可以让你更简单、更快捷地编写代码。
因为Autoprefixer基本上让我们现有的mixins全部都失效,我们目前还没有使用Autoprefixer,但真应该用起来。希望我们能尽快用上它。
我们目前也没有使用Source Maps,但很快会有所改变。(注:Source Maps很酷,它可以提供将编译和压缩的样式文件映射到源文件原始位置)
除此之外,我们使用SCSS提供的工具非常有限,包括:颜色变量, mixins, 颜色函数, 数学函数 和 嵌套。我们确实不需要class的迭代, 设置全局配置项 和 其他的部分。
模式(原文是Architecture,这里翻译成模式更贴切)
常用的CSS模式包括BEM和OOCSS。我们偏向于OOCSS,但我们没有整体的去研究。我们倾向于采用基本的方式来编写CSS代码:(译注:刚开始听到CSS架构时,有点疑惑。CSS怎么也有架构?看过BEM和OOCSS后,才清楚CSS架构其实是编写CSS代码的一种模式。)
- 在选择器(selectors)中,把优先使用类(classes)
- 避免没有必要的嵌套
- 在class name中使用单破折号
- 尽可能保持简短,避免困惑
我以后会在其他文章中写更多关于我推荐的CSS模式。这篇文章中,上面这几点概括了GitHub团队编写CSS的规范,虽不完美,但基本意思都到了。
CSS检测 (Linting)
我们是几周前才开始检测我们的SCSS (SCSS-lint)。即使我们有了足够通用的惯例,每个人的样式和格式都多少有些独特。现在,每个持续集成(CI)构建包括基本的SCSS检测,出现一下情况就会报错:
- 在你的CSS有一个class,但在 app/views/ 模板中没有用到。
- 相同的selector(选择器)使用了多次(它们应该合并到一起)。
- 违反了常规的格式化规则(嵌套限制,rulesets之间的换行,:s后面缺少空格,等)
总而言之,上面这几个规则让我们的代码保持整洁。
两个包(Two bundles)
GitHub.com 有2个CSS包, github 和 github2。几年前,这个拆分是为了解决IE 4095 个选择器限制的问题,针对的是 IE9及IE9以下的版本。目前,GitHub要求访问的IE浏览器是IE 9和IE 9+版本,所以,这个拆分仍然必须保留。
在GitHub的这两个CSS文件中,目前有7000左右的选择器。下面我们对比一下其他网站或框架有多少个选择器。
- Bootstrap v3.2.0 :~1900
- Twitter:~8900
- NY Times: ~2100
- SoundCloud: ~1100
这些数据是借助cssstats获取到的。这是一个很棒的小工具!
通过Sprockets include
GitHub的CSS和JS都是通过Sprockets和require来include。我们通过app/assets/stylesheets目录下面的不同子目录,维护着我们的2个CSS 包。
/*
= require primer/basecoat/normalize
= require primer/basecoat/base
= require primer/basecoat/forms
= require primer/basecoat/type
= require primer/basecoat/utility
= require_directory ./shared
= require_directory ./_plugins
= require_directory ./graphs
= require primer-user-content/components/markdown
= require primer-user-content/components/syntax-pygments
= require primer/components/buttons
= require primer/components/navigation
= require primer/components/behavior
= require primer/components/alerts
= require primer/components/tooltips
= require primer/components/counter
= require primer-select-menu
= require octicons
= require_directory .
*/
Primer 是我们内部的一个框架,我们先include了它们,然后导入了整个目录的SCSS文件(顺序由Sprockets决定,我相信是按字母先后顺序)。通过 require_directory . 来包括我们的样式表很棒,但也有点糟糕。
样式的生效顺序也很重要。这其实真不应该,但在每个设计系统,都有一些规则和基本的样式层级关系。使用Sprockets,我们有时会陷入一些怪异的问题。之所以会遇到这种问题,是因为新的文件在任何时候都可以加入到其中任何一个bundle。取决于它们的文件名,它们将出现在已编译的CSS文件的不同的地方。
另外,使用Sprockets意味着你的SCSS文件不能立即和自动地访问你全局的变量和mixins。也就意味着你不得不在那些引用了一个变量或者mixin的文件顶部,显示地 @import 它们。
不断地被这些偶然出现的怪异问题困扰后,我们提交了一个 request,改成显式地 @import。增加的好处是更加清晰地洞察我们的CSS,更方便地尝试上述提到的bundles。以此同时,不好的一面是不得不去维护一个列表,但我却认为这样增加了对整个系统的掌控。
性能
Bundle大小和选择器的统计图
我们内部使用很多图形来监控网站和API的运作。我们也跟踪一些有趣的前端状态数据。比如:这是我们两个CSS bundles在过去3个月的大小变化。
类似的统计,这是在我们的blob(file)页面的选择器,最近一个月的数量变化。很显然,我们仍然有很多工作要做,以降低 tag selectors。
因为我们定期第部署更新的CSS(每天部署几十次),我们经常把超大CSS文件上的的缓存弄爆掉。我们还没有怎么去优化这些文件的大小或者限制cache busting,但已经开始深入去解决这些问题。
在Twiiter我们曾经有2个bundles,core和more(不知道现在是否还有)。core确保发出第一条tweet所需要的样式尽可能地少,而其他的样式则放到more。在GitHub,我们对bundle的划分有点武断,
一般来讲,selector的性能并非我自己所关注的。我们意识到一些糟糕的实践方式:嵌套,ID,元素等等。但我们并没有去过度优化。其中一个例外就是在 diff 页面。由于在显示diff时需要扩展的描述,我避免类似这样的属性选择器[class^=”octicon”]。如果过多地使用,这些属性选择器会导致浏览器崩溃。
GitHub的一位同事,Jon Rohan,有一个和GitHub CSS性能有关的演讲,非常棒!感兴趣的可以从这里看看。
文档
提到文档,我们目前做的还凑合,但已经在做一些改进。我们有一个对外公开访问的CSS样式指南,写CSS的通用规则和一些组件实例都放在那里。指南用KSS生成。
I还不完善,但可以给大家(这里应该指的是GitHub的团队成员)作为参考。尤其是新员工指导方面,想他们展示我们如何把事情做的更高效(就像我2年前加入GitHub一样)。
Primer
我之前提到了Primer,如果大家还不了解,我介绍一下:Primer是我们内部的一个框架,用于那些公开或者内部的应用的公共样式和组件。包括:
- Normalize
- 盒子大小,排版,链接等方面的全局样式
- 导航
- 表单
- 格子系统
- Markdown样式
- 自定义选择菜单
Primer在GitHub.com, Gist和几个内部的APP中都用到。一般来讲,如果一个样式能够(或者应该)在其他的应用中使用,我们会考虑把它加入到Primer。
Primer大部分的组件都放到了我们的样式指南中(比如:导航条、工具提示条等)。不过,最近我在改进Primer,样式指南中的很多内容不稳定,但我们会尽快更新,把Primer最新的内容包括进来。
有人问Primer是否能开源,我也希望如此,但是一时半会还无法将Primer开源。
重构
在GitHub,我们的代码(包括CSS)都是共享的。和大型的开源项目不同,我们没有严格的版本规则,我们会定期的清除垃圾代码。通过以下两种方式来找到删减的部分。
手工找到那些看起来类似,但却用着不同的HTML和CSS的东西,然后合并它们。
通过运行一个脚本在CSS中查找一个class,看看它在views在是否已经存在。(最近,我们把这个加入到CI测试中,现在经常会看到它)
重构CSS的实际过程并不独特。发现并清楚垃圾代码,提交一个PR(pull request),通知CSS团队,尽快发布。至少说谁删除代码,团队的任何人都可以。
伯乐在线补充:
1)IE<10样式规则限制。详见这里
对于IE 10 以下版本的IE浏览器,样式表有以下限制
- 单个样式文件最多只包括 4095个规则
- 单个样式文件最多只能 @import 31个外部样式
- @import 最多嵌套4层