preview.js中import全局CSS是运行时注入,路径必须从.storybook目录出发,仅支持纯CSS或已编译样式;未编译SCSS、CSS Modules及跨域@import均不生效,Sass变量需通过webpackFinal配置sass-loader注入。

preview.js 中 import 全局 CSS 是运行时注入,不是构建流程的一部分
Storybook 的预览 iframe 和主界面是两个独立的 JS 执行上下文。你在组件文件里 import './Button.css',只会影响该组件自身的样式作用域;而预览区(iframe)默认不带任何全局样式,必须显式注入。直接在 preview.js 里 import '../src/style/reset.css',本质是让 Storybook 在每个 Story 渲染前,把这段 CSS 插入到 iframe 的 <head> 中——它不走 Webpack 构建链,也不参与 CSS Modules 或 Sass 编译。
路径错误是 import 失效的第一大原因
从 .storybook/ 目录出发计算相对路径,不是从项目根目录或组件目录。常见错误包括:
-
./src/style.css→ 实际找的是.storybook/src/style.css,应改为../src/style.css -
src/style.css→ 被解析为 node_modules 下同名路径,大概率 404 -
../../src/style.css→ 多了一级..,路径越界,Node.js 报错Cannot find module
建议用 VS Code 的路径自动补全,或在 preview.js 顶部加一行 console.log(require.resolve('../src/style.css')) 快速验证路径是否可达。
误引未编译 SCSS 或 CSS Modules 会导致静默失效
preview.js 的 import 只接受浏览器能直接执行的 CSS 字符串。以下情况不会报错但也不生效:
立即学习“前端免费学习笔记(深入)”;
-
import '../src/style/variables.scss'→ Sass 变量无法被 JS 解析,Webpack 报You may need an appropriate loader -
import '../src/components/Button.module.css'→ CSS Modules 默认启用局部作用域,规则不会注入 iframe<head> -
@import url('https://cdn.example.com/theme.css')写在被 import 的 CSS 文件里 → 浏览器拦截跨域请求,Network 面板显示Blocked: CORS
真正起效的只有纯 CSS、PostCSS 编译后的输出、或手动编译好的 SCSS(如 sass src/main.scss dist/main.css)。
webpackFinal 配置和 preview.js import 不是替代关系,而是分工不同
如果你的组件样式文件(比如 Button.scss)里写了 color: $primary-color,光靠 preview.js 导入 reset.css 是没用的——Sass 变量根本没被解析。这时才需要 webpackFinal 配置 sass-loader 的 additionalData:
webpackFinal: async (config) => {
config.module.rules.push({
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
include: path.resolve(__dirname, '../src'),
options: { additionalData: `@import "${path.resolve(__dirname, '../src/style/variables.scss')}";` }
});
return config;
}
注意:这个配置只影响 .scss 文件的构建过程,不影响 preview.js 里已写的 import 语句;两者可共存,但职责分明——前者解决“组件内 Sass 语法可用”,后者解决“所有 Story 共享一套视觉基底”。
最常被忽略的点是:你以为 import 了某个 CSS 文件就万事大吉,其实它可能空、可能被覆盖、可能压根没进 iframe 的 <head>。打开任意 Story,直接检查预览 iframe 的 Elements 面板,才是唯一可信的验证方式。


















