零运行时引入CSS的核心是构建阶段生成独立CSS文件并由HTML直接加载。import "./styles.css"会将样式打包进JS bundle,依赖JS执行注入,导致首屏渲染延迟;正确做法是在gatsby-ssr.js中读取public/下已构建的带hash的CSS文件,用onRenderBody注入<link>或<style>;Linaria虽能构建时输出CSS,但若未在gatsby-ssr.js中注入,SSR仍无样式,不满足零运行时。

直接在组件里 import CSS 文件,或只在 gatsby-browser.js 里导入全局样式,都不是零运行时引入——前者会把样式打包进 JS bundle,后者根本不会参与 SSR 样式注入。真正实现零运行时,核心是让 CSS 完全脱离 JavaScript 运行时控制,在构建阶段就生成独立文件,并由 HTML 直接加载。
为什么 import "./styles.css" 不是零运行时
Webpack 把 import 的 CSS 当作模块处理,最终会通过 style-loader(或类似机制)在 JS 中动态插入 <style> 标签。这意味着:首屏渲染依赖 JS 执行、样式无法被浏览器并行下载、JS 失败则样式丢失。
-
import语句本身是 JS 行为,必须等 JS 解析执行后才触发样式注入 - 即使用了
gatsby-plugin-sass或gatsby-plugin-postcss,只要路径是通过 JSimport引入的,就仍走 JS 模块链路 - 构建产物中 CSS 通常被打包进 JS 文件(如
app-xxx.js),而非独立.css文件
正确做法:用 gatsby-ssr.js + 构建产物路径读取 CSS
目标是绕过 JS 模块系统,直接把已构建好的 CSS 内容注入 HTML <head>。这要求你在 gatsby-ssr.js 中读取 public/ 下的最终 CSS 文件,并用 setHeadComponents 注入为 <style> 或 <link>。
- 确保 CSS 已通过插件(如
gatsby-plugin-postcss)构建到public/static/css/,文件名带 hash(如app.123abc.css) - 在
gatsby-ssr.js中用fs.readFileSync同步读取该文件内容(注意:仅限构建时,不能在浏览器端运行) - 用
onRenderBody钩子将内容包裹进<style>标签,传给setHeadComponents - 不要在
gatsby-browser.js做任何对应操作——那是客户端行为,与零运行时目标冲突
Linaria 是更彻底的零运行时方案
它不依赖 Webpack 的 CSS loader,而是用 Babel 插件在构建时解析 tagged template literals,直接输出标准 CSS 文件,并自动注入 <link>。你写的样式代码像 JS 一样组织,但运行时完全无 JS 开销。
立即学习“前端免费学习笔记(深入)”;
- 安装
linaria和gatsby-plugin-linaria,并在gatsby-config.js中启用 - 写法示例:
const Button = styled.button`color: blue;`,构建后生成独立.css文件 - 必须配置
gatsby-ssr.js导出onRenderBody,否则 SSR 阶段无法注入生成的 CSS - 和 CSS Modules 不兼容——Linaria 要求样式定义必须出现在组件文件内,不能靠外部
.module.css文件
最容易被忽略的是:所谓“零运行时”,不是指不用 JS 文件,而是指样式不依赖 JS 执行来生效。哪怕你用了 Linaria,如果漏掉 gatsby-ssr.js 的注入逻辑,SSR 输出的 HTML 里就没有样式,首屏依然要等 JS 加载完才能渲染——这就不是真正的零运行时。


















