MetaMask Extension 的 Storybook 组件开发指南:启动、配置与 UI 组件测试实战
2026/9/15 13:55:13 网站建设 项目流程

MetaMask Extension 的 Storybook 组件开发指南:启动、配置与 UI 组件测试实战

【免费下载链接】metamask-extension:globe_with_meridians: :electric_plug: The MetaMask browser extension enables browsing Ethereum blockchain enabled websites项目地址: https://gitcode.com/GitHub_Trending/me/metamask-extension

MetaMask 浏览器扩展仓库(metamask-extension)将 Storybook 作为其设计系统(Design System)的核心组成部分,用于在隔离环境中浏览、调试和交互式验证 UI 组件。本指南将带你完成从克隆仓库、安装依赖到启动 Storybook 的完整流程,并结合仓库中的 .storybook/main.js、.storybook/preview.js 等配置文件,深入解析 MetaMask 如何定制 Storybook 的 Webpack 构建、国际化、Redux 状态与主题切换,帮助你在本地高效地开发与测试 MetaMask 的组件和页面。

一、Storybook 在 MetaMask 项目中的角色

MetaMask 的界面由数千个 React 组件构成(仓库中 ui/components 与 ui/pages 目录下共有上千个组件与页面文件),它们彼此依赖 Redux 状态、后台连接(background connection)与国际化消息。若直接在完整扩展环境中调试单个组件,需要处理大量环境依赖。

Storybook 正是为解决这一问题而引入的:它把每个组件当作一个独立的"故事"(story)渲染在浏览器中,让开发者无需启动完整的扩展即可:

  • 快速浏览组件的不同视觉状态;
  • 通过 Controls 面板实时修改组件属性(props);
  • 以亮色 / 暗色主题、多种语言环境预览效果;
  • 配合 a11y 与 docs 插件检查可访问性并生成组件文档。

从仓库的 package.json 可以看到,MetaMask 将 Storybook 与其设计系统深度绑定,并配套了构建、部署与自动化测试脚本,下文会逐一说明。

二、快速开始:克隆仓库并启动 Storybook

官方在 .storybook/README.md 中给出了最小启动路径。MetaMask 使用 Yarn 作为包管理器(仓库根目录存在 yarn.lock),因此不要使用npm install,否则可能因依赖版本不一致而失败。

# 1. 克隆仓库(或使用你已有的本地副本) git clone https://gitcode.com/GitHub_Trending/me/metamask-extension # 2. 进入仓库根目录并安装依赖 cd metamask-extension yarn # 3. 启动 Storybook 开发服务器 yarn storybook

启动成功后,终端会输出类似下面的信息:

info Storybook started on => http://localhost:6006/

在浏览器中打开 http://localhost:6006/ 即可看到 Storybook 界面。从此处你可以浏览项目中的各类组件,并直接在右侧面板修改组件的部分属性,实时观察渲染变化。

yarn storybook对应的脚本定义在 package.json:

"storybook": "storybook dev -p 6006 -c .storybook"

它等价于执行 Storybook CLI 的dev命令,指定端口6006、配置文件目录.storybook

提示:MetaMask 是一个包含大量依赖的大型仓库,首次yarn安装耗时较长。如果启动时出现依赖解析或兼容性问题,建议确认 Node.js 版本与仓库要求一致,并优先清空本地 node_modules 后重新安装。

三、常用 Storybook 相关脚本一览

除开发启动外,package.json 中还定义了多个与 Storybook 生命周期相关的脚本:

脚本命令用途
storybookstorybook dev -p 6006 -c .storybook启动本地开发服务器(默认 6006 端口)
storybook:buildstorybook build -c .storybook -o storybook-build构建静态站点到storybook-build/目录
storybook:deploystorybook-to-ghpages --existing-output-dir storybook-build --remote storybook --branch main将静态构建结果发布到 GitHub Pages
test-storybooktest-storybook -c .storybook运行 Storybook 交互测试(基于 @storybook/test-runner)
test-storybook:ci先构建并用http-server服务 6006 端口,wait-on就绪后运行test-storybook在 CI 中端到端执行 Storybook 测试

配套依赖方面,仓库固定使用 Storybook9.1.20版本(见 package.json),并安装了@storybook/react-webpack5@storybook/addon-a11y@storybook/addon-docs@storybook/addon-webpack5-compiler-babel@storybook/storybook-deployer@storybook/test-runner等插件(package.json)。

四、深入 .storybook 配置目录

.storybook目录是整个 Storybook 实例的中枢,包含以下关键文件:

