现代 CSS

PostCSS和一些有兴趣的东西

特别声明:如果您喜欢小站的内容,可以点击申请会员进行全站阅读。如果您对付费阅读有任何建议或想法,欢迎发送邮件至: airenliao@gmail.com!或添加QQ:874472854(^_^)

PostCSS是一个很有趣的项目。简而言之,它将CSS转换成抽象语法树(AST),也就是JavaScript可以操作的一种数据形式。基于JavaScript的PostCSS插件可以执行不同的代码操作。PostCSS本身并没有改变你的CSS,它请允许插件执行和转换你的代码。

实际上,对于CSS的操作,PostCSS插件并没有任何限制。只要你有想得到的,你都可以写一个PostCSS插件来实现。

重要的是要知道PostCSS是什么?@Envato-tuts写了一个有关于深入学习PostCSS系列教程,其中有一篇就是关于PostCSS需要知道哪些?

有关于《深入学习PostCSS》,我们也翻译了一些,但整个系列没有译完,如果不喜欢看英文的,可以点击这里

PostCSS不是一个预处理器

是的,你可以绝对地使用它作为预处理器,但你也可以在没有任何预处理器功能的情况下使用PostCSS。比如我只使用Autoprefixer和CSS Nano。而这些都不是预处理器。

PostCSS不是一个后处理器

后处理器常被看作是处理一个完成了的样式表,比如处理CSS样式中的有效的,标准的CSS语法规则,如果发现不符合,做相应的处理。比如说添加浏览器的私有前缀。然而,PostCSS可以做这些事情,但它绝不仅仅是处理文件,只不过是受你使用的插件限制而以。

PostCSS不是新语法

有一些很好的,非常有名的PostCSS插件,允许你在代码中使用未来的语法,也就是说使用将来可用的CSS属性,但这些属性现在还没有得到广泛的支持。然而,PostCSS本身并不是只用来支持未来语法的。

是否使用未来的语法是你的选择,而不是需求。我是使用SCSS的,我做了一些特殊的功能处理,并在一个功能中使用PostCSS。如果我如此选择,我可以转向PostCSS,并且使用未来的特性,而不用担心浏览器是否支持。

PostCSS不是一个清理或优化的工具

Autoprefixer插件成功的导致了PostCSS的普遍看法,认为是在完成了CSS之后再来清理,并且优化的,让属性能得到跨浏览器的兼容性。

当然,有很多很优秀的插件可以提供清理和优化相关的功能,但这些也只是一些可用的插件而以。

我为什么选择PostCSS?

我最初决定不使用PostCSS,直到我发现Autoprefixer和CSSNano一些我喜欢的工具,实际上是PostCSS插件。这让我开始研究PostCSS本身,看看它到底是什么。我发现的是一个基本工具和丰富的生态系统插件,可以做很多你可能想要的事情,比如说你的CSS前添加前缀是根据BEM这样的规则。

我也喜欢@Martin提出的单一责任原则:

单一责任原则是一种计算机编程原理,它指出每个模块或类都应该对应软件提供的功能的一部分责任,并且这个责任应该完全由类来封装。

维基百科中的单一责任原则

基本上,每个PostCSS插件只处理一个任务,并且把这个任务做好。我们不应该创建那些不止一件事情的类,我们也不应该重复使用国一个PostCSS插件来处理已经可以使用的功能。

在这篇文章中,我们将探讨如何使用Gulp构建一个PostCSS工作流,如何构建一个插件以及如保将插件添加到我们已创建好的PostCSS工作流中。

运行PostCSS

我主要在一个Gulp环境中工作,我构建了PostCSS插件中的Autoprefixer这个任务,自动添加浏览器的私有前缀。假设你之前没有做过,那么你需要安装一全局的Gulp:

npm i -g gulp

然后需要安装我们要的插件:gulppostcssautoprefixer。进入你正在开发的项目,像下面那样安装这些插件,其中-D表示将要安装的插件保存为开发依赖项。

