likes
comments
collection
share

Vite + storybook 搭建自己的 React 组件库

作者站长头像
站长
· 阅读数 11

Vite 以原生 ESM 方式提供源码,使用 esbuild 预构建依赖,将启动速度提高了10-100倍,用 Rollup 打包代码(不排除未来使用 esbuild 打包代码)

为什么选择 Vite ?

尤雨溪主题演讲《2022 前端生态趋势》在工具链一节中的工具链的抽象层次提到过关于 Vite 的定位。

  • browserify/webpack/rollup,专注于打包,抽象层次低。

也就是想要用这些工具做一个应用时候,需要大量使用第三方插件,以及大量的配置。

  • Parcel/Vue-CLI/CRA 专注于应用,抽象层次高。

相对而言的缺点是比较复杂庞大的黑盒。 当你需要自定义的定制时,会不可避免的遇到和默认功能出现一些冲突的时候。

  • Vite 的 CLI 专注于应用,抽象层次高(有很多开箱即用的配置)。API 专注于支持上层框架,抽象层次中。

有很多新出来的框架都基于 Vite 作为底层的工具链。

Vite + storybook 搭建自己的 React 组件库

最后选择 Vite 而非 webpack,除了 Vite 的速度之外,易集成和开箱即用的配置,也是选择的因素。

Storybook-builder-vite 使得 storybook + webpack 的默认变成了 storybook + webpack/vite。

Vite 库模式的配置

先使用 Vite 搭建项目,并选择你需要的模板。

npm init vite

把 src 内的文件清空,index.html 也可以删了。

构建 es 和 umd 格式代码

vite.config.ts 的配置:

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { resolve } from "path";

export default defineConfig({
  plugins: [react()],
  build: {
    lib: {
      // 入口文件将包含可以由你的包的用户导入的导出:
      entry: resolve(__dirname, "src/components/index.tsx"),
      name: "ReactComponents",
      fileName: (format) => `react-components.${format}.js`,
    },
    rollupOptions: {
      // 确保外部化处理那些你不想打包进库的依赖
      external: ["react", "react-dom"],
      output: {
        // 在 UMD 构建模式下为这些外部化的依赖提供一个全局变量
        globals: {
          react: "React",
          "react-dom": "react-dom",
        },
      },
    },
  },
});

目前构建出来的代码是只有 js,而没有 ts 类型声明的。

构建增加 ts 类型声明

Vite 虽然天然支持引入 .ts 文件,但 Vite 是使用 esbuild 将 Typescript 转译为 Javascript,而 esbuild 不做任何类型检查或声明输出。

所以要生成类型声明,我们可以使用tsc。 tsconfig.json 配置如下:

  "compilerOptions": {
    // 指定输出目录
    "outDir": "dist",
    // 不生成输出文件
    "noEmit": false,
    // 指定编译的文件
    "include": ["src/**/*"],
  }

还需要在 tsconfig.node.json (专门用于 vite.config.ts 的 TypeScript 配置文件)配置如下:

{
  "exclude": ["src/stories/*"]
}

这样打包生成 ts 声明的时候,可以排除文档这一块。

配置 package.json

  "scripts": {
    "build": "vite build && tsc --declaration --emitDeclarationOnly",
  }

tsc 的命令一定要在 vite build 之后执行,不然生成的声明会被清空。 (打包出来的结果不会把你的 .d.ts 给打包进去,改成 .ts 就可以了)

指定 npm 包加载的入口文件

配置 package.json

  "main": "./dist/react-components.umd.js",
  "module": "./dist/react-components.es.js",
  "typings": "./dist/components/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/react-components.es.js",
      "require": "./dist/react-components.umd.js"
    }
  }

