cssnano会删除@keyframes,因reduceidents和discardunused默认启用,将未被静态分析到的动画名误判为死代码;需禁用reduceidents、discardunused、mergerules并用/! animate: name /注释保留关键帧。

cssnano 为什么删掉你的 @keyframes?
不是它“想删”,而是 reduceIdents 和 discardUnused 默认启用后,会把未被静态分析到的动画名当作“死代码”直接剔除。比如你用 JS 动态设置 element.style.animationName = 'bounceIn',但 cssnano 没看到任何 animation: bounceIn 出现在 CSS 文件里,就顺手把整个 @keyframes bounceIn 删了。
常见现象包括:
- 页面加载后动画完全不触发,
getComputedStyle(el).animationName返回none - 打包后 DevTools 的 Styles 面板里搜不到对应
@keyframes块 - 使用 Tailwind 的
animate-bounce等工具类时,动画突然失效
必须禁用的三项 cssnano 规则
reduceIdents 会重命名或删除动画名;discardUnused 在无 AST 上下文时误判“未使用”;mergeRules 可能合并掉带 animation 的规则,导致层叠顺序错乱。
安全配置要显式关掉它们:
require('cssnano')({
preset: ['default', {
reduceIdents: false,
discardUnused: false,
mergeRules: false
}]
})
注意:discardUnused 尤其危险——它不看 JS 调用链,只扫描 CSS 内部引用。只要没写 animation: slideUp,哪怕你在 HTML 里写了 class="js-trigger-slideUp",它也照删不误。
如何让 cssnano “认出”动态动画名?
靠注释标记最可靠,且不依赖构建时的 JS 分析能力。
在 @keyframes 前加保留注释:
/*! cssnano-enable */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
/*! cssnano-disable */
或者更轻量的方式(推荐):
- 在
postcss.config.js中启用comment: { preserve: /^!/ },确保/*! */不被删 - 所有关键动画块都用
/*! animate: fadeIn */这类自定义标记包裹 - 配合 Webpack 的
DefinePlugin注入运行时变量时,避免生成带哈希的动画名(如slideIn_abc123),否则 cssnano 根本无法识别
动画相关属性压缩的风险点
animation 简写值容易被 mergeLonghand 或 normalizeWhitespace 破坏:比如 animation: fadeIn 0.3s ease-in both; 可能被格式化成 animation:fadeIn.3sease-inboth,IE11 和部分安卓 WebView 会直接忽略整条声明。
建议关闭这两项:
-
mergeLonghand: false—— 防止把animation-duration和animation-name合并后错位 -
normalizeWhitespace: false—— 保留空格,避免解析失败
真正难的不是压多小,而是压完还能动——动画名、timing-function、fill-mode 这些语义信息一旦丢失,浏览器不会报错,只会静默失效。检查 dist 目录里的 CSS,打开 DevTools 的 Animations 面板,逐个验证关键帧是否还在,比跑一遍 Lighthouse 更管用。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











