首页
/ Storybook Code Panel 使用指南:用 parameters.docs.codePanel 在 Canvas 中展示 Story 实时源码

Storybook Code Panel 使用指南:用 parameters.docs.codePanel 在 Canvas 中展示 Story 实时源码

2026-09-07 14:02:10作者:咎竹峻Karen

Code Panel(代码面板)是 Storybook Docs 提供的一个面板型功能,在 Canvas(画布)浏览模式选中某个 Story 时,即时渲染该 Story 的源码片段,替代已停用的 Storysource 插件。本文以 docs/_snippets/code-panel-enable-in-preview.md 为骨架,结合 addon-docs 源码,讲清它的启用方式、作用范围(全局 / 组件 / Story 三级)以及与 Source 参数体系的联动关系,帮助你在 React、Vue、Angular、Web Components 等任意受支持框架下快速把"看代码"融入日常组件开发工作流。

Code Panel 是什么:从 Storysource 到内置 Docs 面板

在 Storybook 9 之前,要在预览界面查看当前 Story 的源码,通常依赖独立的 @storybook/addon-storysource 插件及其配套的 source-loader 来注入并展示源码。该方案需要额外安装插件与 loader,且在架构升级后逐渐被维护方弃用。根据仓库根目录 MIGRATION.md 的记录,Storybook 9 中 @storybook/addon-storysource@storybook/source-loader 已被移除,替代方案正是由 @storybook/addon-docs 内置提供的 Code Panel——它在功能上与原插件类似,但集成方式更简单、性能表现更好,且与 Docs 的源码渲染体系复用同一套实现(见 MIGRATION.md 中 “Storysource Addon removed” 一节,可对照查看移除步骤)。

Code Panel 面板在 Canvas 视图中展示 Story 源码的界面

从渲染机制看,Code Panel 并非把 .stories 文件当作文本"原样打印",而是渲染 "当前参数的 Story 源码":Story 中通过 args 定义的输入值会被替换成实际生效的值后再输出。例如某个带 childrenvariant 等参数的 Button Story,在调整控制台参数后,面板里的 JSX 会同步显示控件当前传入的值,从而直观呈现"渲染此状态的代码长什么样"。

在 .storybook/preview 中全局启用 Code Panel

Code Panel 默认关闭,需要显式打开。推荐在 .storybook/preview.* 配置中把 parameters.docs.codePanel 设为 true,这样它会作用于项目中全部 Story,无需逐个组件文件重复配置。配置项的类型定义位于 code/addons/docs/src/types.ts

/**
 * Enable the Code panel.
 */
codePanel?: boolean;

下面的代码片段完整覆盖了不同框架与写法的全局启用方式,可依项目情况直接复制使用。

CSF 3(JavaScript,通用渲染器)

export default {
  parameters: {
    docs: {
      codePanel: true,
    },
  },
};

CSF 3(TypeScript,通用渲染器)

// Replace your-framework with the framework you are using (e.g., react-vite, vue3-vite, angular, etc.)
import type { Preview } from '@storybook/your-framework';

const preview: Preview = {
  parameters: {
    docs: {
      codePanel: true,
    },
  },
};

export default preview;

使用 TS 时请务必把 your-framework 换成你实际安装的框架包,例如 @storybook/react-vite@storybook/vue3-vite@storybook/angular 等。Preview 类型能对 parameters 等字段做编译期校验,避免手写拼错配置。

CSF Next(React,实验性写法 🧪)

// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
import { definePreview } from '@storybook/your-framework';

export default definePreview({
  parameters: {
    docs: {
      codePanel: true,
    },
  },
});
// Replace your-framework with the framework you are using (e.g., react-vite, nextjs, nextjs-vite)
import { definePreview } from '@storybook/your-framework';

export default definePreview({
  parameters: {
    docs: {
      codePanel: true,
    },
  },
});

CSF Next(Vue 3)

import { definePreview } from '@storybook/vue3-vite';

export default definePreview({
  parameters: {
    docs: {
      codePanel: true,
    },
  },
});
import { definePreview } from '@storybook/vue3-vite';

export default definePreview({
  parameters: {
    docs: {
      codePanel: true,
    },
  },
});

CSF Next(Angular)

import { definePreview } from '@storybook/angular';

export default definePreview({
  parameters: {
    docs: {
      codePanel: true,
    },
  },
});

CSF Next(Web Components)

import { definePreview } from '@storybook/web-components-vite';

