Vite + React从零开始搭建一个开源组件库


Posted in Javascript onJune 25, 2022

前言

日常使用开源的组件库时我们或多或少的都会做一些自定义的配置来符合实际的设计,当这些设计形成一定规模时,设计狮们就会形成一套规范,实施到前端这里就变成了组件库。

本文的目标是从0开始搭建一个面向组件库的基础设施,一起来探索下吧~。

?目标

  • 开发环境
  • 组件库编译,需要生成umd和esm模块的组件代码
  • 支持按需导入与全量导入
  • 组件文档/预览
  • 代码格式化和规范检测工具

?搭建开发环境

现在的时间点Vue或者React都可以用Vite来进行开发打包,这里有老前辈Vant的尝试我们可以放心使用~。

?️生成模板

yarn create vite my-components --template react-ts

这里我们创建生成一套react-ts的应用模板,可以仅保留main.tsx用于组件库的开发调试。

?CSS预处理器

CSS预处理器Sass与Less都可以选择,这里用了Sass:

yarn add sass

不需要配置直接用就可以,与它搭配的规则检查可以安装stylelint:

yarn add stylelint stylelint-config-standard stylelint-config-prettier-scss stylelint-config-standard-scss stylelint-declaration-block-no-ignored-properties

同时根目录下新建.stylelintrc:

{
  "extends": [
    "stylelint-config-standard",
    "stylelint-config-prettier-scss",
    "stylelint-config-standard-scss"
  ],
  "plugins": [
    "stylelint-declaration-block-no-ignored-properties"
  ],
  "rules": {
    "no-descending-specificity": null,
    "no-invalid-position-at-import-rule": null,
    "declaration-empty-line-before": null,
    "keyframes-name-pattern": null,
    "custom-property-pattern": null,
    "number-max-precision": 8,
    "alpha-value-notation": "number",
    "color-function-notation": "legacy",
    "selector-class-pattern": null,
    "selector-id-pattern": null,
    "selector-not-notation": null
  }
}

具体的规则可以查看文档,或者直接用ant-design/vant的规范,总之制定一个用起来舒服的即可。

?eslint

eslint与stylelint基本一个套路,这里不再重复,可以直接用开源组件库的规范。

?组件库编译

组件库的编译和默认的应用编译有一些不同,Vite有预设的打包组件库的选项可以帮我们省去大部分自定义的时间。

⚡️vite的打包配置

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

const path = require("path");

const resolvePath = (str: string) => path.resolve(__dirname, str);

export default defineConfig({
  plugins: [react()],
  build: {
    lib: {
      entry: resolvePath("packages/index.ts"),
      name: "componentsName",
      fileName: format => `componentsName.${format}.js`,
    },
    rollupOptions: {
        external: ["react", "react-dom", "antd"],
        output: {
          globals: {
            react: "react",
            antd: "antd",
            "react-dom": "react-dom",
          },
        },      
    },
  },
})

这里我们的入口不是上面的main.tsx,组件库的打包入口需要是一个包含了所有组件的索引文件,大概可以长这样:

import Button from "./button/index";
export { Button };

默认情况下Vite的配置会打包umd和esm两种模式,只需要写一下名字即可。

同时在打包时我们也不希望外部的库打包进去,像必然存在的reactvue,二次封装的组件库antdvant这些都需要剔除出去。

现在直接build可以看到生成了esumd两份不同版本的文件,里面仅存在我们的代码:

yarn build
$ tsc && vite build
vite v2.9.12 building for production...
✓ 10 modules transformed.
dist/componentsName.es.js   2.27 KiB / gzip: 0.97 KiB
dist/componentsName.umd.js   1.81 KiB / gzip: 0.96 KiB
✨  Done in 3.12s.

⚡️自动生成ts类型文件

打包进行到上面已经初步可用,还不具备Ts的类型定义,用在Ts项目里会报错,这里我们可以用Ts的rollup插件来生成对应的类型:

yarn add @rollup/plugin-typescript tslib

Vite中的rollupOptions扩展一下plugins:

