时间:2023-09-10 18:30:02 | 来源:网站运营
时间:2023-09-10 18:30:02 来源:网站运营
CSS预编译器三剑客及PostCSS:这篇文章包含两个部分,第一部分是个CSS预编译器:Sass、Less、Stylus,他们之间的对比,第二部分是现在大火的PostCSS。Less & SCSS:.wrap { display: block;}Sass:.wrap display: blockStylus:.wrap display block
Sass最开始通过缩进,空格,换行的形式来控制层级关系,写过Python的同学一定不会陌生,后来又支持了传统的类CSS语法的Scss。Less中规中矩,使用CSS的风格,对新手非常友好,也利于现有项目的迁移。Stylus既可以使用Sass风格的语法来编写,也兼容CSS的风格。Less:@smallFont: 12px;small { font-size: @smallFont;}Sass:$smallFont: 12px;small { font-size: $smallFont;}Stylus:smallFont = 12pxsmall font-size smallFont
Less:@color: red;.content-1 { color: @color;}@color: black;.content-2 { color: @color;}/* 编译出来的CSS*/.content-1 { color: black;}.content-2 { color: black;}
Less中的变量,在声明中使用时,如果出现多次赋值的情况,其会取最后一次赋值的值,这也就是上面的.content-1, content-2中的color都是black的原因。基于Less中变量的这个特性,我们可以很轻易的改变原有组件库或者类库中变量的值,只需要在引入Less文件后,对特定的变量赋值即可。同时也会带来一定的隐患:如果不同的组件库或类库使用了相同的变量名,那么就会出现覆盖的情况,所以应该采用模块化的方式。Sass:$color: red;.content-1 { color: $color;}$color: black;.content-2 { color: $color;}Stylus:color = red;.content-1 color colorcolor = black;.content-2 color color;/* 编译出来的CSS*/.content-1 { color: red;}.content-2 { color: black;}
上面我们可以看到,Sass/Stylus中的变量,如果出现多次赋值的情况,其会取声明前面最近的一次赋值的值,这就是为什么.content-1的color为red,.content-2的color为black的原因。同时,在Sass/Stylus编写的不同组件库或类库中的变量,不会出现冲突,但是这就为通过覆盖变量的值来自定义样式提出了挑战,我们应该怎么做呢?考点来了,Sass/Stylus中提供了"不存在即赋值"的变量声明:Sass:$a: 1;$a: 5 !default;$b: 3 !default;// $a = 1, $b = 3Stylus:a = 1a := 5 b = 3// a = 1, b = 3
细看代码,你一定会明白:使用"不存在即赋值"语法,编译器会检查前面是否定义了同名变量,如果没有定义,执行变量定义;如果前面定义了同名变量,则不会执行这个赋值的操作。因此,我们可以在使用了Sass/Stylus的组件库或类库中使用"不存在即赋值"的方式来定义变量,在引入之前定义好需要同名变量,就能达到目的了。Less:@prefix: ui;@prop: background;.@{prefix}-button { @{prop}-color: red;}Sass:$prefix: ui$prop: background;.#{$prefix}-button{ #{$prop}-color: red;}Stylus:prefix = uiprop = background.{prefix}-button {prop}-color red
变量相关的内容基本上覆盖到了,接下来就是样式的复用了。提到样式复用,必定会提到mixin和继承了。对于mixin,这三个预编译器也都有不同的实现方式:Less:.mixin-style(@color: red) { color: @color;}.content { .mixin-style(red);}Sass:@mixin mixin-style { color: red;}.content { @include mixin-style;}Stylus: mixin-style(color) color color .content mixin-style(red) // or mixin-styls red 透明mixin
在Less中,可以直接引入一个CSS的class作为mixin(这种方式非常不推荐), 同时也提供上面的能够传入参数的mixin;Sass比较中规中矩,通过@mixin和@include的方式定义和引入mixin;Stylus不需要显示的声明mixin,同时还提供透明mixin的功能,就像属性一样引入。Sass/Stylus:.message{ padding: 10px; border: 1px solid #eee;}.warning{ @extend .message; color: #e2e21e;}Less:.message { padding: 10px; border: 1px solid #eee;}.warning { &:extend(.message); color: #e2e21e;}
个中优劣,大家可以自己评判。更多详细的对比,可以查看顾轶灵大神的文章。PostCSS is a tool for transforming styles with JS plugins. These plugins can lint your CSS, support variables and mixins, transpile future CSS syntax, inline images, and more.所以我的理解是,PostCSS是一个使用JS插件来转换样式的工具。PostCSS跟CSS预处理器的定位不同,看了前面的内容,大家都知道CSS预处理器的作用是什么,而PostCSS的作用主要有lint css,支持CSS Next语法,自动添加前缀等等功能,通过插件,基本上可以覆盖CSS 预处理器的功能,同时实现多得多的预处理器实现不了的功能。既然PostCSS这么牛逼,那我们来深入研究一下。
.content{ color: red;}
如果我们检测到某个样式中有"color: red"这样一条样式规则,需要自动增加"background-color: red"这样一条规则。git clone git@github.com:postcss/postcss-plugin-boilerplate.git
node start这个时候会需要你输入一些信息,然后就会自动生成一个插件的模板文件夹,还是进入到这个对应的文件夹下,这个时候就能看到熟悉的package.json,index.js等文件,使用npm安装了依赖之后就可以在index.js文件中编写插件的逻辑了。同样的,我们可以在项目中通过npm安装任意依赖,然后在index.js中通过require引入,这就是背靠JavaScript的优势。
var postcss = require('postcss');module.exports = postcss.plugin('postcss-practice', function (opts) { opts = opts || {}; // Work with options here return function (root, result) { // Transform CSS AST here };});
编写出来的插件代码是这样的:const postcss = require('postcss');const postcss = require('postcss');module.exports = postcss.plugin('postcss-practice', function (opts) { opts = opts || {}; console.log(opts); // Work with options here return function (root) { root.walkRules(rule => { console.log(rule.selector); rule.walkDecls(decl => { if ( decl.prop === 'color' && decl.value === 'red' ) { rule.append({ prop: 'background-color', value: 'red' }); } }); }); };});
非常简单,解释一下:root.walkRules会遍历每一个CSS规则,可以通过rule.selector拿到每一组规则中的选择器,然后通过rule.walkDecls遍历每一组规则中的样式声明, 通过decl.prop,decl.value拿到样式声明中的属性和值。上面判断属性是否为'background-color'、值是否为'red',如果满足条件则向规则中插入一条新的样式声明(这里为了简单,没有考虑是否已经存在background-color声明)。var postcss = require('postcss');var plugin = require('./index.js');function run(input, output, opts) { return postcss([ plugin(opts) ]).process(input) .then(result => { expect(result.css).toEqual(output); expect(result.warnings().length).toBe(0); });}// Write tests hereit('does something', () => { return run('a{color: red;}', 'a{color: red;background-color: red;}', {});});
然后是运行: "npm run test"的结果:关键词:编译