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 相关文章推荐
js innerHTML 的一些问题的解决方法
Jun 22 Javascript
javascript 实现简单的table排序及table操作练习
Dec 28 Javascript
JS获取select的value和text值的简单实例
Feb 26 Javascript
动态加载jQuery的方法
Jun 16 Javascript
js设置文字颜色的方法示例
Dec 30 Javascript
深入理解Javascript中的观察者模式
Feb 20 Javascript
关于jQuery里prev()的简单操作代码
Oct 27 jQuery
Vue.js 实现微信公众号菜单编辑器功能(二)
May 08 Javascript
在Vue中使用axios请求拦截的实现方法
Oct 25 Javascript
jQuery实现的简单歌词滚动功能示例
Jan 07 jQuery
如何使用proxy实现一个简单完整的MVVM库的示例代码
Sep 17 Javascript
小程序websocket心跳库(websocket-heartbeat-miniprogram)
Feb 23 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删除特定数组内容并且重建数组索引的方法.
2011/03/25 PHP
javascript编程起步(第四课)
2007/02/27 Javascript
Js判断参数(String,Array,Object)是否为undefined或者值为空
2013/11/04 Javascript
node.js入门教程迷你书、node.js入门web应用开发完全示例
2014/04/06 Javascript
HTML页面弹出居中可拖拽的自定义窗口层
2014/05/07 Javascript
纯js实现悬浮按钮组件
2016/12/17 Javascript
jQuery插件HighCharts绘制2D柱状图、折线图的组合双轴图效果示例【附demo源码下载】
2017/03/09 Javascript
微信小程序使用image组件显示图片的方法【附源码下载】
2017/12/08 Javascript
JavaScript实现页面中录音功能的方法
2019/06/04 Javascript
解决Antd 里面的select 选择框联动触发的问题
2020/10/24 Javascript
详解Vue 的异常处理机制
2020/11/30 Vue.js
[02:11]完美世界DOTA2联赛10月28日赛事精彩集锦:来吧展示实力强劲
2020/10/29 DOTA
Python设计模式之单例模式实例
2014/04/26 Python
python实现列表中由数值查到索引的方法
2018/06/27 Python
Python列表切片操作实例总结
2019/02/19 Python
python批量修改文件夹及其子文件夹下的文件内容
2019/03/15 Python
python TF-IDF算法实现文本关键词提取
2019/05/29 Python
python ftplib模块使用代码实例
2019/12/31 Python
PyCharm汉化安装及永久激活详细教程(靠谱)
2020/01/16 Python
python3 字符串知识点学习笔记
2020/02/08 Python
Python基于traceback模块获取异常信息
2020/07/23 Python
Volcom法国官网:美国冲浪滑板品牌
2017/05/25 全球购物
说出ArrayList,Vector, LinkedList的存储性能和特性
2015/01/04 面试题
违反工作纪律检讨书
2014/02/15 职场文书
优秀毕业生推荐信范文
2014/03/07 职场文书
校园环保建议书
2014/05/14 职场文书
效能监察建议书
2014/05/19 职场文书
森林防火宣传标语
2014/06/27 职场文书
纪律教育学习心得体会
2014/09/02 职场文书
2014年文艺部工作总结
2014/11/17 职场文书
2014年妇幼保健工作总结
2014/12/08 职场文书
小学教师节活动总结
2015/03/20 职场文书
4S店收银员岗位职责
2015/04/07 职场文书
2017元旦晚会开幕词
2016/03/03 职场文书
详解Python requests模块
2021/06/21 Python
python井字棋游戏实现人机对战
2022/04/28 Python