{
  ...,
  rollupOptions: {
    ...,
    plugins: [
      typescript({
        target: "es2015", // 这里指定编译到的版本,
        rootDir: resolvePath("packages/"),
        declaration: true,
        declarationDir: resolvePath("dist"),
        exclude: resolvePath("node_modules/**"),
        allowSyntheticDefaultImports: true,
      }),
    ],
  }
}

重新打包会发现所有packages目录下的文件都生成了一份d.ts的类型定义。

⚡️样式懒加载与全量加载

日常应用的开发时我们会在组件里导入样式,这样打包时构建工具会自动处理。

在构建组件库时为了支持更多的环境考虑,组件内不会导入样式,样式需要单独处理。

可以选择配置多入口或者用插件,Vite没有找到如何配置多入口,所以这里选择了用插件的方式。

开发时由于我们的组件单独组件内不会导入具体的样式,可以在开发的入口处导入全量样式省去手工导入的麻烦:

const req = import.meta.globEager("./*/style/index.scss");

export default req;

插件没有找到可以直接用的插件,这里自己写了一个:

import { compile } from "sass";
import postcss from "postcss";
import postcssImport from "postcss-import";

const autoprefixer = require("autoprefixer");

const path = require("path");

const resolvePath = str => path.resolve(__dirname, str);

const glob = require("glob");

function generateCssPlugin() {
  return {
    name: "generate-css",
    async generateBundle() {
      const files = glob.sync(resolvePath("packages/**/style/*.scss"));

      const allProcess = [];
      const allRawCss = [];

      files.forEach(file => {
        const { css } = compile(file);
        allRawCss.push(css);
        const result = postcss([autoprefixer, postcssImport]).process(css, {
          from: file,
          to: file.replace(resolvePath("packages"), "dist"),
        });

        allProcess.push(result);
      });

      const results = await Promise.all(allProcess);

      results.forEach(result => {
        this.emitFile({
          type: "asset",
          fileName: result.opts.from
            .replace(resolvePath("packages"), "dist")
            .replace("dist/", "")
            .replace("scss", "css"),
          source: result.css,
        });
      });

      // 上半部分编译单独的css,下半部分会把所有css编译为一整个。

      const wholeCss = await postcss([autoprefixer, postcssImport]).process(
        allRawCss.join("\n")
      );

      this.emitFile({
        type: "asset",
        fileName: "styles.css",
        source: wholeCss.css,
      });
    },
  };
}

generateBundle是rollup的插件运行钩子,更多信息可以在这里找到。

再次打包可以看到生成了单个的样式与全量的样式,全量的可以走CDN导入,按需加载的可以用如vite-plugin-imp的构建工具进行按需加载。

Vite + React从零开始搭建一个开源组件库

?文档

文档我们需要同时兼顾到预览,这里我们可以选择storybook:

npx storybook init

之后不需要配置,直接用即可。

内置的mdx文件可以让我们同时写Markdown与jsx:

import { Meta, Story } from "@storybook/addon-docs";

import { Button } from "../packages";

<Meta title="Button" component={Button} />

<Canvas>
  <Story name="Button">
    <Button>这里写Jsx</Button>
  </Story>
</Canvas>

# 用法

**markdown 语法**

❤️npm 发布与发布前的测试

npm的发布流程比较简单,直接

npm login

npm version patch

npm publish

就可以了,对于私有的npm仓库地址我们可以在package.json中定义:

{
  "publishConfig": {
    "registry": "https://npm.private.com"
  }
}

,除此之外package.json中我们最好还要定义一下此组件库的基础入口信息:

{
  "main": "./dist/componentsName.umd.js",
  "module": "./dist/componentsName.es.js",
  "typings": "./dist/index.d.ts",
}

?测试

发布前的测试不同于单元测试(本文没有折腾单元测试),我们需要将打包好的库给实际的项目去使用,模拟安装发布后的包:

在组件库目录运行:

npm link

这样会基于当前目录的名字创建一个符号链接,之后在实际的项目中再次运行:

npm link componentsName

此时node_modules中对应的包会链接到你的组件库中,在组件库的任何修改都可以及时反馈。

当然不仅仅用于测试,开发时也可以用这种方式。

到此这篇关于Vite + React 如何从0到1搭建一个开源组件库的文章就介绍到这了,更多相关React 搭建组件库内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!


Tags in this post...

