在 Storybook 中初始化 MSW Addon:通过 mswLoader 与 definePreview 为全部 Story 提供接口 Mock 能力
本篇指南讲解如何在 Storybook 项目中初始化 msw-storybook-addon,让基于 Mock Service Worker(MSW)的网络请求拦截能力覆盖到全部 Story。你将掌握两种注册方式——CSF 3 下在 .storybook/preview 中挂载项目级 mswLoader(),以及实验性 CSF Next 下通过 definePreview 的 addons 数组接入 addonMsw(),并同步了解配套的安装、Service Worker 生成、静态资源目录配置以及迁移到 addon v3 的注意点。文中的多框架代码片段来自 msw-addon-initialize.md,完整上下文可见 mocking-network-requests.mdx。
为什么需要在 preview 中完成初始化
MSW 依靠 Service Worker 在浏览器网络层拦截请求并返回 Mock 数据。要让这种拦截能力渗透进 Storybook 的渲染环境,msw-storybook-addon 必须把"启动 / 重置 Mock Server"的逻辑接入到 Storybook 的运行周期中。
其接入点是 Loader 机制。在 Storybook 中,Loader 是在 Story 及其装饰器渲染之前异步执行的数据加载函数,执行结果会通过 render 上下文注入到 Story;其中项目级(global)Loader 通过 .storybook/preview.js 的 loaders 导出对所有 Story 生效(参见 loaders.mdx)。mswLoader() 正是一个为此设计的项目级 Loader:它在每条 Story 渲染前启动 MSW,并在 Story 切换后清理 Handler,从而保证"每条 Story 从干净状态开始,只有自己注册的接口 Mock 生效"。
注意,本文与所有配套片段面向 MSW addon v3。若你仍在使用 v2,需要先完成版本迁移,官方提供了自动迁移 Codemod:
npx msw-storybook-migrate
该命令可自动把 v2 的配置与 Story 迁移到 v3 语法,迁移指南与本节警告完整收录于 mocking-network-requests.mdx 的页面开头。
方式一:CSF 3 下注册项目级 mswLoader
在传统 CSF 3 写法中,注册动作发生在 .storybook/preview.js / .storybook/preview.ts。核心是从 msw-storybook-addon/csf3 子路径导入 mswLoader,并把调用结果 mswLoader() 放进 loaders 数组。这样一个数组会作为项目级 Loader 应用到项目中所有 Story。
以通用渲染器(React、Vue 等各类框架)的 .storybook/preview.js 为例:
import { mswLoader } from 'msw-storybook-addon/csf3';
export default {
/*
* Register the MSW loader for all stories
* See https://github.com/mswjs/msw-storybook-addon#csf-30
* to learn how to customize it
*/
loaders: [mswLoader()],
};
若项目使用 TypeScript,则把 .storybook/preview.ts 写成带类型标注的 Preview 对象。注释中需要把 @storybook/your-framework 替换为实际框架包名(如 @storybook/react-vite、@storybook/nextjs、@storybook/vue3-vite 等):
import type { Preview } from '@storybook/your-framework';
import { mswLoader } from 'msw-storybook-addon/csf3';
const preview: Preview = {
/*
* Register the MSW loader for all stories
* See https://github.com/mswjs/msw-storybook-addon#csf-30
* to learn how to customize it
*/
loaders: [mswLoader()],
};
export default preview;
参数说明与自定义入口
mswLoader() 作为函数调用,其设计目的与文档注释中提示的"learn how to customize it"一致——它支持按需传入初始化选项(例如自定义 onUnhandledRequest 策略、handlers 预置列表等),用于调整 MSW 在当前 Story 渲染前的启动行为。当你不需要任何自定义时,直接使用无参数形式 mswLoader() 即可得到开箱即用的默认配置。
代码中 loaders 是数组而非单个函数,这一点值得展开:Storybook 允许多个项目级 Loader 并行执行、结果合并进 Story 上下文的 loaded 字段,若键冲突则"后定义的优先"。因此即便将来你还要挂载其他数据加载逻辑,也只需在数组中追加元素,而不会被 mswLoader() 覆盖(loaders.mdx)。
方式二:CSF Next 实验性写法接入 addon
对于使用实验性 CSF Next 语法的项目,注册方式发生变化:不再使用 loaders 数组,而是把 msw-storybook-addon 主入口默认导出的 addonMsw() 注册进 definePreview 的 addons 数组。definePreview 需要从当前框架包导入(React 系为 @storybook/react-vite、@storybook/nextjs、@storybook/nextjs-vite,Vue 为 @storybook/vue3-vite,Angular 为 @storybook/angular,Web Components 为 @storybook/web-components-vite)。
React(.storybook/preview.tsx,TypeScript 或 JavaScript 均可):
import { definePreview } from '@storybook/react-vite';
import addonMsw from 'msw-storybook-addon';
export default definePreview({
/*
* Register the MSW loader for all stories
* See https://github.com/mswjs/msw-storybook-addon#csf-next
* to learn how to customize it
*/
addons: [addonMsw()],
});
Vue3(.storybook/preview.ts):
import { definePreview } from '@storybook/vue3-vite';
import addonMsw from 'msw-storybook-addon';
export default definePreview({
addons: [addonMsw()],
});
Angular(.storybook/preview.ts):
import { definePreview } from '@storybook/angular';
import addonMsw from 'msw-storybook-addon';
export default definePreview({
addons: [addonMsw()],
});
Web Components(.storybook/preview.ts):
import { definePreview } from '@storybook/web-components-vite';
import addonMsw from 'msw-storybook-addon';
export default definePreview({
addons: [addonMsw()],
});
在上述各框架下,JS 变体仅把类型导入换成 JS 运行时导入、文件扩展名从 .ts/.tsx 调整为 .js,其余结构完全一致(React 的 JS 变体使用 @storybook/react-vite 的 definePreview,Angular / Vue / Web Components 同理)。CSF Next 目前处于实验阶段,原文片段在 tab 标题中以 🧪 标识,生产项目按官方成熟度提示决定是否采用。
两种方式背后的等价关系
从仓库文档对两种模式的描述可以看出,它们的语义是同一件事的两种语法外壳:CSF 3 用 loaders: [mswLoader()] 表达"项目级全局 Loader",CSF Next 用 addons: [addonMsw()] 表达"为项目注册一个 addon"——二者最终都会让 MSW 拦截能力覆盖所有 Story。迁移工具 npx msw-storybook-migrate 能帮你在这两种形态之间完成 v2→v3 的转换,避免手工改写出错。
初始化前的三步配套配置
preview 中的注册是 MSW addon 接入的"最后一步",在此之前需要完成三项前置配置(详见 mocking-network-requests.mdx 的 Set up the MSW addon 小节)。
1. 安装依赖
MSW addon 与 MSW 本体需要一并安装为开发依赖(msw-addon-install.md),按包管理器选择命令:
npm install msw msw-storybook-addon --save-dev
pnpm add msw msw-storybook-addon --save-dev
yarn add msw msw-storybook-addon --save-dev
2. 生成 Service Worker 文件
若项目尚未使用过 MSW,需要生成 MSW 所需的 Service Worker 文件(msw-generate-service-worker.md):
npx msw init ./public --save
Angular 项目通常需要调整目标目录(例如保存到 src),以确保产物被框架正确托管;--save 参数会把相关配置写入 package.json,避免提交后丢失。
3. 通过 staticDirs 托管生成文件
默认情况下生成的 Service Worker 位于项目的 /public,Storybook 需要通过 staticDirs 把它作为静态资源提供给浏览器加载(main-config-static-dirs.md)。在 .storybook/main.js(或 .storybook/main.ts)中配置:
export default {
framework: '@storybook/your-framework', // 替换为实际框架包名
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
staticDirs: ['../public', '../static'],
};
若你的 MSW 文件输出到别的目录,这里需同步指向该目录;staticDirs 的更多取值规则参见 staticDirs 文档。
初始化完成后如何编写 Mock Story
preview 初始化只负责"接通能力",具体每条 Story 要 Mock 哪些请求,则由 Story 级配置决定。完成上面的注册后,你可以在 YourPage.stories.ts 中通过 beforeEach 拿到注入的 msw 上下文对象,用 msw.use(...) 注册请求处理器。
以 REST 请求为例(msw-addon-configure-handlers-http.md),一个成功、一个失败的 Story 写法如下:
import { http, HttpResponse, delay } from 'msw';
import type { Meta, StoryObj } from '@storybook/react-vite';
import { DocumentScreen } from './YourPage';
const meta = {
component: DocumentScreen,
} satisfies Meta<typeof DocumentScreen>;
export default meta;
type Story = StoryObj<typeof meta>;
const TestData = {
user: { userID: 1, name: 'Someone' },
document: {
id: 1,
title: 'Something',
brief: 'Lorem ipsum dolor sit amet.',
status: 'approved',
},
};
export const MockedSuccess: Story = {
beforeEach({ msw }) {
msw.use(
http.get('https://your-restful-endpoint/', () => {
return HttpResponse.json(TestData);
}),
);
},
};
export const MockedError: Story = {
beforeEach({ msw }) {
msw.use(
http.get('https://your-restful-endpoint', async () => {
await delay(800);
return new HttpResponse(null, { status: 403 });
}),
);
},
};
这里 beforeEach 支持多层作用域——Story 级、组件(meta)级乃至项目级(preview.ts)均可定义,层级越高覆盖范围越广。除了 REST,msw.use(graphql.query('AllInfoQuery', ...)) 同样可用于拦截 GraphQL 查询,成功与失败分支分别返回 data 或 errors(示例见 msw-addon-configure-handlers-graphql.md)。若你仍在使用 v2 的 msw 参数(非 beforeEach)定义 Handler,v3 下应改用上述新语法,或直接运行 npx msw-storybook-migrate 完成自动迁移。
小结
把 MSW addon 接入 Storybook 的关键动作集中在 .storybook/preview 一处:
- CSF 3:从
msw-storybook-addon/csf3导入mswLoader,以项目级loaders: [mswLoader()]注册到默认导出的Preview配置; - CSF Next(实验性):从
msw-storybook-addon导入addonMsw,在框架包definePreview的addons数组中注册addonMsw(); - 前置条件:安装
msw与msw-storybook-addon、用msw init生成 Service Worker、在staticDirs中托管产物; - 落地效果:注册后任意 Story 都能通过
beforeEach({ msw })精确 Mock REST / GraphQL 请求,实现组件在无真实后端下的隔离渲染与测试。
各代码变体与初始化注释原样保留于 msw-addon-initialize.md,完整实践教程见 mocking-network-requests.mdx,可直接对照查阅更多框架的完整示例。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0626
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00