唯一安全起点是用 css-tree.parse() 解析 ast,再展开简写、归一化值、合并同义规则;跨文件需先合并 css;需人工检查 js 动态 class、保留关键伪类和媒体查询;微前端下禁止直接操作 :root,应收束变量至容器级作用域。

用 css-tree 解析 AST 是唯一安全起点
字符串替换或正则清洗 CSS,99% 会炸掉媒体查询嵌套、伪类顺序、自定义属性作用域。真正能识别 margin: 0 和 margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0 是同一语义的,只有基于 AST 的解析器。
必须用 css-tree.parse() 先把整段 CSS 转成语法树,再逐节点处理:
- 对每个
Declaration节点,调用expandShorthand()展开简写(如padding: 1em 2em→ 四个独立padding-*) - 对值做归一化:
0px→0,#fff→white,URL()→url()(注意大小写) - 忽略浏览器前缀差异(
-webkit-transform和transform视为不同,除非你明确开启兼容模式)
合并同义规则不能只靠 csso restructure
csso 的 restructure: true 确实能合并同文件内无隔离的重复声明,但它有硬边界:
- 只在同一文件内生效,跨文件需先用
rollup或esbuild合并入口 CSS - 不识别
@layer、!important、@supports块内的上下文,可能误提公共样式 - 完全看不到 CSS-in-JS(如
styled-components),得先用babel-plugin-styled-components提取为真实 CSS - 若项目用了
postcss-preset-env,它可能在 csso 前就重命名了类名,导致匹配失效
重构脏 CSS 时最容易被忽略的三件事
不是“能不能合并”,而是“谁在用、在哪用、改完谁负责”:
- JS 动态拼接的 class 名(
el.classList.add('is-' + status))不会出现在静态 HTML 中,uncss或覆盖率工具根本看不见,必须人工查 JS 模块里所有classList和className调用点 - 伪类(
:hover、:focus-within)、属性选择器([class*="icon-"])、@media print这些规则极易被误删,建议先加注释标记/* keep: legacy-print */,禁用postcss-discard-comments - 清理不是删除动作,而是灰度流程:给待删类加前缀(
old-button),Nginx 日志监控 7 天零调用,再执行真实删除,并打 Git tag 记录批次
别碰 :root,微前端下这是污染开关
遗留项目若已混入微前端架构,任何直接操作 :root 的重构(比如批量替换 --color-primary)都会触发双向污染——子应用改了主应用颜色,主应用又覆盖子应用排版模数。
安全做法是把变量作用域收束到容器级:
- 主应用定义
.shell { --color-primary: #2563eb; },所有组件用.shell .button继承 - 子应用挂载点统一加 wrapper 类(如
<div class="subapp-fengkong"></div>),并在其上声明.subapp-fengkong { --color-primary: #ff5722; } - 禁止任何
:root { --xxx }直接写法,CI 卡点检查grep -r ":root.*--" src/
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











