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 相关文章推荐
extjs中grid中嵌入动态combobox的应用
Jan 01 Javascript
基于jquery的高性能td和input切换并可修改内容实现代码
Jan 09 Javascript
js 判断checkbox是否选中的操作方法
Nov 09 Javascript
js中string转int把String类型转化成int类型
Aug 13 Javascript
自定义百度分享的分享按钮
Mar 18 Javascript
浅谈js和css内联外联注意事项
Jun 30 Javascript
利用CSS、JavaScript及Ajax实现图片预加载的方法
Nov 29 Javascript
AngularJS入门教程之Helloworld示例
Dec 25 Javascript
原生js实现手风琴功能(支持横纵向调用)
Jan 13 Javascript
jQury Ajax使用Token验证身份实例代码
Sep 22 Javascript
小程序实现页面顶部选项卡效果
Nov 06 Javascript
Vue指令指令大全
Feb 09 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中对xml读取的相关函数的介绍一
2008/06/05 PHP
php学习笔记 php中面向对象三大特性之一[封装性]的应用
2011/06/13 PHP
php自定义函数实现汉字转换utf8编码的方法
2016/09/29 PHP
php获取ajax的headers方法与内容实例
2017/12/27 PHP
js 获取计算后的样式写法及注意事项
2013/02/25 Javascript
页面加载完成后再执行JS的jquery写法以及区别说明
2014/02/22 Javascript
JS操作HTML自定义属性的方法
2015/02/10 Javascript
js插件YprogressBar实现漂亮的进度条效果
2015/04/20 Javascript
javascript判断并获取注册表中可信任站点的方法
2015/06/01 Javascript
使用coffeescript编写node.js项目的方法汇总
2015/08/05 Javascript
JS模仿编辑器实时改变文本框宽度和高度大小的方法
2015/08/17 Javascript
微信小程序 wxapp内容组件 icon详细介绍
2016/10/31 Javascript
EasyUI 中combotree 默认不能选择父节点的实现方法
2016/11/07 Javascript
Vue.js教程之axios与网络传输的学习实践
2017/04/29 Javascript
基于JavaScript实现带数据验证和复选框的表单提交
2017/08/23 Javascript
微信小程序使用slider设置数据值及switch开关组件功能【附源码下载】
2017/12/09 Javascript
javascript实现QQ空间相册展示源码
2017/12/12 Javascript
30分钟快速实现小程序语音识别功能
2018/11/27 Javascript
微信小程序云开发之使用云函数
2019/05/17 Javascript
了解JavaScript表单操作和表单域
2019/05/27 Javascript
ElementUI radio组件选中小改造
2019/08/12 Javascript
解决Vue使用bus总线时,第一次路由跳转时数据没成功传递问题
2020/07/28 Javascript
React冒泡和阻止冒泡的应用详解
2020/08/18 Javascript
[01:20]辉夜杯背景故事宣传片《辉夜传说》
2015/12/25 DOTA
Python导出DBF文件到Excel的方法
2015/07/25 Python
Python的Django框架中使用SQLAlchemy操作数据库的教程
2016/06/02 Python
10个Python面试常问的问题(小结)
2019/11/20 Python
Python日志:自定义输出字段 json格式输出方式
2020/04/27 Python
Ralph Lauren法国官网:美国高品味时装品牌
2017/12/08 全球购物
衰败城市英国官网:Urban Decay英国
2020/04/29 全球购物
物业管理个人自我评价
2013/11/08 职场文书
小学教师管理制度
2014/01/18 职场文书
教师求职自荐信
2014/03/09 职场文书
学校清明节活动总结
2014/07/04 职场文书
银行奉献演讲稿
2014/09/16 职场文书
夫妻双方自愿离婚协议书
2014/10/24 职场文书