export default definePreview({
  parameters: {
    docs: {
      codePanel: true,
    },
  },
});
import { definePreview } from '@storybook/web-components-vite';

export default definePreview({
  parameters: {
    docs: {
      codePanel: true,
    },
  },
});

说明:带 "CSF Next 🧪" 标签的是使用新一代组件类型推导 API(definePreview 配合 preview.meta())的写法,尚属实验性阶段;上方的 CSF 3 写法(export default 对象 + 类型标注)在当前版本中依然完全可用。两种写法最终都会落到 parameters.docs.codePanel 这一个布尔开关上,面板注册逻辑完全一致。

按组件或按 Story 精细控制

不需要全局开启时,可以把开关下沉到更细的粒度,这也是 docs/_snippets/code-panel-in-meta-and-story.md 里给出的用法:在 Meta 层开启,让本文件所有 Story 都显示 Code Panel;再在个别 Story 层用 codePanel: false 单独关闭。

以 CSF 3 + React 为例:

import type { Meta, StoryObj } from '@storybook/react-vite';

import { Button } from './Button';

const meta = {
  component: Button,
  parameters: {
    docs: {
      // 👇 Enable Code panel for all stories in this file
      codePanel: true,
    },
  },
} satisfies Meta<typeof Button>;
export default meta;

type Story = StoryObj<typeof meta>;

// 👇 This story will display the Code panel
export const Primary: Story = {
  args: {
    children: 'Button',
  },
};

export const Secondary: Story = {
  args: {
    children: 'Button',
    variant: 'secondary',
  },
  parameters: {
    docs: {
      // 👇 Disable Code panel for this specific story
      codePanel: false,
    },
  },
};

JavaScript 版本与此等价,只是去掉 satisfies Meta<typeof Button> 的类型约束:

import { Button } from './Button';

export default {
  component: Button,
  parameters: {
    docs: {
      // 👇 Enable Code panel for all stories in this file
      codePanel: true,
    },
  },
};

// 👇 This story will display the Code panel
export const Primary = {
  args: {
    children: 'Button',
  },
};

export const Secondary = {
  args: {
    children: 'Button',
    variant: 'secondary',
  },
  parameters: {
    docs: {
      // 👇 Disable Code panel for this specific story
      codePanel: false,
    },
  },
};

Svelte(Svelte CSF / CSF 3)、Vue 3、Web Components、Angular 及 CSF Next 写法的完整对照示例,可直接参考 docs/_snippets/code-panel-in-meta-and-story.md 中的其余代码块——模式都是相同的:Meta 层开全局、Story 层做例外覆盖。三层配置(preview → meta → story)遵循 Storybook 常规的参数合并语义,离 Story 越近的层级优先级越高,因此这种"开启 + 定向豁免"的组合方式最适合组件库这种既有通用需求又有少量特例的场景。

Code Panel 在 addon-docs 中是如何被注册的

全局启用背后的实现,可以在 docs 插件的 manager 侧看到完整链路。code/addons/docs/src/manager.tsx 中,插件通过 addons.register 注册了一个名为 Code 的面板:

addons.register(ADDON_ID, (api) => {
  addons.add(PANEL_ID, {
    title: 'Code',
    type: types.PANEL,
    paramKey: PARAM_KEY,
    disabled: (parameters) => !parameters?.docs?.codePanel,
    match: ({ viewMode }) => viewMode === 'story',
    render: ({ active }) => { /* ... */ },
  });
});

从这段注册代码可以提炼出三个关键行为:

  • disabled 由参数驱动:只有当 parameters.docs.codePanel 为真值时面板才会出现,否则标签页整体隐藏——这正是为什么上面所有配置示例都在"开启"这一个布尔开关上做文章。
  • match 限定 viewMode === 'story':Code Panel 只出现在 Canvas(story)视图,Docs 页面里想展示源码则应使用 Source 块(Autodocs 场景),两者定位不同。
  • 源码来自频道事件 SNIPPET_RENDERED:渲染函数通过 api.getChannel() 读取 SNIPPET_RENDERED 事件携带的 { id, source, format, warning }(见 manager.tsx 的载荷类型定义),面板组件据此拿到去 Storybook 渲染端抽取、已经过转换的源码字符串。

