PostCSS 基本使用 #
命令行使用 #
基本命令 #
bash
# 安装 CLI
npm install postcss-cli --save-dev
# 基本用法
npx postcss input.css -o output.css
# 使用配置文件
npx postcss input.css -o output.css --config
# 指定配置文件路径
npx postcss input.css -o output.css --config ./path/to/config
常用选项 #
bash
# 输入输出
npx postcss input.css -o output.css # 指定输出文件
npx postcss input.css -d dist # 指定输出目录
npx postcss src/*.css -d dist # 处理多个文件
# 监听文件变化
npx postcss input.css -o output.css --watch
# Source Map
npx postcss input.css -o output.css --map # 生成 source map
npx postcss input.css -o output.css --no-map # 不生成 source map
# 详细输出
npx postcss input.css -o output.css --verbose
# 使用特定插件
npx postcss input.css -o output.css --use autoprefixer
# 使用多个插件
npx postcss input.css -o output.css --use autoprefixer,cssnano
# 传递插件选项
npx postcss input.css -o output.css --use autoprefixer --autoprefixer.browsers "> 1%"
完整 CLI 选项 #
| 选项 | 说明 |
|---|---|
-o, --output |
输出文件路径 |
-d, --dir |
输出目录 |
-w, --watch |
监听文件变化 |
--verbose |
显示详细日志 |
--map |
生成 source map |
--no-map |
不生成 source map |
--config |
配置文件路径 |
-u, --use |
指定使用的插件 |
-p, --parser |
自定义解析器 |
-t, --stringifier |
自定义生成器 |
-s, --syntax |
自定义语法 |
监听模式 #
bash
# 监听单个文件
npx postcss input.css -o output.css --watch
# 监听多个文件
npx postcss "src/**/*.css" -d dist --watch
# 结合其他工具
npx postcss input.css -o output.css --watch --verbose
处理多个文件 #
bash
# 处理目录下所有 CSS 文件
npx postcss "src/*.css" -d dist
# 递归处理子目录
npx postcss "src/**/*.css" -d dist
# 保持目录结构
npx postcss "src/**/*.css" -d dist --base src
Node.js API #
基本用法 #
javascript
const postcss = require('postcss')
const autoprefixer = require('autoprefixer')
const css = `
.button {
display: flex;
user-select: none;
}
`
// 异步处理
postcss([autoprefixer])
.process(css, { from: 'input.css', to: 'output.css' })
.then(result => {
console.log(result.css)
})
.catch(error => {
console.error(error)
})
// 同步处理(不推荐)
const result = postcss([autoprefixer]).process(css, { from: undefined })
console.log(result.css)
async/await 用法 #
javascript
const postcss = require('postcss')
const autoprefixer = require('autoprefixer')
async function processCss(css) {
try {
const result = await postcss([autoprefixer])
.process(css, { from: 'input.css', to: 'output.css' })
console.log(result.css)
// 获取 warnings
result.warnings().forEach(warn => {
console.warn(warn.toString())
})
return result.css
} catch (error) {
console.error('PostCSS error:', error)
}
}
处理文件 #
javascript
const postcss = require('postcss')
const autoprefixer = require('autoprefixer')
const fs = require('fs')
const path = require('path')
async function processFile(inputPath, outputPath) {
const css = fs.readFileSync(inputPath, 'utf8')
const result = await postcss([autoprefixer])
.process(css, {
from: inputPath,
to: outputPath,
map: { inline: false }
})
// 写入 CSS 文件
fs.writeFileSync(outputPath, result.css)
// 写入 source map
if (result.map) {
fs.writeFileSync(outputPath + '.map', result.map.toString())
}
}
processFile('src/style.css', 'dist/style.css')
使用配置文件 #
javascript
const postcss = require('postcss')
const postcssLoadConfig = require('postcss-load-config')
async function processWithConfig(css, ctx) {
// 加载配置
const config = await postcssLoadConfig(ctx)
// 使用配置处理
const result = await postcss(config.plugins)
.process(css, {
from: ctx.from,
to: ctx.to,
map: config.options.map
})
return result.css
}
processWithConfig(
'.button { display: flex }',
{ from: 'src/style.css', to: 'dist/style.css' }
)
流式处理 #
javascript
const postcss = require('postcss')
const autoprefixer = require('autoprefixer')
const { Readable, Writable } = require('stream')
const cssStream = new Readable({
read() {
this.push('.button { display: flex }')
this.push(null)
}
})
const postcssStream = new Writable({
write(chunk, encoding, callback) {
const css = chunk.toString()
postcss([autoprefixer])
.process(css, { from: undefined })
.then(result => {
console.log(result.css)
callback()
})
.catch(callback)
}
})
cssStream.pipe(postcssStream)
PostCSS 对象 #
result 对象 #
javascript
const result = await postcss([autoprefixer])
.process(css, { from: 'input.css' })
// 结果属性
result.css // 处理后的 CSS 字符串
result.map // Source map 对象
result.root // AST 根节点
result.messages // 消息数组(警告、依赖等)
// 方法
result.warnings() // 获取所有警告
result.toString() // 获取 CSS 字符串(包含 source map 注释)
root 对象(AST) #
javascript
const result = await postcss([autoprefixer])
.process(css, { from: undefined })
const root = result.root
// 遍历节点
root.walk(node => {
console.log(node.type) // 'rule', 'atrule', 'decl', 'comment'
})
// 遍历规则
root.walkRules(rule => {
console.log(rule.selector)
})
// 遍历声明
root.walkDecls(decl => {
console.log(decl.prop, ':', decl.value)
})
// 遍历 @ 规则
root.walkAtRules(atRule => {
console.log(atRule.name, atRule.params)
})
节点操作 #
javascript
const postcss = require('postcss')
const css = '.button { color: red; }'
const root = postcss.parse(css)
// 创建新节点
const newRule = postcss.rule({ selector: '.container' })
const newDecl = postcss.decl({ prop: 'padding', value: '20px' })
newRule.append(newDecl)
root.append(newRule)
// 修改节点
root.walkDecls('color', decl => {
decl.value = 'blue'
})
// 删除节点
root.walkDecls('color', decl => {
decl.remove()
})
// 替换节点
root.walkDecls('color', decl => {
decl.replaceWith(
postcss.decl({ prop: 'color', value: 'green' })
)
})
console.log(root.toString())
插件使用 #
基本插件使用 #
javascript
const postcss = require('postcss')
const autoprefixer = require('autoprefixer')
const cssnano = require('cssnano')
const plugins = [
autoprefixer({
overrideBrowserslist: ['> 1%', 'last 2 versions']
}),
cssnano({
preset: 'default'
})
]
const result = await postcss(plugins)
.process(css, { from: 'input.css' })
条件加载插件 #
javascript
const postcss = require('postcss')
const autoprefixer = require('autoprefixer')
const cssnano = require('cssnano')
const isProduction = process.env.NODE_ENV === 'production'
const plugins = [
autoprefixer,
isProduction && cssnano({ preset: 'default' })
].filter(Boolean)
const result = await postcss(plugins)
.process(css, { from: 'input.css' })
插件选项 #
javascript
const autoprefixer = require('autoprefixer')
const cssnano = require('cssnano')
const postcssPresetEnv = require('postcss-preset-env')
const plugins = [
// Autoprefixer 选项
autoprefixer({
overrideBrowserslist: ['> 1%', 'last 2 versions'],
grid: true,
flexbox: 'no-2009'
}),
// PostCSS Preset Env 选项
postcssPresetEnv({
stage: 2,
features: {
'nesting-rules': true
}
}),
// cssnano 选项
cssnano({
preset: ['default', {
discardComments: { removeAll: true },
normalizeWhitespace: false
}]
})
]
Source Map #
生成 Source Map #
javascript
const result = await postcss([autoprefixer])
.process(css, {
from: 'src/style.css',
to: 'dist/style.css',
map: true // 或 map: { inline: true }
})
console.log(result.css) // CSS + source map 注释
console.log(result.map) // source map 对象
外部 Source Map #
javascript
const result = await postcss([autoprefixer])
.process(css, {
from: 'src/style.css',
to: 'dist/style.css',
map: {
inline: false, // 不内联
prev: null, // 之前的 source map
sourcesContent: true, // 包含源内容
annotation: true // 添加注释
}
})
// 分别保存
fs.writeFileSync('dist/style.css', result.css)
fs.writeFileSync('dist/style.css.map', result.map.toString())
链式 Source Map #
javascript
// 第一步处理
const result1 = await postcss([plugin1])
.process(css, {
from: 'src/style.css',
to: 'temp/style.css',
map: { inline: false }
})
// 第二步处理(使用上一步的 source map)
const result2 = await postcss([plugin2])
.process(result1.css, {
from: 'temp/style.css',
to: 'dist/style.css',
map: {
inline: false,
prev: result1.map // 传入上一步的 source map
}
})
错误处理 #
捕获错误 #
javascript
try {
const result = await postcss([autoprefixer])
.process(css, { from: 'input.css' })
console.log(result.css)
} catch (error) {
// PostCSS 错误对象
console.error(error.message)
console.error(error.stack)
// 错误位置
if (error.source) {
console.error('Error at line:', error.line)
console.error('Column:', error.column)
console.error('File:', error.file)
}
}
自定义错误 #
javascript
const postcss = require('postcss')
const plugin = postcss.plugin('my-plugin', () => {
return root => {
root.walkDecls('color', decl => {
if (decl.value === 'red') {
throw decl.error('Do not use red color!', {
plugin: 'my-plugin',
word: 'red'
})
}
})
}
})
try {
await postcss([plugin]).process('.button { color: red }', { from: undefined })
} catch (error) {
console.error(error.message) // Do not use red color!
}
警告处理 #
javascript
const result = await postcss([autoprefixer])
.process(css, { from: 'input.css' })
// 获取所有警告
const warnings = result.warnings()
warnings.forEach(warning => {
console.log('Type:', warning.type)
console.log('Text:', warning.text)
console.log('Line:', warning.line)
console.log('Column:', warning.column)
console.log('Plugin:', warning.plugin)
})
实用示例 #
批量处理文件 #
javascript
const postcss = require('postcss')
const autoprefixer = require('autoprefixer')
const cssnano = require('cssnano')
const fs = require('fs')
const path = require('path')
const glob = require('glob')
async function processFiles(pattern, outputDir) {
const files = glob.sync(pattern)
for (const file of files) {
const css = fs.readFileSync(file, 'utf8')
const relativePath = path.relative('src', file)
const outputPath = path.join(outputDir, relativePath)
// 确保输出目录存在
fs.mkdirSync(path.dirname(outputPath), { recursive: true })
const result = await postcss([autoprefixer, cssnano])
.process(css, {
from: file,
to: outputPath,
map: { inline: false }
})
fs.writeFileSync(outputPath, result.css)
if (result.map) {
fs.writeFileSync(outputPath + '.map', result.map.toString())
}
console.log(`Processed: ${file} -> ${outputPath}`)
}
}
processFiles('src/**/*.css', 'dist')
监听文件变化 #
javascript
const postcss = require('postcss')
const autoprefixer = require('autoprefixer')
const fs = require('fs')
const chokidar = require('chokidar')
async function processFile(filePath) {
const css = fs.readFileSync(filePath, 'utf8')
const outputPath = filePath.replace('src', 'dist')
const result = await postcss([autoprefixer])
.process(css, { from: filePath, to: outputPath })
fs.mkdirSync(path.dirname(outputPath), { recursive: true })
fs.writeFileSync(outputPath, result.css)
console.log(`Processed: ${filePath}`)
}
const watcher = chokidar.watch('src/**/*.css')
watcher
.on('add', processFile)
.on('change', processFile)
.on('unlink', filePath => {
const outputPath = filePath.replace('src', 'dist')
if (fs.existsSync(outputPath)) {
fs.unlinkSync(outputPath)
}
})
提取 CSS 变量 #
javascript
const postcss = require('postcss')
const extractVariables = postcss.plugin('extract-variables', () => {
const variables = {}
return root => {
root.walkDecls(decl => {
if (decl.prop.startsWith('--')) {
variables[decl.prop] = decl.value
}
})
return { variables }
}
})
const css = `
:root {
--primary: #007bff;
--secondary: #6c757d;
}
`
const result = await postcss([extractVariables])
.process(css, { from: undefined })
console.log(result.variables)
// { '--primary': '#007bff', '--secondary': '#6c757d' }
CSS 统计 #
javascript
const postcss = require('postcss')
const cssStats = postcss.plugin('css-stats', () => {
let stats = {
rules: 0,
selectors: 0,
declarations: 0,
properties: {},
values: {}
}
return root => {
root.walkRules(rule => {
stats.rules++
stats.selectors += rule.selectors.length
})
root.walkDecls(decl => {
stats.declarations++
stats.properties[decl.prop] = (stats.properties[decl.prop] || 0) + 1
stats.values[decl.value] = (stats.values[decl.value] || 0) + 1
})
return { stats }
}
})
const result = await postcss([cssStats])
.process(css, { from: undefined })
console.log(result.stats)
调试技巧 #
打印 AST #
javascript
const postcss = require('postcss')
const css = '.button { color: red; }'
const root = postcss.parse(css)
console.log(JSON.stringify(root, null, 2))
节点遍历调试 #
javascript
const postcss = require('postcss')
const css = `
.button {
color: red;
padding: 10px;
}
`
const root = postcss.parse(css)
root.walk(node => {
console.log('Type:', node.type)
console.log('Source:', node.source)
console.log('---')
})
插件调试 #
javascript
const debugPlugin = postcss.plugin('debug', () => {
return {
postcssPlugin: 'debug',
Once(root) {
console.log('Processing CSS...')
},
Rule(rule) {
console.log('Found rule:', rule.selector)
},
Declaration(decl) {
console.log(` ${decl.prop}: ${decl.value}`)
}
}
})
debugPlugin.postcss = true
await postcss([debugPlugin]).process(css, { from: undefined })
下一步 #
现在你已经掌握了 PostCSS 的基本使用方法,接下来学习 插件系统 深入了解 PostCSS 的核心机制!
最后更新:2026-03-28