.storybook/ ├── README.md # 官方使用说明 ├── main.js # 主配置:stories 路径、addons、Webpack ├── preview.js # 全局参数、装饰器、工具栏、Redux 集成 ├── i18n.js / locales.js # 国际化 Provider 与 40+ 语言消息 ├── test-data.js # 注入 Storybook 的 Redux 初始状态 ├── metamask-storybook-theme.js # Storybook 品牌主题 ├── metametrics.js # MetaMetrics 埋点 Provider 包装 ├── preview-body.html # 自定义预览 HTML(挂载根节点) ├── index.css # 全局样式 ├── actions/sb-send-action.js # send 流程的 action mock ├── initial-states/ # 各审批页面的初始状态 fixture ├── reducers/sb-history-reducer.js ├── shims/scure-bip39-english.js # 助记词库浏览器端 shim └── images/ # 演示用 token / 图标资源

4.1 主配置 main.js:stories、addons 与 Webpack 定制

.storybook/main.js 是 Storybook 的入口配置,核心内容包括:

(1)扫描范围与插件

stories: ['../ui/**/*.stories.js', '../ui/**/*.stories.tsx'], addons: [ '@storybook/addon-a11y', '@storybook/addon-docs', '@storybook/addon-webpack5-compiler-babel', ],

Storybook 会递归扫描ui目录下所有*.stories.js*.stories.tsx文件。经检索,当前仓库中存在 300+ 个 stories 文件(如ui/components/app/alert-system/general-alert/general-alert.stories.tsx),分布在ui/componentsui/pages各处。

(2)静态资源与环境变量

staticDirs: ['../app', './images'], env: (config) => ({ ...config, INFURA_PROJECT_ID: process.env.INFURA_STORYBOOK_PROJECT_ID || '', ENABLE_ENFORCED_SIMULATIONS: process.env.ENABLE_ENFORCED_SIMULATIONS || '', }),
  • staticDirsapp/.storybook/images/暴露为静态资源目录,故事中可直接引用其中的图标、字体等;
  • env.metamaskrc文件(见dotenv.config)读取INFURA_STORYBOOK_PROJECT_ID等变量注入构建环境,避免将真实密钥带入 Storybook。

(3)Webpack 定制

webpackFinal是配置中最复杂的部分,其作用可归纳为四类:

  1. 模块别名替换(mock):将webextension-polyfill替换为 ui/mocks/webextension-polyfill.js,把多层store/actionshooks/useAnalytics替换为对应 mock;@metamask/scure-bip39的英文词表也替换为 .storybook/shims/scure-bip39-english.js。
  2. Node polyfill / fallback:对child_processfscrypto等 Node 内置模块设置false或浏览器替代实现(如process/browserstream-browserify),使扩展代码可在浏览器中运行。
  3. SCSS 处理链路:为*.scss追加style-loader → css-loader → postcss-loader(tailwindcss + autoprefixer)→ sass-loader(sass-embedded,modern-compiler API)的完整加载链,loadPaths包含ui/cssnode_modules,保证 MetaMask 的 Tailwind 样式体系在 Storybook 中生效。
  4. 字体与全局变量:通过CopyWebpackPlugin复制ui/css/utilities/fonts/与 FontAwesome webfonts 到输出目录,并通过ProvidePlugin注入Bufferprocess全局变量。

框架方面,配置为@storybook/react-webpack5,并关闭了reactDocgen与 TS 类型检查(typescript: { reactDocgen: false, check: false })以加速构建。

4.2 全局预览 preview.js:装饰器、工具栏与状态注入

.storybook/preview.js 负责 Storybook 的全局渲染环境,主要包含:

(1)全局参数(parameters)

parameters: { backgrounds: { options: { default: { name: 'default', value: 'var(--color-background-default)' }, alternative: { name: 'alternative', value: 'var(--color-background-alternative)' }, }, }, options: { storySort: { order: ['Getting Started', 'Foundations', 'Components', 'Pages'], }, }, controls: { expanded: true }, }

背景色直接复用设计 token 的 CSS 变量(--color-background-default/--color-background-alternative),并配置了故事排序规则与默认展开的 Controls 面板。

(2)全局工具栏(globalTypes)

globalTypes: { locale: { /* 语言切换:遍历 app/_locales/index.json 生成语言列表 */ }, theme: { name: 'Color Theme', defaultValue: 'both', toolbar: { items: [ { value: 'light', title: 'Light', icon: 'sun' }, { value: 'dark', title: 'Dark', icon: 'moon' }, { value: 'both', title: 'Light/Dark', icon: 'paintbrush' }, ], dynamicTitle: true, }, }, }

工具栏提供Locale(国际化语言)与Color Theme(亮 / 暗 / 双主题)两个切换器。主题逻辑会读取prefers-color-scheme系统偏好,并通过设置document.documentElementdata-theme属性来切换设计 token 变量。

(3)Redux Store 与后台连接 mock

export const store = configureStore(testData); const proxiedBackground = new Proxy({}, { get(_, method) { return function () { return new Promise(() => {}); }; }, }); setBackgroundConnection(proxiedBackground);
  • Store 由 ui/store/store.js 的configureStore基于 .storybook/test-data.js 中的测试状态创建。这份状态包含账户、交易记录、token 缓存、Snap 列表、市场数据等完整的 Redux 切片,保证组件"看起来像在真实扩展中运行";
  • 后台连接通过一个 Proxy 对象 mock:任何对 background 方法的调用都会返回一个永不 resolve 的 Promise,从而避免真实的后台通信。

