原文链接:https://tylergaw.com/articles...
译者:Icarus
邮箱:[email protected]
多年来我一直使用Sass.但是最近我想要使用PostCSS和它的cssnext插件来尝试处理样式.我爱死了现在就可以使用将来的CSS特性,相对于之前我用的工具,它们更顺手一些.我的个人站点就是尝试新特性的最好的测试地.
第一步是列出我Sass用法的清单.我需要知道我使用了哪些特性,并且确信新特性在postCSS中有替代品.以下是我正在这个项目中使用的特性:
部分引用(partial import)
变量(variables)
嵌套(nesting)
混合宏(mixins)
拓展(extend)
占位类(placeholder classes)
颜色函数(darken and rgba color functions)
压缩(compression)
准备工作
在切换到新语法之后我需要做一些准备.现在项目的目录结构是Sass的典型用法.我用下划线(_
)来命名文件,文件的拓展名为scss
.我使用两个文件夹来组织Sass文件.moudules
文件夹保存不直接产生CSS的Sass文件,像是变量、占位类和混合宏.partials
保存编译出CSS的Sass文件.
这是最初的文件结构:
css/
scss/
modules/
_module.scss
...
partials/
_partial.scss
...
tylergaw.scss
每个Sass组件会在tylergaw.scss
中引入.
@import "modules/setup";
@import "modules/reset";
@import "modules/fonts";
我重新组织并且重命名了文件.我先把所有文件的后缀名从scss
改为css
.我使用了一个Bash脚本来完成这项工作,而不是一个一个修改.
`for f in *.scss; do git mv -- "$f" "${f%.scss}.css"; done;`
前面的下划线是编写Sass的习惯所以我也去掉了它.我没办法使用Bash命令一次性完成,所以只能手动每个去修改.
最后一步就是将所有的CSS文件都移动至modules
文件夹并且删除partials
文件夹.我认为将所有CSS都当成modules来管理要比将他们按照moudules/partials拆分更容易理解.
环境搭建
我以PostCSS CLI为起始,在package.json
里添加了一个临时的构建脚本命令:
"scripts": {
"postcss": "postcss -o public/css/tylergaw.css src/css/tylergaw.css"
}
在没有更改任何样式的情况下我编译了CSS:
`npm run postcss`
正常工作.控制台没有报错,但是页面上没有任何CSS样式.
构建过程是可用的,现在的任务是把样式找回来.
在Chrome的控制台里我看到了很多404信息.这表示我们第一个丢失的特性就是内联@import
.tylergaw.css
通过@import
来引入CSS模块.浏览器看到这些,知道它要做什么.浏览器会通过HTTP请求来加载每个模块.我的构建过程只复制了一个独立的CSS文件,而不是每个模块.正因如此,浏览器找不到它们.
我可以改变构建过程来让默认的@import
工作,但那样效率很低.我需要一个Sass样式内联@import
的替代品.
第一个插件
postcss-import
插件可以代替Sass中的@import
,在通过npm安装之后,我更新了构建脚本代码:
"scripts": {
"postcss": "postcss -u postcss-import -o public/css/tylergaw.css src/css/tylergaw.css"
}
再次运行npm run postcss
,单个的CSS文件就包含了所有模块.现在的页面就展示出了部分样式.
这会是CSS的未来吗?
在Sass中展现出内联方式的@import
功能是非常强大的.它让我们能更好的组织样式.我不确定将来这个功能会不会原生支持.我们使用这种功能时总是需要一步编译,看起来也不坏.
我想postcss-import
插件会成为我PostCSS的一个主要配置,对其他人来说应该也一样.下面引用了插件作者的看法:
This plugin should probably be used as the first plugin of your list. This way, other plugins will work on the AST as if there were only a single file to process, and will probably work as you can expect.
[postcss-import](https://github.com/postcss/postcss-import#postcss-import)
cssnext
cssnext是PostCSS中一个插件,用于将未来CSS特性编译为现今支持的特性.特别需要指出,它和Sass或Less并非不同的语言.它提供正在进行中的CSS规范的特性.一些特性已经得到浏览器支持.另外一些还处于规范的初始阶段.
我使用cssnext来填补失去的Sass特性留下的鸿沟.
浏览器私有前缀
在构建这个网站之前我了解过Autoprefixer.我用自定义Sass混合宏来解决添加所需要的前缀的问题.cssnext包含了Autoprefixer,所以我可以将这整个混合宏模块移除.
变量
下一步我将Sass变量改为CSS自定义属性.比如在_setup.scss中,我这样写:
$grey: #1e1e1d;
$yellow: #ffad15;
$offwhite: #f8f8f8;
$darkerwhite: darken($offwhite, 15);
这不是所有我使用的Sass变量,但是主要就这些.剩下都在独立的模块中.
注意: 自定义属性和变量的区别.CSS自定义属性只在属性值有效,不能用于选择器,属性名或媒体查询.
新的setup.css
:
:root {
--white: #fff;
--grey: #1e1e1d;
--yellow: #ffad15;
--offwhite: #f8f8f8;
...
}
以下为使用示例:
a {
color: var(--yellow);
}
除了语法,CSS自定义属性和Sass变量工作方式是相同的.由于浏览器支持的限制,自定义属性值仍然需要编译.在上面的示例中,编译后的值为color: #ffad15
.
颜色函数
在之前的例子中,我遗漏了一个变量:$darkerwhite: darken($offwhite, 15);
.这是另一个我需要寻找替代的Sass特性.这里有一个规范草案提供CSS颜色函数.cssnex现在包含这些函数,这非常酷.下面是setup.css
,其中darkerwhite
自定义属性是通过颜色函数和阴影调节器来实现的.
:root {
...
--offwhite: #f8f8f8;
--darkerwhite: color(var(--offwhite) shade(20%));
...
}
颜色函数提供了许多调节器.你可以在一个函数中使用多个调节器:
`background-color: color(#d32c3f shade(40%) alpha(40%));`
编译结果为:
`background-color: rgba(127, 26, 38, 0.4);`
再次重申,现在cssnext会将color()
编译为16进制或rgba的色值.当颜色函数得到浏览器支持后,编译过程就没有必要了.颜色操作在运行时就可以发生.
嵌套
嵌套是CSS预处理器不可或缺的特性.任何让人舒服的样式工具的必需品.Tab Atkins对CSS嵌套有一个正在进行中的规范,并且cssnext让它成为现实.
CSS的嵌套语法包含一个前置于内层的&
,以下为sass片段:
.projects-list {
...
li {
& > div {...}
}
a {
...
&:hover,
&:focus {...}
&::after {...}
}
@media (min-width: 640px) {...}
}
对于CSS嵌套,我将它修改为以下形式:
.projects-list {
...
& li {
& > div {...}
}
& a {
...
&:hover,
&:focus {...}
&::after {...}
}
@media (min-width: 640px) {...}
}
基本的嵌套需要前置的&
.伪类和选择器在Sass和CSS中是相同的.媒体查询不需要前置&
.
另外值得注意的是@nest
.正如文档中提到的,复杂的嵌套可能需要引入@nest
来代替&.这个项目我还没有用到,或许将来用得到.
拓展和占位类
Sass中的@extend
和占位类是我经常使用的两个特性。下面是Futura头部的样式示例:
%futura {
font-family: 'futura-pt', helvetica, sans-serif;
}
%futura-heading {
@extend %futura;
font-weight: 700;
line-height: 1.1;
text-transform: uppercase;
}
这是一个用例:
.my-heading {
@extend %futura-heading;
}
我在之前了解过CSS自定义属性的用法。这里有一个正在进行中的@apply
规则的规范与之相关。@apply
允许储存一系列的属性并且在选择器引用。我用@apply
来代替Sass的extend
.
回到setup.css
来,我更新了Futura头部的属性:
:root {
...
--franklin: {
font-family: 'futura-pt', helvetica, sans-serif;
};
--franklin-heading: {
@apply --franklin;
font-weight: 700;
line-height: 1.1;
text-transform: uppercase;
};
}
这里是一个示例:
.my-heading {
@apply --franklin-heading;
}
@apply
不是继承.在目前的cssnext中,@apply
将属性和值直接复制到每条规则中.这是个小项目所以没问题.但是在大型的项目中,可能会导致样式冗余,项目非常臃肿.这种情况下最好还是使用通用类名来适用相似情况.
现在我的网站看起来和之前一样了.项目页是个例外.它的每个磁贴区域都有不同颜色.接下来我会解释怎么在没有Sass的情况下正确且高效的编写样式.
带参数的混合宏
我用Sass的混合宏来让项目编写样式更简便.这个混合宏有一个磁贴颜色的参数.以下是这个project-block
的混合宏.
@mixin project-block ($c) {
background-color: $c;
a {
color: $c;
&:hover {
background-color: $c;
color: $offwhite);
}
}
}
下面是一个示例:
.p-jribbble {
@include project-block(#ff0066);
}
在写这篇文章的时候,我还没有在CSS找到能模拟这个功能的特性.自定义属性配合@apply
不是函数,所以我们不能为它传递参数.在将来,自定义选择器可能会允许使用参数.在草案规范中有一个看起来很有前途的复杂示例.但我承认现在我还没完全明白它是怎么工作的.
这不意味着我运气不好.我写CSS的时间要长于Sass,但也没多久.我还用了另一个正进行中的规范特性,matches选择器.
下面是一个代替project-block
混合宏的CSS示例:
.p-jribbble,
.p-jribbble a:matches(:hover, :focus) {
background-color: var(--color-jrb);
& a {
color: var(--color-jrb);
}
}
颜色变量是早些在文件中:root
作用域定义的.cssnext将以上CSS编译为:
.p-jribbble,
.p-jribbble a:hover,
.p-jribbble a:focus {
background-color: #ff0066
}
.p-jribbble a,
.p-jribbble a:hover a,
.p-jribbble a:focus a {
color: #ff0066;
}
最后两个选择器...a a:hover
和...a a:focus
匹配不到任何元素.他们是不必要的.但是除了占用几比特的空间他们也没有任何影响.为了代码的可读性,我更倾向于a
选择器的嵌套.
更多PostCSS特性
为了样式按顺序回归,我决定利用更多的PostCSS插件.我用css mqpacker来合并使用相同查询条件的媒体查询.我也用cssnano来优化代码.
这也是为什么我期待去使用PostCSS.使用Sass的时候我感觉困在当前的特性中.但因为PostCSS本质是一个插件集合在工作,更具拓展性.如果我有特殊需要,我可以自己来写一个插件.它的潜力令人兴奋.
我妥协了
在使用这个新工具工作了几天后,我完全投入进去了.从Sass转向新的CSS语法非常简单,并且是在五六年间我每个项目都用Sass编写的情况下.
我喜欢这个思想转变.cssnext对CSS的处理很像Babel对Javascript.它们都允许你去使用未来的特性来编写代码.