cssnano需置于autoprefixer之后并显式配置preset: 'default',避免与构建工具双重压缩;它仅做语义无损压缩,混淆需用postcss-obfuscator等专用插件。

cssnano 本身不压缩 CSS,它只是 PostCSS 流水线中一个插件;真正起作用的是你把它放在正确位置、用对参数、并避开构建工具的隐式压缩干扰。
cssnano 必须放在 autoprefixer 之后
顺序错了,前缀就没了——cssnano 会把带前缀的重复声明(比如 -webkit-transform 和 transform)当成冗余删掉,如果它在 autoprefixer 前运行,那连前缀都还没加,压缩器就直接干掉“看似重复”的规则了。
-
postcss.config.js中plugins数组顺序必须是:require('autoprefixer')→ 其他转换类插件(如postcss-preset-env)→require('cssnano') - 别写成条件启用:比如
env === 'production' ? require('cssnano') : null,Webpack 的css-minimizer-webpack-plugin本身已集成cssnano,重复启用会导致calc()被错误折叠、自定义属性丢失 - 验证方法:临时把
cssnano注释掉,跑一次构建,用浏览器开发者工具检查元素是否还有-ms-或-webkit-前缀;再放开,确认压缩后前缀仍在
cssnano v6+ 必须显式传 preset: 'default'
只写 require('cssnano') 会报错 Invalid CSS after ""——这不是配置语法错,而是 v6+ 废弃了默认 preset,必须手动指定。
- 正确写法:
require('cssnano')({ preset: 'default' }) - 不要用
{ preset: 'advanced' }或{ preset: 'safe' },前者已在 v5 后移除,后者是旧版 alias,v6+ 不识别 - 如果你需要禁用某项优化(比如保留
@keyframes名称),得单独配选项:reduceIdents: false,而不是靠 preset 切换
避免和构建工具的内置压缩打架
Webpack、Vite、Meteor 都自带 CSS 压缩逻辑,硬塞 cssnano 进 postcss.config.js 很可能触发双重压缩,结果是 rem 被转成 px、@supports 被整个删掉、甚至输出空文件。
立即学习“前端免费学习笔记(深入)”;
- Webpack:关掉
css-loader的minimize选项(设为false),让压缩只由css-minimizer-webpack-plugin控制 - Vite:默认不用 PostCSS 压缩,需手动在
vite.config.ts的css.postcss.plugins里加cssnano,且仅在build.minify === 'esbuild'外生效 - Meteor:用
standard-minifier-css就够了,它内部已集成cssnano并自动对齐 PostCSS 配置;自己再配cssnano反而会让@import提升失效
混淆类需求不能只靠 cssnano
class 名混淆、选择器重命名、映射表导出——这些 cssnano 做不了。postcss-obfuscator 才是干这个的,但它必须插在 css-loader 之后、mini-css-extract-plugin 之前,且要额外配 classIgnore 和 parseSFC。
-
cssnano只做语义无损压缩:删空格、合并声明、缩颜色值(#ff0000→red)、移未用@keyframes - 混淆必须用专用插件,且要自行导出映射 JSON 供 JS 侧引用
classNames,否则运行时找不到 class - 常见坑:在 Vue SFC 里用
scoped时没开parseSFC: true,结果只混淆了外部样式,组件内样式原样输出
最常被忽略的一点:PostCSS 配置文件里别 require('./package.json'),CI 环境或 Docker 容器里这文件可能不存在,导致整个流水线在 parse 阶段静默失败——用 process.env.npm_package_version 更稳。


















