从性能优化的角度看,Tailwind 或 Bootstrap 这类 CSS 框架其实挺冤枉的。问题不在框架本身,而在于默认的打包策略——它会把整套样式一股脑全部塞进 main.css,其中甚至包含大量从未在首屏出现的类名,比如 .sm:hidden、.lg:p-8。结果就是,浏览器必须先下载、解析并构建完整的 CSSOM,才能开始渲染首屏。在移动端弱网环境下,一个 200KB 的 CSS 文件,足以让首屏渲染卡顿超过 1.5 秒。

为什么 Tailwind 或 Bootstrap 会让移动端首屏变慢
这个问题的根源其实很清晰:不是框架效率低,而是打包策略过于“粗暴”。默认配置下,构建工具会将所有规则都打包进一个 CSS 文件,导致首屏需要加载的 CSS 体积远超实际所需。在桌面端或许还能忍受,但在移动端,尤其是弱网环境下,几乎就是性能的灾难。
提取关键 CSS 时必须避开的三种规则
手动提取关键 CSS 时,有个常见的陷阱:如果内联前没有过滤掉某些规则,反而会触发额外的请求或阻塞解析,导致优化效果大打折扣。需要警惕以下三类:
@import语句:它会同步发起新的请求,完全破坏内联的意义,必须避免。@font-face声明:字体文件的 URL 会被提前触发加载,而且你无法控制它的优先级,容易造成不必要的资源竞争。- 含
url()的声明(比如background-image: url(logo.png)):首屏图片如果通过 CSS 加载,既无法使用loading="lazy"做懒加载,也不能用srcset实现响应式降级,灵活性太差。
正确的做法是:只保留纯结构、颜色、间距类名对应的规则,比如 .header、.text-lg、.bg-blue-500,并且确保这些类名确实出现在首屏的 HTML 中。其他无关的规则,一律不要内联。
用 critters 插件自动提取 + 动态注入剩余 CSS
手动维护关键 CSS 列表,基本不可行。尤其是当 class 名是动态拼接的时候(比如 class="text-${color}"),手动维护几乎是一种折磨。推荐的做法是在构建流程中接入 critters 插件(Vite 和 Webpack 都能用)。它基于 Puppeteer 模拟首屏渲染路径,自动抓取真正用到的规则,再生成关键 CSS。
至于非关键 CSS,不要直接写 ,那样会阻塞渲染。改用这个模式:
这样既能预加载资源,又不阻塞初始渲染;onload 回调能确保样式在 DOM 就绪后才应用,避免出现 FOUC(无样式内容闪烁)。
移动端特别要注意的 class 名陷阱
Tailwind 默认会生成所有响应式变体,比如 sm:、md:、lg:。但移动端首屏,几乎只用 sm: 及以下断点。如果没配置 purge 或 content 路径,大量未用的 lg:p-6 类就会混进关键 CSS,白白增加体积。
这里有三个检查点值得留意:
- 确认
tailwind.config.js中的content字段精确指向首屏模板文件(比如src/pages/home.vue),而不是整个src/目录。指得越精确,效果越好。 - 禁用不必要的插件。比如
@tailwindcss/aspect-ratio,如果首屏没有宽高比需求,就别让它参与构建。 - 避免在首屏元素上写
class="hidden md:block"。因为md:block属于非首屏样式,应该移出关键路径。
说实话,真正难的不是“怎么添加优化”,而是每次发版后重新验证关键 CSS 是否仍然精准覆盖首屏结构。尤其当产品同学临时加了一个 class="xl:text-4xl" 到 banner 上时,优化效果可能就在不经意间被破坏了。
