Storybook 使用手册——实战演练

/images/page-cover/nasa_multi-axis_gimble_rig.jpg

在Storybook使用手册系列文章中,介绍了Storybook的基本概念和文档编写方式。在这篇文章中,将在一个现有项目中实操 Storybook 的接入过程。

选用我的个人博客 作为演示项目,处于以下几点考虑:
博客是典型的Web应用,可以提供较为全面的实操演练;博客中组件数量不会太多,工作量不大;方便博客后续的开发维护

https://storybook.js.org/showcase/ 有不少优秀的案例,感兴趣的同学也可以参考。

初始化

进入项目中,执行Storybook提供的初始化命令,在项目中快速初始化对应的配置。

npx storybook init

Storybook会创建两个目录:.storybookstories 。前者是Storybook的配置目录,后者是Storybook的文档目录,可以在配置中自定义修改。

d85dacea37007fdf

同时,对package.json也有修改,除了增加Storybook需要的依赖之外,还增加了两个script命令,用于启动开发服务器和打包文档

9aaf1069a3152541

其实到这一步,Storybook的初始化就已经完成了,非常简单。不得不说,Storybook提供的工具链对与开发者来说确实非常友好。

社区有很多优秀的开发工具,提供了非常好的开发体验。应该多多学习这种从产品的角度考虑用户体验的创作思路。

Storybook 默认创建stories目录,提供了一些demo。本人倾向于将组件的Story的配置于组件代码放在一起维护。

编写组件文档

项目中有一个圆形箭头,用于链接的装饰。

63babed6ec3aa815

9b5539aa17cfe4e8

我已经将其封装为CircleArrow组件,现在来为它增加一个漂亮的文档。在组件所在目录中创建一个CircleArrow.stories.tsx 文件。

import React from 'react'; import { ComponentStory, ComponentMeta } from '@storybook/react'; import { CircleArrow } from './index'; export default { title: 'Component/CircleArrow', component: CircleArrow, argTypes: { backgroundColor: { control: 'color' }, }, } as ComponentMeta<typeof CircleArrow>; const Template: ComponentStory<typeof CircleArrow> = (args) => <CircleArrow {...args} />; export const Default = Template.bind({}); Default.args = { size: 24, rotate: 0, theme: 'dark', }; export const Theme = Template.bind({ }); Theme.args = { theme: 'dark', }; export const Rotate = Template.bind({ }); Rotate.args = { rotate: 0, size: 24, };

9b5a28ca13717740

CircleArrow组件启用了CSS Module,样式中使用了全局的CSS变量。默认情况下Storybook无法找到对应的样式,我们需要增加一些额外的配置。

  • .storybook/preview.js中导入全局的样式,将作用在所有的Storybook页面
import '../src/index.css'; // ... rest of the settings
  • .storybook/main.js 中增加自定义 webpack 配置
module.exports = { webpackFinal: async (config) => { // Prevent webpack from using Storybook CSS rules to process CSS modules config.module.rules.find( (rule) => rule.test.toString() === "/\\.css$/" ).exclude = /\.module\.css$/; // Tell webpack what to do with CSS modules config.module.rules.push({ test: /\.module\.css$/, include: path.resolve(__dirname, "../src"), use: [ { loader: "style-loader", options: { modules: { namedExport: true, }, }, }, { loader: "css-loader", options: { importLoaders: 1, modules: { namedExport: true, }, }, }, ], }); // Transpile Gatsby module because Gatsby includes un-transpiled ES6 code. config.module.rules[0].exclude = [/node_modules\/(?!(gatsby)\/)/]; // use babel-plugin-remove-graphql-queries to remove static queries from components when rendering in storybook config.module.rules[0].use[0].options.plugins.push( require.resolve("babel-plugin-remove-graphql-queries") ); return config; }, }

这里增加了两个关于Gatsby的配置,顺手解决了另外一个问题。,参考自:https://stackoverflow.com/questions/68136827/issue-loading-css-module-classes-in-storybook-v6-4/68499636#68499636

从初始化配置到编写组件文档,好像特别简单?

打包输出

运行npm run build-storybook后,默认会将storybook的内容输出到storybook-static 目录中。因为博客是基于Github Page的静态站点,所以为了使得博客内容和Storybook的组件文档同时被托管在同一个项目中,我将Storybook输出的目录放在了Gatsby默认输出的public目录下的storybook目录吗。

# 指定output目录 build-storybook -o ./public/storybook

自动部署

在Github Action的workflow配置中,增加构建 Storybook 的步骤,下面是完整的workflow配置。

name: Deploy GitHub Pages on: schedule: - cron: "0 0 * * *" workflow_dispatch: push: branches: - source - master jobs: build-and-deploy: runs-on: ubuntu-latest steps: - name: BeforeScript run: | git config --global user.name "zhanglun" git config --global user.email "zhanglun1410@gmail.com" - name: Checkout uses: actions/checkout@v2 - name: Set Node Enviroment uses: actions/setup-node@v3 with: node-version: '17' - name: Install Dependenices run: | yarn install yarn run download npx gatsby clean npm cache clean --force - name: Build run: | echo "Starting building..." yarn run build yarn run build-storybook cp ./CNAME ./public/CNAME cp ./ads.txt ./public/ads.txt # 部署到 GitHub Pages - name: Deploy uses: JamesIves/github-pages-deploy-action@releases/v3 with: ACCESS_TOKEN: ${{ secrets.gatsbyDeploy }} BRANCH: gh-pages FOLDER: public

可以访问 https://zhanglun.xyz/storybook/ 体验一下实际效果。

如果遇到了ReferenceError: __BASE_PATH__ is not defined,在.storybook/preview.js中增加变量定义即可

... global.__BASE_PATH__ = "/" ...

结束语

以上就是如何在项目中接入Storybook的全部过程,好像挺简单?难的部分应该是如何写文档吧,hah。