当前位置 博文首页 > manbax:postcss 运用及原理
目标:
这篇文章大部分工作都在调试 postcss 源码,涉及到的源码见 https://github.com/gaollard/postcss-demo/blob/master/doc.md
在聊 postcss 之前,我们需要知道什么是 CSS 后处理工具。我们比较熟悉的 Less/Sass/Stylus,这类工具都属于CSS 预处理工具。预处理指的是通过特殊的规则,将非 css 文本格式最终生成 css 文件,而 postcss 则是对 CSS 进行处理,最终生成CSS。
可能大部分前端开发者都使用过 Autoprefixer 这款插件,它以 Can I Use (浏览器兼容性支持) 为基础,自动处理兼容性问题,下面是一个简单的例子:
// Autoprefixer 处理前的CSS样式
.container {
display: flex;
}
.item {
flex: 1;
}
// Autoprefixer 处理后的CSS样式
.container {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
}
.item {
-webkit-box-flex: 1;
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
}
在这个例子中过,通过使用 Autoprefixer 插件,帮助我们自动处理浏览器前缀,极大的提高了编码效率。其实,Autoprefixer 正是 postcss 众多插件中的一款,postcss 提供的简洁明了API,并且文档十分详细,这为其生态建设提供了有力的支撑。
点击 这里 查看更多可用插件。
两个主要的功能:
这里只介绍 postcss API,如果您使用 webpack,只需要将 postcss 包装为 postcss-loader 即可。
// 01-simple-demo
const autoprefixer = require('autoprefixer')
const postcss = require('postcss')
const precss = require('precss')
const fs = require('fs')
const path = require('path')
const src = path.resolve('./src/app.css');
const dest = path.resolve('./dest/app.css');
fs.readFile(src, (err, css) => {
postcss([precss, autoprefixer]) // [precss, autoprefixer] 为使用的插件列表,返回 Processor 对象
.process(css) // process 接收 css 资源
.then(result => { // result 为 Result 实例
fs.writeFile(dest, result.css, function(err) {
if (err) throw err;
});
})
})
相关源码:
// 02-use-parser
const postcss = require('postcss')
const fs = require('fs')
const path = require('path')
const src = path.resolve('./src/app.css');
const css = fs.readFileSync(src);
const root = postcss.parse(css);
console.log(root); // CSS 抽象语法树
相关源码:
主要步骤:
主要内容:
最不容易理解的也是最难的点:CSS AST 的生成以及操作。
你暂且将 AST 理解一个节点树,这些节点不完全相同,它们继承自同一个节点(源码中为Container)。
在学习 postcss 初期,通过查看可视化的 postcss css 语法树,可以帮助你理解。使用使用 css ast 在线工具,下图为一个很标准的 css 文档,有注释、媒体查询,以及选择器样式:
/**
* Paste or drop some CSS here and explore
* the syntax tree created by chosen parser.
* Enjoy!
*/
@media screen and (min-width: 480px) {
body {
background-color: lightgreen;
}
}
#main {
border: 1px solid black;
}
ul li {
padding: 5px;
}
上面的 css 最终会处理为下图结构,通过打印信息我们可以发现树型结构的 JS 对象是一个名为 Root 的构造函数,而起树型结构的 nodes 节点下还有 Common,AtRule, Rule 构造函数。
CSS AST 节点主要有以下构造类组成:
一个选择器代表一个Rule,选择器对应的样式列表 nodes 为 Declaration构造函数
大部分节点结构是类似的,如果你理解了 Rule 节点的结构,相信其他类型的节点对你也是很轻松的!
Declaration 是 css 样式属性,prop为样式属性,value为样式值。可给 Rule 手动添加样式属性,也可以修改prop,value。上文提到的 Autoprefixer 就是通过 clone 当前属性,修改 prop 并添加到选择器下,Declaration 节点非常简单:
大部分节点都继承了 Container,因此我们先看看公共属性:
node.parent.insertAfter(node, add)
插件用于丰富 postcss 的功能。
插件编写文档
- https://www.postcss.parts
- https://github.com/himynameisdave/postcss-plugins
postcss([PluginA({}), PluginB({})])
.process(css, {from: ``, to: ``})
.then(result => {
// do ...
}).catch(error => {
throw new Error(error)
})
上文中,我们对 css 处理后生成的 Root 以及其节点下的 Commont,AtRule,Rule, Declaration 有了基本的认识,那么我们是如何获得Root,又将拿这些构造函数做些什么呢。
const fs = require('fs')
const path = require('path')
const postcss = require('postcss')
const css = fs.readFileSync(path.resolve(__dirname, './main.css'), 'utf8')
const checkDepth = postcss.plugin('check-depth', (opt) => {
opt = opt || { depth: 3 }
return root => {
root.walkRules(rule => {
let selector = rule.selector.replace(/(^\s*)|(\s*$)/g, '')
if (selector.split(/\s/).length > opt.depth) {
throw rule.error(`css selector depth is too long`)
}
})
}
})
postcss([checkDepth({
depth: 3
})])
.process(css, { from: ``, to: `` })
.then(result => {
console.log(result);
})
.catch(error => {
throw new Error(error)
})
const postcss = require('postcss') // postcss
module.exports = postcss.plugin('px2rem', function(opts) {
opts = opts || {};
return function (root, result) {
root.replaceValues(/\d+px/, { fast: 'px' }, string => {
return opts.ratio * parseInt(string) + 'rem'
})
}
});
通过上述插件代码的示例,可以看出整个流程还是很清晰的