npm i -D gulp gulp-postcss autoprefixer

这项任务目前由两部分组成:

  • 使用的处理器列表
  • 任务本身

该任务通过sourcemaps输入,然后运行已配置的PostCSS和Autoprefixer,并配置好浏览器版本。然后,它将sourcemaps和输出写入到目标文件夹中:

gulp.task("processCSS", () => {
    // What processors/plugins to use with PostCSS
    const PROCESSORS = [
        autoprefixer({browsers: ['last 3 versions']})
    ];
    return gulp
        .src("src/css/**/*.css")
        .pipe($.sourcemaps.init())
        .pipe(postcss(PROCESSORS))
        .pipe($.sourcemaps.write("."))
        .pipe(gulp.dest("src/css"))
        .pipe($.size({
        pretty: true,
        title:
            "processCSS"
        }));
});

如果我们在CSS处理过程中最后运行这个任务,我们可以将目标从src/css更改为dest/css,但是这个任务第一次使用的过程有一个额外的任务,就是压缩代码的任务,这是超出了Sass给我们的范围。这里并没有使用CSSNano,所以我必须将文件保存在源目录中进行进一步的处理。当我们讨论可以使用的其他插件时,我们将重新讨论这个问题。

添加第二个插件

我们可以通过Sass的压缩格式来压缩CSS。如果想要压缩更多,那么需要使用CSS Nano插件来做进一步压缩。

要使用它,首先得先安装这个插件:

npm i -D cssnano

接下来需要修改构建脚本:

const cssnano = require('cssnano');

最后,我们需要修改任务,将CSS Nano合并到任务中来。我们通过向处理器数据组添加CSS Nano来实现这一点。修改后的任务如下:

gulp.task("processCSS", () => {
    // What processors/plugins to use with PostCSS
    const PROCESSORS = [
        autoprefixer({browsers: ['last 3 versions']}),
        ccssnano()
    ];
    return gulp
        .src("src/css/**/*.css")
        .pipe($.sourcemaps.init())
        .pipe(postcss(PROCESSORS))
        .pipe($.sourcemaps.write("."))
        .pipe(gulp.dest("src/css"))
        .pipe($.size({
        pretty: true,
        title:
            "processCSS"
        }));
});

我们可以使用相同的方式添加更多的处理器:

  • 安装需要的插件
  • 将插件添加到处理器数组中
  • 配置添加的插件
  • 测试已添加的插件

根据上面的步骤以确保添加的插件能按照你的要求工作。

创建一个插件

我发现,PostCSS最吸引人的地方在于它的API,它可以让开发人员能够轻松地创建插件来满足特定的需求。

CSS代码会是什么样子

例如,假设我们有一组有关于营销方面的字体用于内容中。而不是在堆栈中输入所有的字体,你可以这样做:

html {
    font-family: stack("Arial");
    weight: normal;
    style: normal;
}

编译后得到的CSS会像这样:

html {
    font-family: 'Arial, "Helvetica Neue", Helvetica, sans-serif';
    weight: normal;
    style: normal;
}

配置项目

为了初始化插件项目,我们必须创建一个文件夹,并使用NPM初始化包,并自动接受默认值。我们可以用以下命令来做:

mkdir local-stacks # Creates the directory
cd local-stacks # Changes to the directory we just created
npm init --yes # Inits NPM accepting all defaults automatically

现在我们必须创建一个index.js文件作为我们的插件入口点。我们可以这样做:

touch index.js

或者在文本编辑器中创建文件。我通常使用Visual Studio Code。

编写代码

为了能让插件运行,我们还需要安装两个插件:PostCSS引擎和Underscore,我们将用来合并本地和插件配置。我没有使用ES6模块导入语法(尽管它会让代码变得更简单),那是因为我想使用老版本的Node模块。

然后我们定义了我们想要使用的字体堆栈数组。我们想要用于堆栈的名称是key,而堆栈本身就是value