更多关于 package.json 文件相关的配置,可以看看 这篇文章 (这篇文章没有提到 exports ,这个字段主要是用来限制导出的,作为补充可以看看 这篇

关于发布 NPM 包

发布的时候你希望只发布打包的文件,可在 package.json 中通过 files 字段去控制。

  "files": [ "dist" ]

最后 npm publish 发布就好了。

组件内置引入 css

当你直接引用组件时,会发现样式丢失了。

查看 vite 打包出来的 index.es.js 文件,可以发现并没有 import css,所以在直接使用组件的时候,会导致需要单独引入组件样式,才能生效。

可以使用 vite-plugin-libcss 插件,在打包出来的 index.es.js 的第一行自动加上 import style.css,解决需要单独引用组件样式的问题。

到目前为止,关于库的打包发布就结束了,下一步,文档。

storybook 文档

初始化文档

在 vite 的项目内执行以下代码,初始化 Storybook。

npx sb init --builder @storybook/builder-vite
//or
npx storybook init --builder vite

Storybook 的一个主要优势是用插件扩展 Storybook 的用户界面和行为(ControlsDocsAccessibility),大多数功能都是以插件实现的。 (这里可以找到 storybook 核心团队开发的 “官方” 插件

默认情况下,Storybook 会默认安装“基本”插件(@storybook/addon-essentials),增加用户初始体验。

QA

如何查看完整源代码

我们点击 show code 显示的是经过精简的源代码片段。

Vite + storybook 搭建自己的 React 组件库

简单组件是没问题的,但如果组件复杂一点的话,其实是并不友好。因为无法直接复制这个示例组件的完整代码直接进行复用。

源文档块 似乎无法通过配置取消自动精简源代码,需要安装 @storybook/addon-storysource 插件,这可以帮助我们看到 stories 内的源代码。

这时候 show code 显示代码,就显得有点重复多余了,可通过配置 parameters,把 show code 给关掉。

export default {
  parameters: {
    docs: {
      page: null,
    },
  },
};

配置路径别名

在配置 Vite 的 resolve.alias 路径别名时,需要注意始终使用绝对路径,相对路径的别名值会被原封不动地使用,导致无法正常解析。

但默认情况下,@storybook/builder-vite 是不会读取 vite.config.ts 文件。

所以需要在 .storybook/main.js|ts 内配置:

const { mergeConfig } = require('vite');
const path = require('path');

module.exports = {
  async viteFinal(config) {
    return mergeConfig(config, {
      resolve: {
        alias: { ReactComponents: path.resolve(__dirname, '../src/components/index.tsx') },
      },
    });
  },
};

ts 也要记得配置下,在 tsconfig.json 内配置

{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "ReactComponents": ["src/components/index.tsx"]
    }
  },
}

预览文档

如何预览自己打包出来的组件库文档呢?

因为打包出来的文件夹名字是 storybook-static,所以让我们在 package.json 写入以下代码:

  "scripts": {
    "preview-storybook": "npx http-server storybook-static"
  },

执行一下,然后就可以看到本地服务启动的可以访问的链接啦。

Vite + storybook 搭建自己的 React 组件库

实践建议

  • 当示例涉及到多个组件时,或参数数量较多时。可以通过 argTypes 对参数进行分组,从而使结构更清晰。
export default {
  argTypes: {
    param: {
      table: { category: 'Group' },
    },
  },
};
  • 编写 stories 时,开发者应该像用户一样使用组件(这是在 dumi 中学到的)。
// 正确示例
import { Button } from 'ReactComponents';

// 错误示例,用户不知道 Button 组件是哪里来的
import Button from './index.tsx';
import Button from '@/Button/index.tsx';

同时,记得把 stories 内的 Default export 写到最下面

// Button.stories.js|jsx
import React from 'react';

export const Primary = () => <Button primary>Button</Button>;

export default {
  title: 'Button',
  component: Button,
};

这样子在查看组件源码的时候,会更加的友好。

按照这一原则,demo 还能直接被用户直接拷贝到项目中使用。

参考 :

storybook.js.org/blog/storyb…

cn.vitejs.dev/guide/build…

petermekhaeil.com/til/type-de…