postcss学习
安装
- 任意目录npm init -yes
使用这种方式,项目的名词一定不要和某些库的名词冲突,比如叫webpack,postcss,如果叫这个名词安装相关库时就会报ENOSELF的错误
- 安装webpack
npm i --save-dev webpack webpack-cli
- 安装postcss
npm i --save-dev postcss
- 安装相关loader
npm i --save-dev postcss-loader
- 安装babel相关资源(因为会通过import引入资源,所以需要安装)
npm i --save-dev babel-cli babel-preset-es2015 babel-loader
- 安装mini-css-extract-plugin插件(这个插件是webpack4+版本用来提取css用的插件,与extract-text-webpack-plugin功能相似)
npm i --save-dev mini-css-extract-plugin
基本概念
- postcss是一个用JS插件转换成CSS的工具
- postcss不是预处理器
预处理器是指对css能力增强的功能,添加一些一些本身不是css的功能(比如嵌套、变量),通过处理后能转成普通的CSS,
- postcss不是后处理器
通过一些规则把已有的css进行完善,比如添加浏览器前缀
- postcss是作为一个平台的存在,利用postcss提供的插件可以组合各种不同模块,来构建更为复杂的功能
相关解析器
- css-loader是可以在页面中使用import引入css的能力
- style-loader是把css代码生成style标签,放到head标签中
使用流程
- 新建两个文件夹
src:源文件目录
dist:输出目录
- 在webpack.config.js中设置相关规则
const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
context: path.resolve(__dirname, 'src'),
entry: {
mjs: './main.js',
},
mode: 'development',
output: {
path: path.resolve(__dirname, 'dist'),
filename: "[name].bundle.js"
},
module: {
rules: [
{test: /\.(js|jsx)$/, use: 'babel-loader'},
{test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
"css-loader"
]
}
]
},
plugins: [
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: "[name].css",
chunkFilename: "[id].css"
})
]
};
mini-css-extract-plugin使用的例子
从js中分离css代码
// 在src文件夹下新建main.js,base.css,color.css内容如下
// main.js
import './base.css';
import './color.css';
const base = 'main.js';
...
// base.css
body {font-size: 12px}
...
// color.css
.red {color: red}
在package.json添加如下内容
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "./node_modules/.bin/webpack --config webpack.config.js"
},
运行npm run build,这时在dist目录下,就会看到mjs.bundle.js和mjs.css两个文件,打开mjs.css会发现里面内容就是base.css和color.css的内容
通过pcss中引入样式文件
参考上面的例子,这次我们从js把pcss的内容导出到css中
- 安装postcss-import,使在.pcss文件可以使用@import引入样式文件
npm i --save-dev postcss-import
// 在webpack.config.js中做如下修改
{test: /\.pcss$/, use: [
MiniCssExtractPlugin.loader,
"css-loader",
"postcss-loader"
]},
...
// 在src目录下新建mian.pcss
@import './base.css';
@import './color.css';
...
// main.js下做如下调整
// .pcss可以算是postcss专用格式
import './main.pcss';
const base = 'main.js';
这里修改添加一个对post的配置文件,要不运行时会报错,新建.postcssrc.js也可以叫postcss.config.js
// 暂时没有其他配置,可以把留空
module.exports = {
plugins: []
}
运行后,发现mjs.css内容和之前打包后一致,通过这个例子我们在pcss可以import相关样式文件,下面我们安装一个常用的插件,来演示postcss的配置文件
- 安装autoprefixer
npm i --save-dev autoprefixer
- 修改相关配置文件.postcssrc.js
autoprefixer的参数,基本使用默认配置即可,这里只说browsers参数的设定,因为这关系到最终添加前缀的内容
// autoprefixer相关配置可以参考其官方文档
let postcssConfig = {};
postcssConfig.autoprefixer = {
browsers: ['> 1%', 'ff 3']
}
module.exports = {
plugins: postcssConfig
}
- 修改main.pcss内容如下
b {
border-radius:5px;
}
编译运行后结果为
b {
-webkit-border-radius:5px;
-moz-border-radius:5px;
border-radius:5px;
}
通过这个插件我们很便捷的添加了相关浏览器私有前缀,下面讲下如何设定添加前缀的范围。
autoprefixer的插件参数browsers是利用browserslist功能来决定是否需要添加某些浏览器前缀,在browserslist的文档里我们可以找到详细设定,可以设定针对某些浏览器,或者针对国家,或者针对指定平台,下面列举下在项目中最有可能碰到的浏览器
- Android: Android webview浏览器,一般安卓app进行混合开发,应该都有集成
- iOS: ios的Safari浏览器
- Chrome: 谷歌浏览器
- ChromeAndroid: 谷歌浏览器安卓版
- Edge: 微软的Edge浏览器
- ie: ie浏览器
- Safari: Safari桌面浏览器
- ff: firefox浏览器
- and_ff: firefox安卓浏览器
- and_qq: QQ浏览器安卓版
- and_uc: UC浏览器安卓版
browsers接收的是一个数组,所以可以像例子中那样分开设置,下面的话的意思就是为了适配安卓2.3,ios3.2,Safari3.1,IE10浏览器要添加相关前缀
'> 0%','Android 2.3','iOS 3.2','Safari 3.1','IE 10'
至于 > 0%是指当你不想像上面设置那么繁琐的指定浏览器时,可以直接指定个大概,就是我要支持市面上多少多少比例的浏览器,这个数字前面可以添加普通的运算符 > >= < <=
除此之外还可以加前缀修饰符not表示不在某个范围中,此外还可以使用cover extends或者since进行更细化的设置,包括道指定从哪年开始的什么版本。。。感觉把细化的有点恐怖了,就不去深究更细的设置了,基本使用上面的浏览器标识、大概覆盖范围、和特意排除某个浏览器就能完成基本的设置
如果想去掉关于firefox浏览器的设置,可以先修改支持范围,因为如果是> 0%其实就是全部浏览器支持就不存在筛选的问题
browsers: ['> 1%']
这时在运行,其实发现加在border-radius的前缀,应该是现在市面99%浏览器都支持圆角的样式,为此可以进一步做些测试,验证这其他设置
browsers: ['> 1%', 'ff 3']
...
browsers: ['> 1%', 'ff < 4']
圆角功能是ff4才加的功能,我们指定适配某个浏览器版本,这时就会发现运行后的样式一样会有ff的私有前缀,所以一般我们结合浏览器覆盖范围,再加上对特定浏览器的排除就能完成相关设置
有一点需要注意的,设置范围时需要指定范围,不能直接设置ff或者not ff,这时编译会报错,你需要明确指明版本才可以
结合这个例子,其实也就能看出postcss其实像是一个平台,把可能功能集合到一起,通过配置文件的设置,就能指定编译器对pcss文件如何编译,下面介绍比较常用的插件,让我们可以像编写sass或者less那样编写样式
cssnext
这个插件允许开发人员在当前项目中使用css将来版本可能会加入的新特性,这个就非常类似于写ES6的代码,但是使用babel转成ES5的代码,这个插件包含的功能,一般就已经够用的了(这个插件中其实也包含了autoprefixer,使用了这个插件autoprefixer可以不安装)
- 安装该插件
npm i --save-dev postcss-cssnext
修改postcss的配置文件
// 新增
postcssConfig['postcss-cssnext'] = {};
下面介绍cssnext中主要使用的未来语法
- 自定义属性和变量
允许在 CSS 中定义属性并在样式规则中作为变量来使用它们。自定义属性的名称以“–”开头。当声明了自定义属性之后,可以在样式规则中使用“var()”函数来引用
// 在main.pcss样式修改为
b {
border-radius:5px;
}
:root {
--text-color: black;
}
body {
color: var(--text-color);
}
这时运行,cssnext会报个错误,提示你autoprefixer已经被集成到cssnext中,给你报个警报,所以我们可以这么修改设置
let postcssConfig = {};
// 屏蔽单独对autoprefixer的设置,直接在cssnext进行设置
// postcssConfig.autoprefixer = {
// browsers: ['> 1%', 'ff 3']
// }
postcssConfig['postcss-cssnext'] = {
browsers: ['> 1%', 'ff 3']
};
module.exports = {
plugins: postcssConfig
}
再次运行,编译后的样式文件变为
b {
-moz-border-radius:5px;
border-radius:5px;
}
body {
color: black;
}
- 配合自定义变量可以更加便捷的使用calc计算
:root {
--main-font-size: 16px;
--fontSize: 1rem;
}
body {
font-size: var(--main-font-size);
}
h1 {
font-size: calc(var(--main-font-size) * 2);
height: calc(100px - 2em);
margin-bottom: calc(
var(--main-font-size)
* 1.5
)
}
h2 {
font-size: calc(var(--fontSize) * 2);
}
...
// 转化为
body {
font-size: 16px;
}
h1 {
font-size: 32px;
height: calc(100px - 2em);
margin-bottom: 24px
}
h2 {
font-size: 32px;
font-size: 2rem;
}
- 自定义媒体查询
@custom-media --small-viewport (max-width: 30em);
@media (--small-viewport) {
h1 {font-size: 16px}
}
...
// 转码为
@media (max-width: 30em) {
h1 {font-size: 16px}
}
最大最小宽度,可以使用>= <=代替
@custom-media --small-viewport (width >= 500px) and (width <= 1200px);
@media (--small-viewport) {
h1 {font-size: 16px}
}
...
// 转为
@media (min-width: 500px) and (max-width: 1200px) {
h1 {font-size: 16px}
}
- 自定义选择器
CSS 扩展规范(CSS Extensions)中允许创建自定义选择器,可以使用@custom-selector”来定义自定义选择器
@custom-selector :--heading h1, h2, h3, h4, h5, h6;
:--heading {
font-weight: bold;
}
运行后变为
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: bold;
}
- 样式规则嵌套
减少重复的选择器声明,通过两种方式进行嵌套:第一种方式要求嵌套的样式声明使用“&”作为前缀,“&”只能作为声明的起始位置;第二种方式的样式声明使用“@nest”作为前缀,并且“&”可以出现在任意位置
// 嵌套只能使用&开头,除非前缀有@nest
.message {
font-weight: normal;
& .header {
font-weight: bold;
}
@nest .body & {
color: black;
}
}
运行后
.message {
font-weight: normal
}
.message .header {
font-weight: bold;
}
.body .message {
color: black;
}
- 使用image-set设置不同分辨率下的引用图片
// resolve是postcss-assets的功能,下面有介绍,这里我就没准备那么多张图
.foo {
background-image: image-set(resolve('logo.jpg') 1x,
resolve('logo.jpg') 2x,
resolve('logo.jpg') 600dpi);
}
...
// become
.foo {
background-image: url(resolve('logo.jpg'));
}
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {.foo {
background-image: url(resolve('logo.jpg'));
}
}
@media (-webkit-min-device-pixel-ratio: 6.25), (min-resolution: 600dpi) {.foo {
background-image: url(resolve('logo.jpg'));
}
}
- 使用color函数,设置颜色
在工作时设计师应该都会给定相关的颜色色值,很少需要前端自己来算这个值,而且为了保证整体项目的ui统一,应该统一在一处定义好项目中可能用到的所有色值,在一个统一地方进行维护,尽量避免这种还需要自己计算的情况,会造成样式不好维护的情况
a {
color: color(red alpha(-10%));
}
a:hover {
color: color(red blackness(80%));
}
...
// become
a {
color: rgba(255, 0, 0, 0.9);
}
a:hover {
color: rgb(51, 0, 0);
}
- hwb就是把(hsv的色值转成rgb)
body {
color: hwb(90, 0%, 0%, 0.5);
}
...
body {
color: rgba(128, 255, 0, .5);
}
- gray直接引用某个灰度色值
.foo {
color: gray(85);
}
...
// become
.foo {
color: rgb(85, 85, 85);
}
- 允许使用4位颜色色值,这个色值会被转成rgba的色值
body {
background: #9d9c;
}
...
// become
body {
background: rgba(153, 221, 153, 0.8);
}
- 允许使用新的颜色色值名词
color: rebeccapurple;
...
// become
color: #639;
- font-variant没用过,和字体相关
- filter和svg相关,没用过
- initial值,用来表示某些属性的默认值,不建议用,写样式时,某个属性值应该是明确的,不要使用默认设置这种特性,不用就别设置
- rem,会把rem转成px,兼容老版本浏览器
h1 {
font-size: 1.5rem;
}
...
// become
h1 {
font-size: 24px;
font-size: 1.5rem;
}
- :any-link快速设置:link和:visited属性,如果两者样式一致,可以直接使用
nav :any-link {
background-color: yellow;
}
...
// become
nav :link,nav :visited {
background-color: yellow;
}
- :matches,理解成匹配选择器的语法糖
p:matches(:first-child, .special) {
color: red;
}
...
// become
p:first-child, p.special {
color: red;
}
- :not,理解成多个not选择器的语法糖
p:not(:first-child, .special) {
color: red;
}
...
// become
p:not(:first-child):not(.special) {
color: red;
}
- rga,hsl功能增强,可以接收alpha值
.div1 {
background-color: rgb(100 222.2 100.9 / 30%);
}
.div2 {
color: hsl(90deg 90% 70%);
background-color: hsl(300grad 25% 15% / 70%);
}
...
// become
.div1 {
background-color: rgba(100, 222, 101, .3);
}
.div2 {
color: hsl(90, 90%, 70%);
background-color: hsla(270, 25%, 15%, .7);
}
- system-ui设置通用字体
body {
font-family: system-ui;
}
...
// become
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Droid Sans, Helvetica Neue;
}
暂时处在讨论中,不建议使用的特性
- @aplly引入样式样式集
:root {
--danger-theme: {
color: white;
background-color: red;
};
}
.danger {
@apply --danger-theme;
}
...
// 转码后
.danger {
color: white;
background-color: red;
}
下面介绍一些其他插件
postcss-assets
引用外部资源时,可以通过这个插件设置资源查找路径,简化在样式文件中插入图片的操作
安装
npm i --save-dev postcss-assets
在src目录下新建一个images目录,随意往其中存放一张图片(我使用的是一张logo.jpg的图片)
修改postcss配置文件,新增对postcss-assets路径的设置
// 设置loadPaths指定查找路径
postcssConfig['postcss-assets'] = {
loadPaths: ['src/images']
}
这时如果在main.pcss的文件中,如果这么设置一个类
// 配置resolve,指定在查找路径下,搜索图片
.logo {
background-image: resolve('logo.jpg');
}
...
// 编译后会变成,自动识别了
.logo {
background-image: url('/src/images/logo.jpg');
}
postcss-assets还有其他几项设置,不过感觉不常用:
- basePath: 如果.postcssrc.js不在项目的根目录可以使用这个参数就行修改,比如我这里,就是把配置文件在根目录下,这样我在设置loadPaths时,不用再去考虑其他路径的问题,可以从当前位置,直接设置图片目录为src/images,一般这种配置文件感觉放在根目录最好,这样可以很方便别人查看
- baseUrl: 没特别弄明白这参数是什么意思,好像是在服务器运行时,设置url,不过理解不了,部署到生产环境的代码,肯定时编译后的代码,不能在线上再实时编译吧。。。
- cachebuster: 是否设置缓存,默认是false
// 比如如果设为true,图片后就会加一串值,这个看情况添加吧
.logo {
background-image: url('/src/images/logo.jpg?1637e45dd90');
}
- loadPaths: 设置查找特定目录
- relative: 和URL解析有关,没试出来做什么用的。。。使用默认的false,禁用相对url解析
- cache: 默认为false,如果引入文件没有发生变化,则有限使用缓存文件,感觉应该和cachebuster配套使用,能提升编译速度
另外这个插件,还支持通过图片进行相关设置
- inline是把图片转成base64文件,估计要慎用,如果图片很多的话,会影响编译速度
- width是获取一张图片的宽度,height是获取一张图片的高度
.logo {
background-image: inline('logo.jpg');
width: width('logo.jpg');
height: height('logo.jpg');
}
...
// 运行后
.logo {
background-image: ...base64的图片
width: 493px;
height: 448px;
}
CSS MQPacker在必要时会将你的媒体查询成一个规则
// 如果没有安装这个插件
.widget1 {
width: 100%;
@media (min-width: 30em) {
width: 50%;
}
@media (min-width: 60em) {
width: 25%;
}
}
.widget2 {
width: 100px;
@media (min-width: 30em) {
width: 200px;
}
}
...
// become
// 很明显这里关于min-width: 30em的设定是重复的了,正常应该是要进行合并
.widget1 {
width: 100%;
}
@media (min-width: 30em) {
.widget1 {
width: 50%;
}
}
@media (min-width: 60em) {
.widget1 {
width: 25%;
}
}
.widget2 {
width: 100px;
}
@media (min-width: 30em) {
.widget2 {
width: 200px;
}
}
npm install --save-dev css-mqpacker
修改相关配置项
// .postcssrc.js新增下面内容,启用该插件
postcssConfig['css-mqpacker'] = {};
再次运行编译后如下
.widget1 {
width: 100%;
}
.widget2 {
width: 100px;
}
@media (min-width: 30em) {
.widget1 {
width: 50%;
}
.widget2 {
width: 200px;
}
}
@media (min-width: 60em) {
.widget1 {
width: 25%;
}
}
cssnano对编译后的代码进行压缩
因为我使用的是webpack4+以上的版本,这个版本已经集成了cssnano,如果使用时提示不存在,可以通过下面进行安装
npm install --save-dev cssnano
webpack4+以上的版本,基本不需要对cssnano进行配置,直接把模式改成生产模式,打出的样式就是压缩的,如果需要配置,可以按如下修改配置文件
// 整个配置文件
let postcssConfig = {};
postcssConfig['cssnano'] = {preset: 'default'};
postcssConfig['postcss-cssnext'] = {
browsers: ['> 1%', 'ff 3']
};
postcssConfig['postcss-assets'] = {
loadPaths: ['src/images'],
cachebuster: true
};
postcssConfig['css-mqpacker'] = {};
module.exports = {
plugins: postcssConfig
}
再次运行样式代码就进行压缩了,postcss的运行是讲究顺序的
关于Rucksack的使用,Rucksack是封装了不少好的相关插件,不过这个东西毕竟不是标准,别人来看代码的时候还要对Rucksack的写法要属性才能了解是做什么的。
// 比如下面的内容,如果你不了解Rucksack会很懵,完全不了解这个是做什么的
.foo {
font-size: responsive;
}
.bar {
position: relative 20% auto;
}
相比较而言,我个人很推荐使用cssnext,因为cssnext基本就是实现了w3c组织定义的css新规范,这些规范在未来很有可能直接被浏览器支持,而通过cssnext的插件也会把这些功能转成现在浏览器可识别的样式代码
不建议使用 CSS模块化
我对此有比较强烈的抵触感,使用这种方式就会把重构和逻辑开发混在一起,本来重构只需要管理好自己的样式代码,现在还要把留意改动个类名是否会影响到逻辑代码
// 比如下面这些代码
:global .title {
font-size: 20px;
}
.content {
font-weight: bold;
}
...
// 如果经过转换,可能会变成
.title {
font-size: 20px;
}
._content_6xmce_5 {
font-weight: bold;
}
...
// 再借由一些其他处理,我们在页面中很可能是这样使用代码
<div css-module="header.content">Hello world</div>
...
// 然后转意出来后代码变为
<div class="_content_6xmce_5">Hello world</div>
也就是说,现在重构在写样式时,不能再考虑修改content的类名了,因为一修改对应的header.content就找不到这个名词了,虽然在实际开发中这种场景其实并不多,重构定义好的类名也是不会随意修改的(而且也可以通过一些规范规则避免这些情况),但是这种为了解决一个问题,而可能引出另外一个问题的行为是否好呢(比如),如果按项目,页面,模块这几个维度设置好样式嵌套逻辑,是否可以很完美的解决样式组织的问题呢?
避免编译器对pcss文件报错
我使用的是我使用的是vscode,引用插件后写的样式,编译器基本是不认识的,会给你报错,所以这里说下如何避免对pcss的报错,其他编译器应该也有类似的方法
对vscode添加对.pcss文件的支持
- 安装postcss-sugar-language插件
- 进入setting,搜索files.associations在其中添加如下内容
"*.css": "postcss"
同时设置"postcss.validate": false,避免检查器对pcss进行检查