cssnano默认preset会重排伪类、合并选择器、删空规则,导致:hover覆盖:focus、动态类失效、字体图标消失;修复需禁用mergeRules、sortSelectors、discardEmpty和normalizeWhitespace四项,并确保autoprefixer在cssnano前执行。

cssnano 默认 preset 会重排伪类、合并选择器、删空规则,直接导致 :hover 覆盖 :focus、动态类失效、字体图标消失——这不是 bug,是它默认就干这事。
为什么压缩后按钮悬停没反应、Tab 失焦、图标不显示
根本原因是 mergeRules 和 sortSelectors 在默认 preset 下全开:
-
mergeRules把a:hover和a:focus合并成一条规则,浏览器按书写顺序渲染,后写的覆盖先写的,:focus 样式直接被吞 -
sortSelectors把a:focus:hover重排成a:hover:focus,伪类优先级和触发逻辑全变 -
discardEmpty删除::before { content: " "; },字体图标依赖这个空格占位,删了就空白 -
normalizeWhitespace在 IE11 下把display: -ms-flexbox格式化成非法值,布局直接崩溃
autoprefixer 必须在 cssnano 前面,顺序错等于白配
PostCSS 插件执行是线性的,顺序错了,压缩就变成破坏:
- 错误写法:
[cssnano(), autoprefixer()]→display: flex先被压缩掉,autoprefixer没机会补-webkit-flex,iOS 12 直接白屏 - 正确写法:
[autoprefixer(), cssnano({ preset: ['default', { mergeRules: false, sortSelectors: false }] })] - Vite 用户注意:
css.postcss.options.plugins同样要手动保证顺序,别信插件自动注入 - Webpack 用户额外检查:
MiniCssExtractPlugin.loader别设minimize: true(已废弃),否则绕过整个 PostCSS 链,cssnano 彻底失效
postcss-import 没跑完,cssnano 就开始压缩,等于瞎压
cssnano 只处理传给它的那块字符串。如果源 CSS 里有 @import "base.css",但 postcss-import 没在它前面执行,它看到的只是这一行字,根本不知道 base.css 里有什么:
- 结果:跨文件重复规则无法合并,@import 依赖缺失,产出文件缺样式
- 常见诱因:loader 顺序错(比如
postcss-loader写在sass-loader前面),或postcss.config.js没导出函数来 resolve @import 路径 - Vue SFC 用户特别注意:
的链路必须是sass-loader → postcss-loader → MiniCssExtractPlugin.loader,漏一环,cssnano 就处理原始 SCSS 语法,直接报错或静默跳过
安全配置不是“少开几个选项”,而是明确关掉这四条
别用 preset: 'default' 然后零星加 false,v6+ 必须显式传参,最小安全集就是这四个关掉:
-
mergeRules: false—— 防止不同语义的选择器(如.btn:hover和.link:hover)被强行合并 -
sortSelectors: false—— 保留你写的伪类顺序,不重排:first-child:hover这类组合 -
discardEmpty: false—— 保住::before { content: "" }和font-family: "icon", sans-serif里的 fallback -
normalizeWhitespace: false—— IE11 和某些安卓 WebView 对 flex 声明格式敏感,别碰
如果你用了 postcss-custom-properties,还得额外加 preserve: true,否则 var(--color) 在变量替换前就被当成无效声明删了。真正容易被忽略的不是哪条配置,而是 cssnano 的输入是否完整——它只认 PostCSS 流程喂给它的那一口,喂得不对,再怎么调参也救不回布局。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











