在vite项目中使用storybook

在vite项目中使用storybook

从 Storybook 6.3 开始,由于 Eirik Sletteberg、Sasan Farrokh 和 Ian VanSchooten 的英勇工作,您可以使用 Vite 来构建您的 Storybook。

当您将新构建器添加到您的项目时,Storybook 停止与 webpack 捆绑并开始与 Vite 捆绑。这意味着您可以获得以下好处:

  1. 显着提高了构建速度
  2. 与您的 Vite 项目设置的兼容性
  3. 访问 Vite 的插件生态系统

使用步骤说明

  • 首先您需要创建一个vite项目(这里是vite react-ts)
npm init @vitejs/app vite-react-demo --template react-ts

cd vite-react-demo

npm i
  • 初始化storybook
npx sb init --builder storybook-builder-vite
  • 此时会自动多出两个文件夹
文件夹名称作用
.storybook配置文件
src/stories组件story案例
  • 运行storybook
npm run storybook
  • 运行成功截图
可见vite缓存相关信息
见此图,表示配置成功
  • story 案例代码说明(比如button)
// Button.ts
// import react from 'react'; // 如果使用17以及以上版本,不需要导入
import './button.css';

interface ButtonProps {
  /**
   * Is this the principal call to action on the page?
   */
  primary?: boolean;
  /**
   * What background color to use
   */
  backgroundColor?: string;
  /**
   * How large should the button be?
   */
  size?: 'small' | 'medium' | 'large';
  /**
   * Button contents
   */
  label: string;
  /**
   * Optional click handler
   */
  onClick?: () => void;
}

/**
 * Primary UI component for user interaction
 */
export const Button = ({
  primary = false,
  size = 'medium',
  backgroundColor,
  label,
  ...props
}: ButtonProps) => {
  const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
  return (
    <button
      type="button"
      className={['storybook-button', `storybook-button--${size}`, mode].join(' ')}
      style={{ backgroundColor }}
      {...props}
    >
      {label}
    </button>
  );
};


// Button.stories.ts
// import react from 'react'; // 如果使用17以及以上版本,不需要导入
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { Button } from './Button';

export default {
  title: 'Example/Button',
  component: Button,
  argTypes: {
    backgroundColor: { control: 'color' },
  },
} as ComponentMeta<typeof Button>;

const Template: ComponentStory<typeof Button> = (args) => <Button {...args} />;

export const Primary = Template.bind({});
Primary.args = {
  primary: true,
  label: 'Button',
};

// ...


对于Button.ts中ButtonProps interface申明,对应story的Doc是能自动推断出Description、Default、Control的,如下图所示 (TIPS: 下图是webpack项目截图):

webpack项目截图

而实际上vite项目,并没有自动推断出相关信息,说实话有点尴尬,目前还没找到解决方案(或许只能等官方发现,或者您知道可以下方留言)!!!



扩展配置!!!!!!!!

后期我在react-ts vite项目的基础上添加了以下配置

  1. postcss -> px to rem(我的项目是h5,所以要自适应手机端)
  2. vite.config.ts
使用到了vant,所以要作一些额外配置


// antd.theme.js

const theme = { // https://hub.fastgit.org/ant-design/ant-design-mobile/blob/master/components/style/themes/default.less
  '@hd': '2px',
  '@brand-primary': '#ff8a00',
  '@brand-primary-tap': '#ee8a00',
  '@brand-success': '#6abf47',
  '@brand-warning': '#ffc600',
  '@brand-error': '#f4333c',
  '@brand-important': '#ff5b05',  // 用于小红点
  '@brand-wait': '#108ee9',
  'color-text-base': '#333',
};

module.exports = theme;


// vite.config.ts

import path from 'path';
import reactRefresh from '@vitejs/plugin-react-refresh';
import styleImport from 'vite-plugin-style-import';
import { ConfigEnv, UserConfigExport } from 'vite';

const theme = require('./antd.theme');

