Storybook Story 级 layout 参数详解:精确控制单个 Story 在 Canvas 中的布局
2026/9/11 8:36:00 网站建设 项目流程

Storybook Story 级 layout 参数详解:精确控制单个 Story 在 Canvas 中的布局

【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook

layout参数是 Storybook 中用于控制 Story 在 Canvas 画布中定位方式的核心参数,它支持在全局(.storybook/preview)、组件(meta)与单个 Story 三个层级分别配置,优先级逐级提升。本文以 Story 级别的layout: 'centered'配置为核心,覆盖 Angular、React、Vue、Svelte、Web Components 等框架在 CSF 3、CSF Next 与 Svelte CSF 三种写法下的完整示例,并结合仓库源码(WebView.ts、base-preview-head.html)剖析其底层实现,帮助你在实际项目中按需控制组件在画布中的摆放位置。

layout 参数是什么

根据官方文档 Story layout 的说明,layout是 Storybook 的参数(parameter)体系中的一员,专门用于控制 Story 在 Canvas 标签页中的定位方式。它接受以下取值:

取值行为说明
centered组件在 Canvas 中水平且垂直居中显示适合按钮、图标、徽章等小尺寸组件,避免其在画布角落出现
fullscreen组件可铺满 Canvas 的整个宽高适合页面级、全屏类组件,去掉所有内边距
padded在组件周围添加额外内边距默认值;组件保留四周留白
none移除任何布局类从源码看是特殊值,用于清除当前布局效果(见下文源码解析)

其中padded是默认值,也就是说当你不设置该参数时,Storybook 默认采用带内边距的布局。

三个配置层级:全局、组件、Story

layout参数可以在三个层级设置,遵循 Storybook 参数合并机制——越具体的层级优先级越高:

  1. 全局层级:在.storybook/preview中设置,作用于项目中的所有 Story。
  2. 组件层级:在组件 stories 文件的meta导出(即默认导出)中设置,作用于该组件的所有 Story。
  3. Story 层级:在单个 Story 对象上设置,只作用于该 Story。这是本文的核心主题,也是原文档 storybook-story-layout-param.md 的代码片段所演示的场景。

例如在全局配置中让所有 Story 居中:

// .storybook/preview.js export default { parameters: { layout: 'centered', }, };

而在组件级别,则将参数放入meta中:

// Button.stories.ts(React 等通用框架) import type { Meta } from '@storybook/your-framework'; import { Button } from './Button'; const meta = { component: Button, // Sets the layout parameter component wide. parameters: { layout: 'centered', }, } satisfies Meta<typeof Button>; export default meta;

当需要在同一个组件的不同 Story 间采用不同布局时(例如按钮组件的大部分 Story 居中展示、而其中一个全宽 Story 使用fullscreen),就需要 Story 级配置,它拥有最高的优先级。

Story 级 layout 配置:各框架完整示例

以下示例均演示在单个 Story 上设置parameters: { layout: 'centered' },文件名为Button.stories.*

CSF 3 写法

Angular(CSF 3)

// Button.stories.ts import type { Meta, StoryObj } from '@storybook/angular'; import { Button } from './button.component'; const meta: Meta<Button> = { component: Button, }; export default meta; type Story = StoryObj<Button>; export const WithLayout: Story = { parameters: { layout: 'centered', }, };

React 等通用框架(CSF 3,JS/JSX)

// Button.stories.js import { Button } from './Button'; export default { component: Button, }; export const WithLayout = { parameters: { layout: 'centered', }, };

通用框架(CSF 3,TS/TSX)

// Button.stories.ts // Replace your-framework with the framework you are using, e.g. react-vite, nextjs, vue3-vite, etc. import type { Meta, StoryObj } from '@storybook/your-framework'; import { Button } from './Button'; const meta = { component: Button, } satisfies Meta<typeof Button>; export default meta; type Story = StoryObj<typeof meta>; export const WithLayout: Story = { parameters: { layout: 'centered', }, };

Svelte(CSF 3,JS)

// Button.stories.js import Button from './Button.svelte'; export default { component: Button, }; export const WithLayout = { parameters: { layout: 'centered', }, };

Svelte(CSF 3,TS)

// Button.stories.ts // Replace your-framework with svelte-vite or sveltekit import type { Meta, StoryObj } from '@storybook/your-framework'; import Button from './Button.svelte'; const meta = { component: Button, } satisfies Meta<typeof Button>; export default meta; type Story = StoryObj<typeof meta>; export const WithLayout: Story = { parameters: { layout: 'centered', }, };

Web Components(CSF 3,JS)

// Button.stories.js export default { component: 'demo-button', }; export const WithLayout = { parameters: { layout: 'centered', }, };

Web Components(CSF 3,TS)

// Button.stories.ts import type { Meta, StoryObj } from '@storybook/web-components-vite'; const meta: Meta = { component: 'demo-button', }; export default meta; type Story = StoryObj; export const WithLayout: Story = { parameters: { layout: 'centered', }, };

Svelte CSF 写法

Svelte CSF(JS)

<!-- Button.stories.svelte --> <script module> import { defineMeta } from '@storybook/addon-svelte-csf'; import Button from './Button.svelte'; const { Story } = defineMeta({ component: Button, }); </script> <Story name="WithLayout" parameters={{ layout: 'centered', }} />

Svelte CSF(TS)写法与 JS 版本完全一致,同样通过<Story parameters={{ layout: 'centered' }} />的方式声明。

CSF Next 🧪 写法

