Vite生产构建默认启用CSS提取,所有CSS均输出为独立文件并通过<link>加载,不会内联到HTML中;开发环境的<style>注入仅限HMR,不参与生产流程。

小体积 CSS 能内联,但不是“加个 import 就自动进 <style>”——Vite 默认只在开发环境把 import './index.css' 注入到 <head> 的 <style> 里;生产构建时,它会走 MiniCssExtractPlugin 路线,输出独立 .css 文件并用 <link> 加载。要真正在 HTML 中内联,得绕过默认 CSS 提取流程。
为什么 import './style.css' 在生产构建后没内联?
Vite 生产构建默认启用 CSS 提取(等价于 build.cssCodeSplit: true),所有 CSS 都被收集、压缩、写入 dist/assets/xxx.css,再由 <link rel="stylesheet"> 引入。哪怕只有 200 字节,也不会进 HTML。
-
build.cssCodeSplit: false可禁用 CSS 分割,但仅让 CSS 合并进 JS chunk,仍不会生成<style>标签 - 开发时的
<style>注入是 HMR 专用逻辑,不参与生产构建流程 - 试图用
style-loader(Webpack 方案)在 Vite 中无效——Vite 不走 Webpack loader 链
用 vite-plugin-singlefile 最省事但要注意体积阈值
这个插件会在构建末期扫描所有产出的 CSS 文件,读取内容,直接拼进 HTML 的 <head> 里,同时删掉原始 .css 文件(可选)。它不依赖你手动 import,也不管你用的是 @import 还是 import。
- 必须设
inlineCSS: true(默认开启),否则只内联 JS - 它不校验 CSS 体积:若某个 chunk 的 CSS 含未处理的
url(./icon.png)或@font-face,内联后仍会触发额外请求,首屏反而更慢 - 若项目有多个 HTML 入口(如
login.html,admin.html),需确认插件是否配置了includeChunks或对应入口名,否则只处理index.html - 搭配
build.rollupOptions.output.inlineDynamicImports = true更稳妥,避免异步 chunk 的 CSS 漏掉
手动内联(raw-loader + EJS 模板)最可控但容易漏依赖
适合明确知道哪些是“关键 CSS”的场景,比如仅内联 reset.css、viewport、首屏按钮/标题规则。
立即学习“前端免费学习笔记(深入)”;
- 必须用
raw-loader@0.5.1(新版返回{ default: string },EJS 模板无法直接<%= css %>插值) - 内联内容里不能含
@import、url()、未 base64 处理的@font-face src——这些都会变成新网络请求 - 若首屏 DOM 是 JS 渲染的(如纯 CSR),内联 CSS 必须覆盖所有初始状态类(如
.btn.is-loading),否则用户看到无样式的骨架 - 模板中写法示例:
<style type="text/css"><%= require('!!raw-loader!./critical.css').default %></style>
真正关键的不是“能不能塞进去”,而是“浏览器解析时有没有被阻塞”。验证方式只有一个:DevTools Network 面板勾选 Offline 刷新,看首屏文字、色块、按钮是否完整渲染——如果出现白屏或闪动,说明内联失败或样式未覆盖初始状态。



















