有人说是css预处理器巴拉扒拉,简略的说,PostCss是一个工具,提供了一坨插件用来转化样式。
PostCss是一个Node.js的module,将css解析成一个 AST(abstract syntax tree抽象语法树),通过任意插件解析AST并转换成string输出到一个文件中。插件可能改变AST,也可能能不改变AST。其中的变化通过生成sourcemaps去追踪。
PostCss不会改变你的css,有可能改变样式的是插件。开发者可以通过AST提供的接口来编写插件。so 不要被PostCss的名字误导,以为是和Sass之类的预处理器(preprocessors)对应的后处理器(postprecessors)。有小伙伴会问了,pre 和 post-processing的区别是什么呢?
来自维基百科的官方解释
预处理器是程序中处理输入数据,产生能用来输入到其他程序的数据的程序。
我觉得用人话解释的话,css preprocessing是指对某些代码进行解析,最终编译成css。 postprocessing 是对css进行处理,并最终生成css。
postcss plugins里 可以按照这两个维度进行分类。
比如下面隆重介绍普遍被使用的PostCss的插件 autoprefixer,这是一个比较典型的postprecessor。
autoprefixer的作用是 解析css rules,通过Can I Use 网站提供的值来添加浏览器前缀。
a {
display: flex
}
处理后
a {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex
}
autoprefixer 同样可以移除不必要的浏览器厂商前缀
a {
-webkit-border-radius: 5px;
border-radius: 5px
}
处理后
a {
border-radius: 5px
}
有熟悉sass语法的同学们应该想到了,使用了autoprefixer以后 我们不必再使用@mixin
来定义一坨添加浏览器厂商前缀方法。而是直接写不带厂商前缀的简单样式,插件会自动帮你添加你想要的。
scss语法 在线编译网站
//yo的处理方法
$setting:(
is-vendor-prefix:true,
vendor-prefix:-webkit- -ms-
);
@mixin prefix($property, $value) {
// 是否开启厂商前缀支持
@if map-get($setting, is-vendor-prefix) {
// 遍历输出厂商代码
@each $vendor in map-get($setting, vendor-prefix) {
#{$vendor}#{$property}: $value;
}
}
#{$property}: $value;
}
a {
@include prefix(display,flex)
}
编译成css后
a {
-webkit-display: flex;
-ms-display: flex;
display: flex;
}
我的天 通过autoprefixer, 我们可以省略这么多复杂的套路!
autoprefixer使用Browserslist。推荐使用browsersList config or package.json 这样浏览器配置就能被别的工具所分享。具体配置参考browserslist。
接下来在介绍一个preprocessor plugin precss
precss 提供了一个插件包 提供类似sass的语法特性。包含
嵌套 类似sass语法,可以使用&
连接符来代替父元素
.test {
width: 100%;
&::before {
content: '';
}
}
变量 类似sass语法 将$
在变量前面,:
加在值前面
$text_color: #333;
body {
color: $text_color
}
条件判断 类似sass语法,使用@if
和@else
$column_layout:2;
.column {
@if $column_layout == 2 {
width: 50%;
} @else {
width:100%;
}
}
循环 类似sass语法 可以使用@for
和@each
循环。@for
循环需要一个计数器变量用于追踪循环,通常使用$i
。当@for
循环时,变量相当于当前循环项而@each
用来循环a list of items instead of numbers。
@for $i from 1 to 3 {
p:nth-of-type($i) { // 在sass语法中 需要用#{} 包裹$i
margin-left: calc(100%/$i);
}
}
/* after */
p:nth-of-type(1) {
margin-left: calc( 100% / 1 );
}
p:nth-of-type(2) {
margin-left: calc( 100% / 2 );
}
p:nth-of-type(3) {
margin-left: calc( 100% / 3 );
}
$list: foo, bar, baz;
@each $icon in ($list) {
.icon-$(icon) {
background: url('icons/$(icon).png'); //变量插入string中需要用括号将变量名包裹起来。
}
}
/* after */
.icon-foo {
background: url('icons/foo.png');
}
.icon-bar {
background: url('icons/bar.png');
}
.icon-baz {
background: url('icons/baz.png');
}
mixins precss的混合语法和sass有一些区别。在sass中 定义一个mixin 使用@mixin mixin_name($arg1,$arg2){...}
声明,用@include mixin_name(pass_arg1,pass_arg2);
来调用声明的混合宏。在precss中,使用@define-mixin mixin-name $arg1, $arg2 {...}
声明,用@mixin mixin_name pass_arg1, pass_arg2;
来调用。sass 语法使用@content
是@mixin
能接受额外一整块样式(常见使用场景:媒体查询)。而precss 使用@mixin-content
来声明。
// sass 语法
//图片容器 定义图片高/宽比例
@mixin imgratio($width,$size) {
img{
width: $width;
height: 0;
padding-bottom: $size;
overflow: hidden;
@content;
}
}
//图片保持3:2比例
@include imgratio(100%, 66.66%) {
background: red;//@content
}
//precss 语法
@define-mixin imgratio $width, $size {
img{
width: $width;
height: 0;
padding-bottom: $size;
overflow: hidden;
}
}
@mixin imgratio 100%, 66.66% {
background: red;//@content
}
extends precss的继承(或扩展)语法和sass有区别。在precss中使用@define-extend extend_name{...}
定义继承,用@extend extend_name;
调用。而在css中,使用%extend_name{...}
来定义继承,使用@extend %extend_name
来调用
//precss
@defined-extend inline_text_middle {
display: inline-block;
vertical-align:middle;
}
.txt {
@extend inline_text_middle;
}
//sass
%inline_text_middle{
display: inline-block;
vertical-align: middle;
}
.txt {
@extend %inline_text_middle;
}
imports precss导入的语法类似sass。
@import './variables';
@import './mixins';
@import './extends';
到此precss插件的基本语法就介绍完了。前面介绍过 precss是一个插件包,里面包含了很多插件 具体的可以看官方文档
有的小伙伴可能想了,我不想用这么多包我就单单使用imports。没问题!直接使用postcss-partial-import插件就可以满足你。
还有的小伙伴懒癌晚期,”我好好的sass不用,干嘛要搞一套乱七八糟的插件来实现类似sass语法,烦不烦?”
再次review下postcss的本质,postcss是一个提供了插件系统的工具,不是一个预处理器,所以它不是一个特定的语法拓展,我们可以随心所欲的根据自身的需要使用或者不使用插件。so 我们使用sass的原生语法没问题,你只要使用你想要的插件就可以了(可以参考下文postcss配置)。
postcss既然有插件系统,选择恐惧症患者现在就纠结了,如此多的插件我该选择哪个呢?难道要上vip.com才能不纠结。。
官方文档对插件就有很多种分类,建议小伙伴们抽空看一下。同时还有一个网站可以查找插件 postcss.parts,可以再这里搜到你想要的插件。
插件系统的强大在于插件种类的广泛,几乎包含了你开发中能想到的方方面面,而不是仅仅局限于之前介绍的pre || postprecessor 概念!
到目前为止,你可能已经明白了postcss是个什么东东。那么在实际开发中我们应该怎么用呢?
鉴于我们的项目目前基本上都是使用ykit,下面重点介绍webpack中的配置。至于gulp等配置,网上到处都是,这里就不赘述了。
sass
语法, 而是直接使用原生语法,方便熟悉sass
语法的懒癌患者。创建postcss.config.js文件
//postcss.config.js
module.exports = {
plugins: [
require('cssnext'), // 支持实用最新的语法css语法
require('autoprefixer'), // 处理浏览器前缀
require('postcss-utilities'), // 封装一些常用的css代码组合
require('postcss-alias'), // 支持自定义别名
require('postcss-sorting')// 可以根据规则排序css
]
}
在webapck.config.js中使用postcss-loader,具体见上一步。
postcss-loader 设置应该在预处理器loader之前配置,在css-loader或者style-loader之后配置。
//webpack2
module.exports = {
module: {
rules: [
{
test: /\.scss$/,
use: [ 'style-loader', 'css-loader', 'sass-loader', 'postcss-loader' ]
}
]
}
}
推荐使用extract-text-webpack-plugin插件在bundle || bundles 中取出css到单独文件
//webpack2用法
var ExtractTextPlugin = require('extract-text-webpack-plugin');
...
{
test:/\.scss$/,
loader: ExtractTextPlugin.extract({
fallbackLoader: "style-loader",
loader: [
"css-loader",
"postcss-loader",
"sass-loader"
]
})
}
...
plugins: [
new ExtractTextPlugin('[name].css')
]
//webpack1用法
ExtractTextPlugin.extract(
'style-loader',
'css-loader!postcss-loader!sass-loader'
);
ykit使用插件时默认设置了若干loader。使用postcss-loader的话需要替换loader
这个配置中使用ykit-config-qunar插件。
module.exports = {
plugins: [{
name: 'qunar',
options: {
eslint: true
}
}],
config: function(options) {
this.config.module.loaders = this.config.module.loaders.map(function(loader) {
if (loader.test.test('.scss')) {
return {
test: /\.scss$/,
loader: options.ExtractTextPlugin.extract(
require.resolve('style-loader'), //require.resolve用于从模块名取到绝对路径
require.resolve('css-loader') + '?sourceMap!' + require.resolve('postcss-loader') + '?sourceMap!' + require.resolve('@qnpm/fast-sass-loader-china') + '?sourceMap'
//此处使用sass-loader则报错 'Cannot resolve module 'sass-loader',所以按照qunar插件的用法,继续使用fast-sass-loader-china
)
}
}
return loader
})
return {
exports: [
'./scripts/pages/test.js',
'./styles/pages/test.scss'
],
modifyWebpackConfig: function(baseConfig) {
baseConfig.resolve = baseConfig.resolve || {};
// 设置别名
baseConfig.resolve.alias = {
common: 'scripts/common',
libs: 'scripts/libs',
pagesRoot: 'scripts/pages',
pagesCommon: 'scripts/pages/common',
utils: 'scripts/utils'
}
return baseConfig
},
sync: {
host : "192.168.237.75",
path: "/home/q/www/qunarzz.com/vacation_actpackage_backend"
}
}
},
hooks: {},
commands: [],
server: {
hot: true, // ykit server --hot
overlay: true // 在当前打开页面提示打包错误
}
};
经过以上的介绍,小伙伴们应该能看出postcss的功能还是很强大的。痛点在于一个postcss plugin只是实现一个小的功能或者解决一个小的问题:在配置plugins时需要对它们有一定的认知,还需要花费一些时间来配置你需要的plugins。当然你可以使用类似precss此类的插件包来一建生成你需要的环境。不过只要项目init时候约定好了如何plugins,就可以快捷的移植配置。总体来说还是很值得推广使用哒!
我这里写一个基于sass的插件配置,方便懒癌晚期患者服用。(其实上文已经介绍了)
//postcss.config.js
module.exports = {
plugins: [
require('cssnext'), // 支持实用最新的语法css语法
require('autoprefixer'), // 处理浏览器前缀
require('postcss-utilities'), // 封装一些常用的css代码组合
require('postcss-alias'), // 支持自定义别名
require('postcss-sorting')// 可以根据规则排序css
]
}
好处
坏处
推荐插件配置中可以额外添加的插件:stylelint 大家都懂的CSS linter工具