// https://vitejs.dev/config/
export default ({ command, mode }: ConfigEnv): UserConfigExport => {
  const isBuild = command !== 'serve';

  return {
    clearScreen: false,
    css: { // 使用到了vant自定义主题
      preprocessorOptions: {
        less: {
          javascriptEnabled: true,
          modifyVars: theme,
        },
      },
    },
    server: {
      port: 3003, // 端口自定义
    },
    resolve: {
      alias: { // 文件目录缩写配置
        '@': path.resolve('src'),
        '@assets': path.resolve('src/assets'),
      },
    },
    esbuild: { // 使用到了react17
      jsxInject: `import React from 'react'`,
    },
    plugins: [
      reactRefresh(),
      styleImport({ // vant样式自动导入配置
        libs: [
          {
            libraryName: 'antd-mobile',
            esModule: true,
            resolveComponent: (name) => {
              return `antd-mobile/lib/${name}`;
            },
            resolveStyle: (name) => {
              return `antd-mobile/es/${name}/style/index`;
            },
          },
        ],
      }),
    ],
  };
}

此时再次启动storybook,会报错,因为.storybook/main.js并没有这些配置,加上即可

const path = require('path');
const theme = require('../antd.theme');
// TIPS: 不支持import
const styleImport = require('vite-plugin-style-import').default;

module.exports = {
  stories: [
    '../src/**/*.stories.mdx',
    '../src/**/*.stories.@(js|jsx|ts|tsx)',
  ],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
  ],
  core: {
    builder: 'storybook-builder-vite',
  },
  async viteFinal(config, { configType: env }) {
      // customize the Vite config here
      const customConfig = {
        css: {
          preprocessorOptions: {
            less: {
              javascriptEnabled: true,
              modifyVars: theme,
            },
          },
        },
        resolve: {
          alias: {
            '@': path.resolve('src'),
            '@assets': path.resolve('src/assets'),
          },
        },
        esbuild: {
          jsxInject: `import React from 'react'`,
        },
      };

      config.plugins.push(
        styleImport({
          libs: [
            {
              libraryName: 'antd-mobile',
              esModule: true,
              resolveComponent: (name) => {
                return `antd-mobile/lib/${name}`;
              },
              resolveStyle: (name) => {
                return `antd-mobile/es/${name}/style/index`;
              },
            },
          ],
        }),
      );
      // return the customized config
      return { ...config, ...customConfig };
  },
};


重新启动即可


接着看,还没完,由于我的项目自适应了手机端(postcss -> px to rem),所以按钮看上去非常小,原因是我们还需要在storybook中添加自适应基准字体大小设定

缺少基准字体大小设定
  • 新建.storybook/preview-body.html
<script>
  (function (doc, win) {
    var docEl = doc.documentElement,
      resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
      recalc = function () {
        var clientWidth = docEl.clientWidth;
        if (!clientWidth) return;
        if (clientWidth >= 750) {
          docEl.style.fontSize = '100px';
        } else {
          docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';
        }
      };

    if (!doc.addEventListener) return;
    win.addEventListener(resizeEvt, recalc, false);
    doc.addEventListener('DOMContentLoaded', recalc, false);
  })(document, window);
</script>
  • 重启storybook,发现按钮大小正常显示

添加手机机型

  • 编辑.storybook/preview.js
import { MINIMAL_VIEWPORTS } from '@storybook/addon-viewport';

const customViewports = {
  pixel2: {
    name: 'Pixel 2',
    styles: {
      width: '411px',
      height: '731px',
    },
  },
  iphone5: {
    name: 'iPhone 5/SE',
    styles: {
      width: '320px',
      height: '568px',
    },
  },
  iphone678: {
    name: 'iPhone 6/7/8',
    styles: {
      width: '375px',
      height: '667px',
    },
  },
};

// https://storybook.js.org/docs/react/essentials/viewport
export const parameters = {
  padded: false,
  actions: { argTypesRegex: "^on[A-Z].*" },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
  viewport: {
    viewports: {
      ...MINIMAL_VIEWPORTS,
      ...customViewports,
    },
  },
};
  • 重启storybook,并点击红框中的按钮
  • 选择任意一个自定义机型,比如iPhone5/SE

设置成功!

编辑于 2021-07-06 17:45