const postcss = require('postcss');
const _ = require('underscore');

// Font stacks from http://www.cssfontstack.com/
const fontstacks_config = {
    'Arial': 'Arial, 'Helvetica Neue', Helvetica, sans-serif',
    'Times New Roman': 'TimesNewRoman, 'Times New Roman', Times, Baskerville, Georgia, serif',
    'Lucida Grande':, 'Lucida Sans Unicode', 'Lucida Sans', Geneva, Verdana, sans-serif;
}

toTitleCase会转换传递给它的字符串,这样每个单词的第一个字母就大写。我们用来获取字符串的正则表达式有点复杂(这是我第一次看到它的情况),所以我把它拆分了一下:

  • \w匹配的是任何单个字符(相当于[a-zA-Z0-9_]
  • \S*匹配任何非空字符(相当于[^\r\n\t\f ]
  • *量词 —— 在零和无限次次数之间的匹配,尽可能多的返回
  • g修改器 —— 返回所有匹配项(第一次匹配后不返回)

代码如下:

// Credit for this function to http://bit.ly/1hJj9jb in SO
function toTitleCase(str) {
    return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() +
        txt.substr(1).toLowerCase();});
}

我们导出的模块是实际的插件。我们给它取了一个名local-stacks,我们把它定认为函数。这个函数的功能具有:

  • 我们在样式表中使用了walkRules的所有规则,这是PostCSS API的一部分
  • 对于每一个规则,我们使用walkDecls遍历所有的声明,这也是PostCSS API的一部分
  • 我们测试在声明中是否有一个fontstack调用:
    • 通过匹配括号内的值,然后替换任意的引号,得到所请求的fontstack的名称
    • 如果用户没有指定,则使用结果字符串
    • fontstack_config对象中查看字体堆栈的名称
    • 获取在fontstack调用之前的字符串中的任何值
    • 使用第一个字体和字体堆栈的值创建一个新字符串
    • 将新值作为声明的值返回

代码如下:

module.exports = postcss.plugin('local-stacks', function (options) {

    return function (css) {

        options = options || {};

        fontstacks_config = _.extend(fontstacks_config, options.fontstacks);

        css.walkRules(function (rule) {

            rule.walkDecls(function (decl, i) {
                var value = decl.value
                if (value.indexOf( 'fontstack(' ) !== -1) {

                    var fontstack_requested = value.match(/\(([^)]+)\)/)[1]
                        .replace(/["']/g, "");

                    fontstack_requested = toTitleCase(fontstack_requested);

                    var fontstack = fontstacks_config[fontstack_requested];

                    var first_font = value.substr(0, value.indexOf('fontstack('));

                    var new_value = first_font + fontstack;

                    decl.value = new_value;

                    }
                });
            });
        });
    }
});

下一步和结束

这是一个非常简单的插件。你可以看到Autoprefixer和 CSS Nano这样更复杂的例子以及你能用PostCSS做什么。如果你对PostCSS API感兴趣,可以在这里查看到详细的信息

特别注意,你不必重新造轮子。在www.postcss.parts中有一个大型的插件生态系统。我们可以使用这些插件来得到我们想要的结果。 编写自己的插件很有趣,但并不要总是去做练习

一旦你确定了你的CSS项目,你就可以决定需要哪些PostCSS插件。

扩展阅读

特别声明:如查你想在自己的项目中使用PostCSS,但你又不想在互联网的大海中捞不同的PostCSS教程,你可以购买《深入PostCSS Web设计》一书。

深入PostCSS Web设计

本文根据@Carlos Araya的《PostCSS and crazy things you can do with it》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:https://publishing-project.rivendellweb.net/postcss-and-crazy-things-you-can-do-with-it/

大漠

常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《图解CSS3:核心技术与案例实战》。

如需转载,烦请注明出处:https://www.w3cplus.com/preprocessor/postcss-and-crazy-things-you-can-do-with-it.htmlnike air max 1 yellow

返回顶部