CSF Next 是 Storybook 的下一代 CSF 语法(实验性),通过从../.storybook/preview导入的preview实例,用preview.meta()/preview.story()声明式地组织 stories。

Angular(CSF Next)

// Button.stories.ts import preview from '../.storybook/preview'; import { Button } from './button.component'; const meta = preview.meta({ component: Button, }); export const WithLayout = meta.story({ parameters: { layout: 'centered', }, });

React(CSF Next,TS/TSX 与 JS/JSX 写法一致)

// Button.stories.ts import preview from '../.storybook/preview'; import { Button } from './Button'; const meta = preview.meta({ component: Button, }); export const WithLayout = meta.story({ parameters: { layout: 'centered', }, });

Vue(CSF Next,TS 与 JS 写法一致)

// Button.stories.ts import preview from '../.storybook/preview'; import Button from './Button.vue'; const meta = preview.meta({ component: Button, }); export const WithLayout = meta.story({ parameters: { layout: 'centered', }, });

Web Components(CSF Next,TS 与 JS 写法一致)

// Button.stories.ts import preview from '../.storybook/preview'; const meta = preview.meta({ component: 'demo-button', }); export const WithLayout = meta.story({ parameters: { layout: 'centered', }, });

源码解析:layout 参数在预览层如何生效

理解了配置写法后,我们来看 Storybook 内部是如何消费这个参数的。

布局类映射与默认值

在预览层核心文件 WebView.ts 中,layout参数与 CSS 类名一一映射:

const layoutClassMap = { centered: 'sb-main-centered', fullscreen: 'sb-main-fullscreen', padded: 'sb-main-padded', } as const; type Layout = keyof typeof layoutClassMap | 'none';

Layout类型除了三个合法取值外还包含'none'——从类型定义可见,none是官方认可的"清除布局"特殊值。

应用与校验流程

applyLayout方法(WebView.ts)负责实际应用布局:

applyLayout(layout: Layout = 'padded') { if (layout === 'none') { document.body.classList.remove(this.currentLayoutClass!); this.currentLayoutClass = null; return; } this.checkIfLayoutExists(layout); const layoutClass = layoutClassMap[layout]; document.body.classList.remove(this.currentLayoutClass!); document.body.classList.add(layoutClass); this.currentLayoutClass = layoutClass; }

几个关键实现细节:

  • 默认值'padded':与方法签名一致,未设置layout参数时自动回退到padded,这正是文档所说默认布局的来源。
  • 'none'的处理:不添加任何布局类,而是清除当前已应用的类,可用于覆盖更上层配置、回到无布局样式状态。
  • 切换机制:每次先移除旧类、再添加新类,保证多个 Story 切换时布局不会叠加残留。
  • 非法值告警checkIfLayoutExists(WebView.ts)会对未在映射表中的值通过logger.warn输出警告,提示"desired layout 不是合法选项,可选值为centered, fullscreen, padded, none"——这意味着写错参数值(如'center')不会崩溃,但会在控制台得到明确提示。

调用时机

在每次渲染 Story 前,prepareForStory(WebView.ts)都会读取story.parameters.layout并调用applyLayout

prepareForStory(story: PreparedStory<any>) { this.showStory(); this.applyLayout(story.parameters.layout); // ...滚动位置与 htmlLang 处理 }

由于参数在合并时已按"全局 → 组件 → Story"的顺序完成继承与覆盖,这里拿到的story.parameters.layout已经是该 Story 最终生效的值,从而实现了三级配置的优先级语义。

另外值得注意的是,在 Docs 模式(prepareForDocs,WebView.ts)下,Storybook 会强制applyLayout('fullscreen'),让文档页内容铺满画布——这与 Story 模式下的自定义布局互不影响。

对应 CSS 类定义

这些布局类的实际样式定义在预览 HTML 模板 base-preview-head.html 中:

  • sb-main-centered(base-preview-head.html):将body设为display: flex; align-items: center; min-height: 100vh,并把#storybook-root设为margin: auto,实现水平和垂直双向居中,同时保留1rem内边距与max-height: 100%防止溢出。
  • sb-main-fullscreen(base-preview-head.html):margin: 0; padding: 0; display: block,完全去除留白,让组件占满画布。
  • sb-main-padded(base-preview-head.html):padding: 1rem的带内边距块级布局。

优先级与继承关系

理解 Storybook 的参数合并机制有助于避免"为什么我的全局配置不生效"之类的困惑:

  1. 全局preview中的layout提供项目级默认值;
  2. 组件meta中的layout覆盖全局值,作用于该组件全部 Story;
  3. 单个 Story 上的layout覆盖组件级配置,只影响该 Story

若某 Story 想显式"取消"继承自组件/全局的居中布局,可使用layout: 'none'清除布局类,恢复到无特殊布局状态。

小结

layout参数是控制 Storybook Canvas 展示形态最直接的手段,而 Story 级配置是其中最精细的一层。本文覆盖了:

  • 三种合法取值centered/fullscreen/padded(默认)及特殊值none的语义;
  • 在 CSF 3、CSF Next、Svelte CSF 三种语法下,Angular、React、Vue、Svelte、Web Components 各框架的完整 Story 级配置示例;
  • 从 WebView.ts 的layoutClassMapapplyLayoutcheckIfLayoutExists到 base-preview-head.html 中 CSS 类的完整实现链路;
  • 全局、组件、Story 三级参数继承与覆盖规则,以及 Docs 模式强制fullscreen的特殊行为。

掌握这一参数,你就可以为按钮、图标等小组件一键居中,为全屏页面组件铺满画布,并在同一组件内按 Story 差异化控制展示布局。

【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询