真正内联css必须在gatsby-ssr.js的onrenderbody中同步读取构建产物css文件并注入标签;import方式依赖style-loader导致fouc,gatsby-browser.js引入不参与ssr。

直接在组件里 import "./styles.css" 或只在 gatsby-browser.js 中引入全局样式,都不是真正的内联——前者把 CSS 打包进 JS bundle,后者根本不参与 SSR,首屏必然 FOUC。
为什么 import 语句无法实现 CSS 内联
Webpack 把 import 当作 JS 模块处理,最终依赖 style-loader 在运行时动态插入 <style></style> 标签。这意味着:
- 首屏渲染必须等 JS 下载、解析、执行后才注入样式
- CSS 无法被浏览器并行下载,阻塞关键渲染路径
- JS 加载失败或执行中断,页面就彻底无样式
- 构建产物中 CSS 通常混在
app-xxx.js里,不是独立文件
正确做法:在 gatsby-ssr.js 中读取构建产物并注入 <style></style>
目标是绕过 JS 模块系统,在 HTML 构建阶段就把 CSS 内容写入 。关键步骤如下:
- 确保 CSS 已通过
gatsby-plugin-postcss或gatsby-plugin-sass输出到public/static/css/,文件名带 hash(如app.123abc.css) - 在
gatsby-ssr.js中用fs.readFileSync同步读取该文件(仅限构建时,不可用于浏览器) - 在
onRenderBody钩子中用setHeadComponents注入<style></style>标签,dangerouslySetInnerHTML填入原始内容 - 绝对不要在
gatsby-browser.js做任何对应操作——那是客户端行为,与内联目标冲突
示例代码片段:
exports.onRenderBody = ({ setHeadComponents }, pluginOptions) => {
const cssPath = path.join(__dirname, 'public', 'static', 'css', '*.css')
const cssFile = glob.sync(cssPath)[0]
const cssContent = fs.readFileSync(cssFile, 'utf8')
setHeadComponents([
<style key="inline-css" dangerouslysetinnerhtml="{{" __html: csscontent></style>
])
}
Linaria 是更稳妥的零运行时替代方案
它不走 Webpack 的 CSS loader 链路,而是用 Babel 插件在构建时解析 styled 模板字面量,直接输出标准 CSS 文件,并自动注入 <link>。但注意:
- 仍需配置
gatsby-ssr.js的onRenderBody,否则 SSR 阶段无样式 - 安装时必须包含
sass(Dart Sass)作为 peer dep,否则gatsby-plugin-sass会报错 - 若项目已用
gatsby-plugin-postcss,Linaria 与其共存需注意 PostCSS 插件是否兼容 CSS-in-JS 输出
真正内联的关键不在“怎么写样式”,而在于“谁在哪个阶段把内容塞进 ”——漏掉 gatsby-ssr.js 的 onRenderBody,哪怕 CSS 文件生成得再规范,也只会出现在客户端,首屏照样白屏。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