Javascript 相关文章推荐
Javascript基于AJAX回调函数传递参数实例分析
Dec 15 Javascript
jQuery获取单击节点对象的方法
Jun 02 Javascript
jQuery图片拖动组件Dropzone用法示例
Jan 17 Javascript
Javascript实现登录记住用户名和密码功能
Mar 22 Javascript
使用JS实现气泡跟随鼠标移动的动画效果
Sep 16 Javascript
浅谈vue引入css,less遇到的坑和解决方法
Jan 20 Javascript
浅谈es6中export和export default的作用及区别
Feb 07 Javascript
vue中实现methods一个方法调用另外一个方法
Feb 08 Javascript
vue限制输入框只能输入8位整数和2位小数的代码
Nov 06 Javascript
JS中准确判断变量类型的方法
Jun 01 Javascript
JS如何判断对象是否包含某个属性
Aug 29 Javascript
JS如何实现在弹出窗口中加载页面
Dec 03 Javascript
React自定义hook的方法
Jun 25 #Javascript
小程序实现侧滑删除功能
Jun 25 #Javascript
小程序自定义轮播图圆点组件
Jun 25 #Javascript
微信小程序实现轮播图指示器
Jun 25 #Javascript
create-react-app开发常用配置教程
Jun 25 #Javascript
JavaScript架构搭建前端监控如何采集异常数据
Jun 25 #Javascript
webpack介绍使用配置教程详解webpack介绍和使用
Jun 25 #Javascript
You might like
基于PHP常用函数的用法详解
2013/05/10 PHP
PHP中source #N问题的解决方法
2014/01/27 PHP
php使用GD实现颜色渐变实例
2015/06/02 PHP
PHP获取数组的键与值方法小结
2015/06/13 PHP
微信开发之网页授权获取用户信息(二)
2016/01/08 PHP
Yii 框架使用数据库(databases)的方法示例
2020/05/19 PHP
js监听滚动条滚动事件使得某个标签内容始终位于同一位置
2014/01/24 Javascript
node.js中的events.emitter.once方法使用说明
2014/12/10 Javascript
jQuery+CSS3折叠卡片式下拉列表框实现效果
2015/11/02 Javascript
谈谈我对JavaScript原型和闭包系列理解(随手笔记6)
2015/12/20 Javascript
基于JavaScript实现简单的随机抽奖小程序
2016/01/05 Javascript
12306 刷票脚本及稳固刷票脚本(防挂)
2017/01/04 Javascript
tablesorter.js表格排序使用方法(支持中文排序)
2017/02/10 Javascript
微信小程序 request接口的封装实例代码
2017/04/26 Javascript
VUE利用vuex模拟实现新闻点赞功能实例
2017/06/28 Javascript
vue实现留言板todolist功能
2017/08/16 Javascript
JavaScript如何对图片进行黑白化
2018/04/10 Javascript
微信小程序mpvue点击按钮获取button值的方法
2019/05/29 Javascript
vue2.x 通过后端接口代理,获取qq音乐api的数据示例
2019/10/30 Javascript
vue实现移动端input上传视频、音频
2020/08/18 Javascript
angular共享依赖的解决方案分享
2020/10/15 Javascript
js实现鼠标拖曳效果
2020/12/30 Javascript
Python 3.x读写csv文件中数字的方法示例
2017/08/29 Python
python 字典中文key处理,读取,比较方法
2018/07/06 Python
关于pytorch处理类别不平衡的问题
2019/12/31 Python
python利用递归方法实现求集合的幂集
2020/09/07 Python
美国翻新电子产品商店:The Store
2019/10/08 全球购物
应届护士推荐信
2013/11/16 职场文书
暑假家长评语大全
2014/04/17 职场文书
高中升旗仪式演讲稿
2014/09/09 职场文书
购房协议书范本
2014/10/02 职场文书
2016年优秀团支部事迹材料
2016/02/26 职场文书
《好妈妈胜过好老师》:每个孩子的优秀都是有源头的
2020/01/03 职场文书
PyTorch的Debug指南
2021/05/07 Python
Kubernetes中Deployment的升级与回滚
2022/04/01 Servers
详解Spring Security如何在权限中使用通配符
2022/06/28 Java/Android