如何在运行时切换Storybook v7文档主题?
解决Storybook v7文档主题运行时切换问题
问题核心
你当前的实现仅修改了组件自身的主题类和静态的docs.theme参数,但preview.ts中的parameters是初始化时固定的,运行时修改不会触发文档主题更新;而manager.ts通过addons.setConfig可以动态更新UI主题(侧边栏、工具栏等),但文档内容的主题需要单独处理。
解决方案
需要同步更新UI主题和文档内容主题,利用Storybook的全局变量和通道通信实现动态切换:
1. 修改preview.ts实现文档主题动态更新
import type { Preview } from '@storybook/vue3'; import { themes } from '@storybook/theming'; import { addons } from '@storybook/preview-api'; import { UPDATE_GLOBALS } from '@storybook/core-events'; import '../src/theme/index.scss'; import '../src/theme/themes.scss'; // 统一主题切换逻辑 const switchTheme = (themeName: 'light' | 'dark') => { // 切换组件自身主题类 const htmlEl = document.getElementsByTagName('html')[0]; htmlEl.classList.remove('light', 'dark'); htmlEl.classList.add(themeName); // 保存主题到本地存储 const targetTheme = themeName === 'light' ? themes.light : themes.dark; localStorage.setItem('theme', JSON.stringify(targetTheme)); // 更新全局文档主题变量 addons.getChannel().emit(UPDATE_GLOBALS, { globals: { docsTheme: targetTheme }, }); // 通知manager切换UI主题 addons.getChannel().emit('syncUiTheme', targetTheme); // 强制重新渲染故事和文档 addons.getChannel().emit('FORCE_RE_RENDER'); }; // 初始化主题 const savedTheme = JSON.parse(localStorage.getItem('theme') || JSON.stringify(themes.light)); const initialTheme = savedTheme === themes.light ? 'light' : 'dark'; switchTheme(initialTheme); const preview: Preview = { parameters: { layout: 'centered', actions: { argTypesRegex: '^on[A-Z].*' }, controls: { matchers: { color: /(background|color)$/i, date: /Date$/, }, }, docs: { theme: savedTheme }, // 初始化文档主题 }, // 定义全局变量用于动态更新文档主题 globals: { docsTheme: savedTheme }, // 监听全局变量变化,同步更新文档参数 globalTypes: { docsTheme: { name: 'Docs Theme', description: '切换文档内容主题', defaultValue: savedTheme, onChange: (newTheme) => { addons.getChannel().emit('updateParameters', { docs: { theme: newTheme }, }); }, }, }, }; // 测试延迟切换(实际项目替换为工具栏按钮/交互触发) setTimeout(() => switchTheme('dark'), 4000); export default preview;
2. 修改manager.ts同步UI主题
import { addons } from '@storybook/manager-api'; import { themes } from '@storybook/theming'; import { addons as previewAddons } from '@storybook/preview-api'; // 初始化UI主题 const savedTheme = JSON.parse(localStorage.getItem('theme') || JSON.stringify(themes.light)); addons.setConfig({ theme: savedTheme }); // 监听preview的主题同步事件 previewAddons.getChannel().on('syncUiTheme', (theme) => { addons.setConfig({ theme }); });
关键原理说明
- 全局变量(globals):通过定义
docsTheme全局变量,实现运行时动态传递主题配置。 - 通道通信(Channel):在preview和manager之间建立通信,确保UI主题与文档主题同步切换。
- 动态参数更新:通过
updateParameters事件修改docs.theme参数,触发文档内容重新渲染主题样式。
注意事项
- 确保
@storybook/theming版本与Storybook核心版本一致,避免主题对象不兼容。 - 若使用自定义MDX文档样式,需基于
light/dark类做适配,确保样式随主题切换。
内容的提问来源于stack exchange,提问作者therabbityouknow
相关产品推荐
相关产品推荐

