postcss 是 css 的 transpiler(转换编译器,简称转译器),它对于 css 就像 babel 对于 js 一样,能够做 css 代码的分析和转换。同时,它也提供了插件机制来做自定义的转换;
原理分析
postcss 是 css 到 css 的转译器,它也和 babel 一样,分为 parse、transform、generate 3个阶段。各种转换插件都是工作在 transform 阶段,基于 AST 做分析和转换。
css的AST
atrule:以 @ 开头的规则,比如:
@media ``screen and (``min-width``: ``480px``) {`` ``body {`` ``background-color``: lightgreen;`` ``}``} |
|---|
rule:选择器开头的规则,比如:
ul li {`` ``padding``: ``5px``;``} |
|---|
decl:具体的样式,比如:
padding``: ``5px``; |
|---|
可以通过 astexplorer.net 的网站进行查找解析
postcss插件的写法(webpack工程实践)
postcss-loader可以结合css-loader使用,也可以单独使用,也就是说不配置css-loader也可以达到相同的效果。唯一不同的是,单独使用postcss-loader时,不建议使用css中的@import语句,否则会产生荣誉代码,因此官方还是推荐将postcss-loader放在css-loader之后使用
自动前缀功能(在线网站)
依赖包:postcss-loader、Autoprefixer或者PostCSS Preset Env
简单介绍:
Autoprefixer:自动获取浏览器的流行度和能够支持的属性,并根据这些数据帮你自动为 CSS 规则添加前缀
PostCSS Preset Env:帮你将最新的 CSS 语法转换成大多数浏览器都能理解的语法
注:PostCSS Preset Env 实际预设了很多好用的css插件,完全可以替代autoprefixer使用,但是两者不同时使用,可能引发冲突
关键配置
webpack.config.js里关于css处理的配置
`loader配置`
`{`
` ``test: /.css$/,`
` ``use: [`
` ``// 'style-loader',`
` ``{`
` ``loader: MiniCssExtractPlugin.loader,`
` ``options: {`
` ``publicPath: ``'./'``,`
` ``}`
` ``},`
` ``'css-loader'``,`
` ``'postcss-loader'`
` ``]`
`}`
`插件配置`
`plugins: [`
` ``new` `HtmlWebpackPlugin({`
` ``template: ``'index.html'``,`
` ``}),`
` ``new` `MiniCssExtractPlugin({`
` ``// 这里的配置和webpackOptions.output中的配置相似`
` ``// 即可以通过在名字前加路径,来决定打包后的文件存在的路径`
` ``filename: ``'css/[name].css'``,`
` ``})`
` ``],`
postcss.config.js(当webpack配置里加入postcss-loader)
`const` `autoprefixer = require(``'autoprefixer'``);`
`const` `preset = require(``"postcss-preset-env"``);`
`module.exports = {`
` ``plugins: [`
` ``autoprefixer,`
` ``// preset`
` ``]`
` ``};`
.browserlistrc浏览器兼容范围配置(浏览器兼容谁还写last 2 version) 浏览器兼容详细介绍
`last ``100` `version`
启动webpack工程看下效果
未开启postcss
`div {`
` ``background: linear-gradient(to bottom, white, skyblue)`
`}`
`div{`
` ``background: #eff0f0;`
` ``padding: 1rem;`
` ``border-radius: 8px;`
` ``user-select: none;`
` ``transform: rotate(0deg);`
` ``}`
开启postcss
`div{`
` ``background: #eff0f0;`
` ``padding: 1rem;`
` ``-webkit-border-radius: 8px;`
` ``border-radius: 8px;`
` ``-webkit-user-select: none;`
` ``-moz-user-select: none;`
` ``-ms-user-select: none;`
` ``user-select: none;`
` ``-webkit-transform: rotate(0deg);`
` ``-ms-transform: rotate(0deg);`
` ``-o-transform: rotate(0deg);`
` ``transform: rotate(0deg);`
` ``}`
` `
`div {`
` ``background: -webkit-gradient(linear, left top, left bottom, from(white), to(skyblue));`
` ``background: -webkit-linear-gradient(top, white, skyblue);`
` ``background: -o-linear-gradient(top, white, skyblue);`
` ``background: linear-gradient(to bottom, white, skyblue)`
`}`
自定义变量postcss-simple-vars
可以在css里写变量名,会通过postcss-aimple-var,将里面的变量替换成对应变量,包括
配置项如下
`const` `vars = require(``'postcss-simple-vars'``);`
`module.exports = {`
` ``plugins: [`
` ``vars({`
` ``silent: ``true``,``//postcss-simple会自带检测是否有未知变量,有的话,会报错,这里设置成true,就是不管有没有未知变量了`
` ``variables: {`
` ``$kevin: ``'18px'`
` ``}`
` ``})`
` ``]`
` ``};`
写一个css
`/* $kevin测试 */`
`div {`
` ``font-size: 13px;`
` ``font-size: $kevin;`
` ``--$(kevin): $kevin;`
`}`
`$kevin { }`
`$(kevin)_button { }`
在postcss.config.js里,我们将$kevin设置成'18px'
展示到页面上就是
postcss-cssnext
介绍:
可以将自定义的css变量自动转化成常量,自带前缀功能,会和autoprefixer冲突
postcss.config配置项
`const nextcss = require(``"postcss-cssnext"``);`
`module.exports = {`
` ``plugins: [`
` ``nextcss`
` ``]`
` ``};`
开启前
`:root {`
` ``--``color``: ``red``;`
`}`
`div {`
` ``color``: var(--color);`
`}`
开启后
对于比较大的工程来说感觉有点鸡肋
如果在非:root下使用自定义变量,比如body,会出现报错
原因是默认不让你在其他dom元素里使用自定义变量的,但是控制台没报错,而且去掉这个警告图层,渲染还是正常的
说明我们的工程其实是跑起来了,去掉警告就好了
`const nextcss = require(``"postcss-cssnext"``);`
`module.exports = {`
` ``plugins: [`
` ``nextcss({`
` ``features: {`
` ``customProperties: {`
` ``warnings: false`
` ``}`
` ``}`
` ``})`
` ``]`
` ``};`
这样就解决问题了,但是还是觉得仍然鸡肋
个人看法:
缺点:
1.css变量最大的特点,就是灵活性,这样写,相当于直接把css值写死了,那不如直接写死
2.只对:root下的变量生效,如果像移动端组件里,每个组件会修改自定义变量,会导致失效,因为底层的自定义变量值已经被替换成固定值了
优点
1.这样写,可以把需要经常修改的变量,集中到一起,在修改的时候,不需要去翻箱倒柜的找每一个变量
对比框架适用(比较偏的框架,可跳过):
| antd | finedesign | fineui |
|---|---|---|
| antd | fd(公司内部框架) | fineui(公司内部框架) |
| 自定义变量基本上不在:root上定义,都定义在父类或者自身,不适配 | 自定义变量基本上都定义在:root上,但是fd就是想通过修改自定义变量,来修改主题色,因此不适配 | fineui用的是less变量,写的是类样式,元素只需要添加对应的样式类名即可,用postcss是可以的 |
课外延伸:
问:为什么antd的类名看起来像是随机类名
答:可以通过css-loader配置项来实现
css modules是近年来比较流行的一种开发模式,其理念就是把css模块化,让css也拥有模块的特点,具体如下:
每个css文件中的样式都拥有单独的作用域,不会和外界发生命名冲突
对css进行依赖管理,可以通过相对路径引入css文件
使用css modules不需要额外安装模块,只要开启css-loader的modules配置项即可
`{`
` ``loader: ``'css-loader'``,`
` ``options: {`
` ``// modules: true,`
` ``modules: {`
` ``localIdentName: ``'[name]_[local]_[hash:base64:5]_[path]'``,``//name指的是模块名,local指的选择器标识符,hash:base64:5指的是5位数的hash值`
` ``}`
` ``}`
`},`
test.css
`.kevin {`
` ``font-family: Verdana, Geneva, Tahoma, sans-serif;`
`}`
index.js
`import styles from ``'./css_module/test.css'`
`!(``function``($) {`
` ``$(``'.test'``).addClass(styles.kevin);`
`}(jQuery))`
实际效果