CSS 自诞生以来,基本语法和核心机制一直没有本质上的变化,它的发展几乎全是表现力层面上的提高。最开始 CSS 在网页中的做用只是辅助性的装饰,轻便易学是最大的需求;然而现在网站的复杂度已经不可同日而语,原生 CSS 已经让开发者力不从心。css
当一门语言的能力不足而用户的运行环境又不支持其它选择的时候,这门语言就会沦为 “编译目标” 语言。开发者将选择另外一门更高级的语言来进行开发,而后编译到底层语言以便实际运行。前端
因而,在前端领域,天降大任于斯人也,CSS 预处理器应运而生。而 CSS 这门古老的语言以另外一种方式 “从新适应” 了网页开发的需求。编程
简单梳理一下,CSS 预处理器为咱们带来了几项重要的能力,由浅入深排列以下。(不用在乎你用到了多少,不管深浅,都是获益。)浏览器
页面愈来愈复杂,须要加载的 CSS 文件也愈来愈大,咱们有必要把大文件切分开来,不然难以维护。传统的 CSS 文件切分方案基本上就是 CSS 原生的 @import
指令,或在 HTML 中加载多个 CSS 文件,这些方案一般不能知足性能要求。app
CSS 预处理器扩展了 @import
指令的能力,经过编译环节将切分后的文件从新合并为一个大文件。这一方面解决了大文件不便维护的问题,另外一方面也解决了一堆小文件在加载时的性能问题。框架
把文件切分的思路再向前推动一步,就是 “模块化”。一个大的 CSS 文件在合理切分以后,所产生的这些小文件的相互关系应该是一个树形结构。编程语言
树形的根结节通常称做 “入口文件”,树形的其它节点通常称做 “模块文件”。入口文件一般会依赖多个模块文件,各个模块文件也可能会依赖其它更末端的模块,从而构成整个树形。模块化
如下是一个简单的示例:函数
entry.styl ├─ base.styl │ ├─ normalize.styl │ └─ reset.styl ├─ layout.styl │ ├─ header.styl │ │ └─ nav.styl │ └─ footer.styl ├─ section-foo.styl ├─ section-bar.styl └─ ... |
(入口文件 entry.styl
在编译时会引入所需的模块,生成 entry.css
,而后被页面引用。)工具
若是你用过其它拥有模块机制的编程语言,应该已经深有体会,模块化是一种很是好的代码组织方式,是开发者设计代码结构的重要手段。模块能够很清晰地实现代码的分层、复用和依赖管理,让 CSS 的开发过程也能享受到现代程序开发的便利。
选择符嵌套是文件内部的代码组织方式,它可让一系列相关的规则呈现出层级关系。在之前,若是要达到这个目的,咱们只能这样写:
.nav {margin: auto /* 水平居中 */; width: 1000px; color: #333;} .nav li {float: left /* 水平排列 */; width: 100px;} .nav li a {display: block; text-decoration: none;} |
这种写法须要咱们手工维护缩进关系,当上级选择符发生变化时,全部相关的下级选择符都要修改;此外,把每条规则写成一行也不易阅读,为单条声明写注释也很尴尬(只能插在声明之间了)。
在 CSS 预处理语言中,嵌套语法能够很容易地表达出规则之间的层级关系,为单条声明写注释也很清晰易读:
.nav margin: auto // 水平居中 width: 1000px color: #333 li float: left // 水平排列 width: 100px a display: block text-decoration: none |
在变动出现以前,CSS 中的全部属性值都是 “幻数”。你不知道这个值是怎么来的、它的什么样的意义。有了变量以后,咱们就能够给这些 “幻数” 起个名字了,便于记忆、阅读和理解。
接下来咱们会发现,当某个特定的值在多处用到时,变量就是一种简单而有效的抽象方式,能够把这种重复消灭掉,让你的代码更加 DRY。
咱们来比较一下如下两段代码:
/* 原生 CSS 代码 */ strong { color: #ff4466; font-weight: bold; } /* ... */ .notice { color: #ff4466; } |
// 用 Stylus 来写 $color-primary = #ff4466 strong color: $color-primary font-weight: bold /* ... */ .notice color: $color-primary |
你可能已经意识到了,变量让开发者更容易实现网站视觉风格的统一,也让 “换肤” 这样的需求变得更加轻松易行。
光有变量仍是不够的,咱们还须要有运算。若是说变量让值有了意义,那么运算则可让值和值创建关联。有些属性的值其实跟其它属性的值是紧密相关的,CSS 语法没法表达这层关系;而在预处理语言中,咱们能够用变量和表达式来呈现这种关系。
举个例子,咱们须要让一个容器最多只显示三行文字,在之前咱们一般是这样写的:
.wrapper { overflow-y: hidden; line-height: 1.5; max-height: 4.5em; /* = 1.5 x 3 */ } |
你们能够发现,咱们只能用注释来表达 max-height
的值是怎么来的,并且注释中 3
这样的值也是幻数,还须要进一步解释。将来当行高或行数发生变化的时候,max-height
的值和注释中的算式也须要同步更新,维护起来很不方便。
接下来咱们用预处理语言来改良一下:
.wrapper $max-lines = 3 $line-height = 1.5 overflow-y: hidden line-height: $line-height max-height: unit($line-height * $max-lines, 'em') |
乍一看,代码行数彷佛变多了,但代码的意图却更加清楚了——不须要任何注释就把整件事情说清楚了。在后期维护时,只要修改那两个变量就能够了。
值得一提的是,这种写法还带来另外一个好处。$line-height
这个变量能够是 .wrapper
本身定义的局部变量(好比上面那段代码),也能够从更上层的做用域获取:
$line-height = 1.5 // 全局统一行高 body line-height: $line-height .wrapper $max-lines = 3 max-height: unit($line-height * $max-lines, 'em') overflow-y: hidden |
这意味着 .wrapper
能够向祖先继承行高,而不须要为这个 “只显示三行” 的需求把本身的行高写死。有了运算,咱们就有能力表达属性与属性之间的关联,它令咱们的代码更加灵活、更加 DRY。
把经常使用的运算操做抽象出来,咱们就获得了函数。
开发者能够自定义函数,预处理器本身也内置了大量的函数。最经常使用的内置函数应该就是颜色的运算函数了吧!有了它们,咱们甚至都不须要打开 Photoshop 来调色,就能够获得某个颜色的同色系变种了。
举个例子,咱们要给一个按钮添加鼠标悬停效果,而最简单的悬停效果就是让按钮的颜色加深一些。咱们写出的 CSS 代码多是这样的:
.button { background-color: #ff4466; } .button:hover { background-color: #f57900; } |
我相信即便是最资深的视觉设计师,也很难分清 #ff4466
和 #f57900
这两种颜色到底有什么关联。而若是咱们的代码是用预处理语言来写的,那事情就直观多了:
.button $color = #ff9833 background-color: $color &:hover background-color: darken($color, 20%) |
此外,预处理器的函数每每还支持默认参数、具名实参、arguments
对象等高级功能,内部还能够设置条件分支,能够知足复杂的逻辑需求。
Mixin 是 CSS 预处理器提供的又一项实用功能。Mixin 的形态和用法跟函数十分相似——先定义,而后在须要的地方调用,在调用时能够接受参数。它与函数的不一样之处在于,函数用于产生一个值,而 Mixin 的做用是产生一段 CSS 代码。
Mixin 能够产生多条 CSS 规则,也能够只产生一些 CSS 声明。
通常来讲,Mixin 能够把 CSS 文件中相似的代码块抽象出来,并给它一个直观的名字。好比 CSS 框架能够把一些经常使用的代码片段包装为 mixin 备用,在内部按需调用,或暴露给使用者在业务层调用。
举个例子,咱们常常会用到 clearfix 来闭合浮动。在原生 CSS 中,若是要避免 clearfix 代码的重复,每每只能先定义好一个 .clearfix
类,而后在 HTML 中挂载到须要的元素身上:
/* 为 clearfix 定义一个类 */ .clearfix {...} .clearfix::after {...} |
<!-- 挂载到这两个元素身上 --> <div class="info clearfix">...</div> ... <footer class="clearfix">...</footer> |
把表现层的实现暴露到告终构层,是否是很不爽?而在预处理器中,咱们还能够选择另外一种重用方式:
// 为 clearfix 定义一个 mixin clearfix() ... &::after ... // 在须要的元素身上调用 .info clearfix() footer clearfix() |
CSS 预处理语言没法直接运行于浏览器环境,这意味着咱们编写的源码须要编译为 CSS 代码以后才能用于网页。这彷佛是一个门槛,须要咱们付出 “额外” 的成本。
但在目前的大环境下,大多数项目的前端开发流程已经包含了构建环节,好比选择任何一个脚本模块化方案都是须要在部署时走一道打包程序的。因此对大多数团队来讲,这个门槛其实已经跨过去一大半了。
而一旦接受了这种设定,咱们还能够享受到 “额外” 的福利。在给 CSS 的开发加入编译环节的同时,还能够顺道加入其它构建环节,好比代码校验、代码压缩、代码后处理等等。
“代码后处理” 是指 PostCSS 平台上各种插件所提供的功能,光是 Autoprefixer 这一项就已经值回票价了。咱们不再须要在 CSS 代码中手工添加浏览器前缀了,直接使用标准写法,剩下的事情让工具搞定吧!