面板组件 CodePanel 本身还做了两件对体验很重要的事(manager.tsx):

  1. 防串台:频道上监听 SNIPPET_RENDERED 时,会比对事件中的 id 与当前选中的 currentStoryId。若用户在源码抽取完成前切换了 Story,迟到的旧事件会被丢弃,避免慢速抽取覆盖掉新选中 Story 的源码。
  2. 支持自定义覆盖与暗色主题:展示时优先使用 parameters.docs.source.code 手工指定的代码,否则回退到事件抽取结果;面板配色则跟随当前 Storybook UI 主题(theme.base 非 light 即视为暗色),保证截图、教学素材与整体 UI 观感一致。

面板内容可定制:复用 Source 块参数体系

Code Panel 展示的代码片段与 Source 文档块(Autodocs 页面也在用同一套渲染管线)完全同源,因此也可以复用 Source 的 parameters.docs.source.* 配置来定制输出。docs 侧的类型定义中,codePanelsourcecanvas 等一并挂在 parameters.docs 命名空间下(见 types.ts),这意味着它们天然可以互相配合。常用可定制项包括:

  • code:直接提供自定义源码字符串,面板将原样渲染这段代码,并忽略事件抽取出的 warning;适合展示"伪代码"或需要抹掉敏感实现细节的场景。
  • language / dark:控制高亮语言与明暗模式(默认值分别来自 parameters.docs.source.languageparameters.docs.source.dark)。
  • transform:异步转换函数,可基于原始源码与 Story 上下文动态改写后再展示(例如全局用 Prettier 统一格式化所有代码片段,示例见 parameters-docs-source-transform-in-preview.md)。
  • excludeDecorators:决定源码片段是否包含装饰器注入的包裹代码。
  • typeauto / code / dynamic,其中 dynamic 表示"渲染带当前 arg 值的动态源码"——这正是 Code Panel 在 Canvas 中随控件参数实时更新源码的内在支撑。

在实际渲染优先级上,面板组件先取 parameters.docs.source.code 的手工值,其次才是频道事件刚推送的抽取结果(manager.tsx)。因此,如果你在某个 Story 上写了一段 source.code,它会完全覆盖自动抽取的片段,例如官方模板中的演示 Story 就把面板内容覆盖为一句自定义 JSX。

仓库内验证:模板示例与端到端测试

仓库已经内置了 Code Panel 的演示与验证设施,非常适合用来对照理解上述行为:

  • 示例组件故事位于 code/addons/docs/template/stories/codePanel/index.stories.tsx。其中 Default 在 Meta 层开启了 codePanel: true 并配合 canvas.sourceState: 'shown'CustomCode 通过 docs.source.code 覆盖展示 <button>Custom code</button>WithoutPanel 则在 Story 层用 codePanel: false 把面板关掉——正好一一对应本文讲解的"启用 / 覆盖 / 豁免"三种用法。该文件的注释还指出它同时服务于 code/e2e-internal/story-docs.spec.ts 这条端到端测试。
  • 想在实际组件库里快速复现,只需按上节示例修改你的 .storybook/preview.* 与任意 .stories.* 文件,然后重新启动 dev server,选中相应 Story,观察工具栏下方是否多出一个 Code 标签页。

从 Storysource 迁移到 Code Panel

对于还在使用旧插件的老项目,仓库根目录 MIGRATION.md("Storysource Addon removed" 一节)给出了清晰的迁移路径:

  1. 从项目依赖中移除旧插件及 loader(@storybook/addon-storysource@storybook/source-loader 在 Storybook 9 中已不再提供),例如通过 Storybook CLI 的 addon 管理命令 npx storybook remove @storybook/addon-storysource 完成卸载。
  2. .storybook/preview.js(或对应 TS 文件)中开启 Code Panel,即本文第一节的 parameters: { docs: { codePanel: true } } 配置。
  3. 若之前仅在部分组件上使用 Storysource,可改为在对应 Meta 或 Story 层配置开关,做到等价且更细粒度的控制。

由于 Code Panel 直接复用了 Docs 的 Source 渲染管线,迁移后通常不需要再维护独立的源码加载器与高亮配置,行为一致性也更好——这是迁移相比继续维护旧插件更省心的主要原因。

小结

一句话概括 Code Panel 的使用模型:先在 .storybook/preview.*parameters.docs.codePanel: true 全局打开,再按组件或 Story 覆盖关闭,最后用 parameters.docs.source.* 微调每个片段的内容与样式。 它只出现在 story 视图,把"源码即参数态"的查看体验收编进 Canvas,配合 Source 块在 Docs 页面的存在,共同构成 Storybook 9 及以后版本中围绕组件源码的完整展示闭环。

登录后查看全文
热门项目推荐
相关项目推荐