(4)全局装饰器(decorators)

metamaskDecorator将每个故事包裹在Provider(store) → QueryClientProvider → MemoryRouter → AlertMetricsProvider → I18nProvider → Routes的层级中,其中:

  • 路由支持通过故事参数parameters.initialEntriesparameters.path自定义(默认路径为*、入口为/);
  • I18nProvider负责将当前语言的消息字典通过I18nContext提供给组件(详见下文);
  • withColorScheme装饰器负责按工具栏主题渲染浅色 / 深色包裹层。

(5)预览 HTML

.storybook/preview-body.html 预置了#custom-root#popover-content两个挂载节点,供弹层类组件(Popover、Tooltip 等)渲染使用。

五、国际化与多语言支持

MetaMask 支持 40+ 种语言(app/_locales 目录下每种语言一个messages.json)。Storybook 环境通过两处实现语言切换:

  • .storybook/locales.js 静态导入所有语言包(amardeenzh_CNzh_TW等),导出为按 locale code 索引的字典;
  • .storybook/i18n.js 实现I18nProvider:其t函数优先从当前语言字典取消息,取不到时回退到英文(en),底层复用 ui/helpers/utils/i18n-helper 的getMessage

语言列表本身来自 app/_locales/index.json,在preview.js中生成工具栏下拉项。

六、编写一个真实的故事(Story)

MetaMask 的故事采用 Component Story Format(CSF)。以 ui/components/app/alert-system/general-alert/general-alert.stories.tsx 为例:

import { Severity } from '../../../../helpers/constants/design-system'; import { SecurityProvider } from '../../../../../shared/constants/security-provider'; import GeneralAlert from './general-alert'; export default { title: 'Confirmations/Components/GeneralAlert', component: GeneralAlert, argTypes: { description: { control: 'text', defaultValue: mockPlainText }, provider: { control: { type: 'select' }, options: ['none', ...Object.values(SecurityProvider)], mapping: { none: null }, }, severity: { control: { type: 'select' }, options: [Severity.Danger, Severity.Info, Severity.Warning], }, onClickSupportLink: { action: 'onClickSupportLink' }, }, };

写作要点:

  • title决定故事在侧边栏中的分组层级(Confirmations/Components/...);
  • argTypes定义 Controls 面板的可调参数,select类型配合optionsmapping可在下拉中映射真实对象(如SecurityProvider);
  • action类型的控制项会把组件回调事件打印到 Actions 面板,便于交互验证。

除组件故事外,仓库还提供了审批类页面的初始状态 fixture(.storybook/initial-states/approval-screens 下包含add-token.jsadd-suggested-token.jstoken-approval.js)以及 send 流程的 action mock(.storybook/actions/sb-send-action.js),方便直接以"真实状态"预览复杂页面。

七、用 test-runner 自动化测试故事

MetaMask 使用@storybook/test-runner对故事进行自动化冒烟测试:yarn test-storybook会启动本地 Storybook 并逐一渲染所有故事,捕获渲染错误与控制台异常。

CI 场景下可使用 package.json 中的test-storybook:ci:先用yarn storybook:build构建静态站点,再用http-server在 6006 端口提供静态服务,wait-on等待端口就绪后运行测试,整个过程由concurrently并行编排,保证测试环境与真实构建产物一致。

八、常见问题与注意事项

  • 端口占用:默认端口为 6006,若被占用可通过yarn storybook --port <port>指定新端口(等价于修改-p参数)。
  • 密钥安全:Storybook 环境中的 Infura 项目 ID 默认读取.metamaskrc中的INFURA_STORYBOOK_PROJECT_ID,未配置时为空字符串;切勿将生产密钥硬编码进故事或配置。
  • 遥测main.js中通过core.disableTelemetry: true关闭了 Storybook 遥测上报。
  • 首次构建较慢:仓库规模大、SCSS 链路长,首次构建需要较长时间属正常现象;storybook:build产出的静态站点可直接用于分享组件库快照或部署到 GitHub Pages(storybook:deploy)。
  • 新组件如何接入:在组件同级目录新建组件名.stories.tsx并遵循 CSF 规范导出默认元数据即可,无需修改任何全局配置,Stories 扫描(../ui/**/*.stories.tsx)会自动发现它。

通过以上配置与流程,你可以在 MetaMask 扩展仓库中独立、高效地开发与验证 UI 组件,并借助主题切换、多语言与自动化测试,确保组件在真实产品环境中的表现一致可靠。

【免费下载链接】metamask-extension:globe_with_meridians: :electric_plug: The MetaMask browser extension enables browsing Ethereum blockchain enabled websites项目地址: https://gitcode.com/GitHub_Trending/me/metamask-extension

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

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

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

立即咨询