最轻量方案是用 gatsby-plugin-sass,但需绕过 js import 以生成独立 css 文件;核心是在 gatsby-ssr.js 中读取 public/static/css/ 下带 hash 的编译后 css 并注入 link 标签,或改用 linaria 实现零运行时。

直接用 gatsby-plugin-sass 是最轻量、最符合 Gatsby 构建链路的做法,但“优雅”不在于插件本身,而在于绕过 JS 模块注入、避免运行时 CSS-in-JS 开销,并确保编译产物可被独立加载。
为什么 import './style.scss' 会把 CSS 打进 JS 文件?
因为 Gatsby 默认启用 gatsby-plugin-sass 后,所有 import 的 .scss 文件仍走 Webpack 的 style-loader + css-loader 流程,最终 CSS 被内联为 JS 字符串,在运行时动态插入 <style></style> 标签——这不是零运行时,也不是真正分离的资源。
- 你能在
public/app-xxx.js里搜到大量 CSS 字符串片段 -
gatsby build后打开public/目录,找不到独立的.css文件 - 浏览器 Network 面板看不到
style.css请求,只有 JS 文件加载
怎样让 Sass 编译出独立 CSS 文件并 link 加载?
必须脱离 JS import 链路,改用构建后静态文件注入。核心是:在 gatsby-ssr.js 中读取已生成的 CSS(路径如 public/static/css/app.123abc.css),再注入为 <link rel="stylesheet">。
- 确认
gatsby-plugin-sass已启用,且未配置cssLoaderOptions强制启用 CSS Modules - 构建完成后,CSS 实际已输出到
public/static/css/,文件名带 hash,这是 Webpack 提取插件(mini-css-extract-plugin)的默认行为 - 在
gatsby-ssr.js中用fs.readFileSync同步读取该文件(仅构建时有效),再用setHeadComponents注入:const fs = require('fs') const path = require('path') exports.onRenderBody = ({ setHeadComponents }, pluginOptions) => { const cssPath = path.join(process.cwd(), 'public', 'static', 'css', 'app.*.css') // 注意:需实际匹配 hash 文件名,可用 glob 或构建后固定命名(见下条) const cssFile = fs.readFileSync(cssPath.replace('*', 'abcd123'), 'utf8') setHeadComponents([ <link key="sass-css" rel="stylesheet" href="/static/css/app.abcd123.css"> ]) } - 更稳妥做法:构建前用
gatsby-node.js把 CSS 文件名写入global.__GATSBY_SASS_CSS_HASH__,或生成public/static/css/manifest.json记录真实文件名
有没有更彻底的零运行时替代方案?
有,linaria 是目前 Gatsby 生态中唯一能真正实现“写 Sass 风格、产出纯 CSS、零 JS 运行时”的方案,但它不处理传统 .scss 文件,而是用 JS tagged template literals 写样式。
- 它不依赖 Webpack loader,靠 Babel 插件在构建时解析
css`...`,直接输出标准 CSS 文件并自动注入<link> - 你需要放弃
@import和@use,改用 JS 方式组织变量和 mixin(例如const primary = '#3498db') - 与 Gatsby 集成需额外配置
gatsby-plugin-linaria,并确保babel-plugin-linaria在 Babel 配置中前置 - 无法复用现有
_mixins.scss或 Bootstrap SCSS,迁移成本高——适合新项目,不适合已有 Sass 代码库
真正容易被忽略的是:Gatsby 的 CSS 提取行为默认只对入口 JS 文件生效,如果你在页面组件里 import 了多个 .scss,它们可能被合并进不同 chunk 的 JS,导致 public/static/css/ 下出现多个 hash 文件,而不是单一 app.*.css。这时必须在 gatsby-node.js 中通过 onCreateWebpackConfig 调整 mini-css-extract-plugin 的 filename 和 chunkFilename,强制收敛输出。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











