使用 Vite 开发前端库实践

·  阅读 3936
使用 Vite 开发前端库实践

前言

最近需要做个内部的反馈系统(最终效果如下图所示),需要能在其他 React 前端应用中接入,所以需要开发一个库。经过几番考虑,最终选择了用 Vite 来做开发和打包。原因是 Vite 可以很方便的支持开发时的预览,同时也支持构建 库模式,相比于使用 Rollup 从零开始搭建可以省去很多工作。

截屏2022-01-08 下午3.20.10

在开发过程中总结了一套开发模版,👉 github地址。 其中包含的功能如下:

  1. 支持 Typescript,支持打包时输出 TS 类型文件
  2. 支持使用 Less 语法
  3. 支持导入 SVG 组件语法,即 import { ReactComponent as IconSvg } from './icon.svg'
  4. 支持函数及组件的测试
  5. Eslint、Prettier 配置,提交 commit 时自动进行语法检查

功能很简单,但已经可以满足一般的需求了~ 总的来说,模版功能很少很基础,但也很简洁。

下面详细讲讲使用 Vite 来开发库的一些实践经历。

实践🌟

(1)初始化项目

使用脚手架直接创建即可 yarn create vite,最后选择 react-ts ,即默认配置好 react 和 typescript 的环境。

(2)库模式

为了运行 yarn build 时构建库模式,我们需要做一些配置

build.lib

这里主要是配置打包入口和指定打包模式,如 es,cmd,umd。如果只想打包为 es ,则可以使用如下配置。

{
  entry: './src/index.tsx', // 打包入口
  formats: ['es'], // 指定打包模式为 es
  fileName: (format) => `index.${format}.js` // es 模式打包文件名为 index.es.js 
}
复制代码

build.rollupOptions

覆盖 rollup 的一些默认配置。我们是开发基于 React 的库,所以需要将 react 设置为外部依赖,这样在构建时 rollup 就不会把 react 的源码打包进去。

{
    external: ['react'], // 外部依赖
}
复制代码

相应的,我们需要在 package.json 文件中配置 peerDependencies 字段,来告知外部项目我们需要的依赖。

{
  "peerDependencies": {
    "react": ">=17.0.0"
  }
}
复制代码

build.outdir

构建结果的输出目录,默认为 dist ,我们可以改名为 lib。

{
  outDir: 'lib' // 默认为 dist
}
复制代码

(3)输出 TS 类型文件

为了在外部引用这个库时能看到相关函数和变量的类型,我们需要在构建时输出 TS 类型。但 Vite 并不支持输出 TS 类型文件,我们可以通过 tsc 命令来完成这一工作 。

我们可以创建一个 tsconfig.build.json 的文件,如下所示:

{
  "extends": "./tsconfig.json", // 拓展 tsconfig.json 的配置
  "compilerOptions": {
    "noEmit": false, // 允许生成文件
    "declaration": true, // 需要设置为 true 来支持类型
    "emitDeclarationOnly": true, // 只生成类型文件
    "declarationDir": "lib" // 类型文件的导出目录
  },
  "include": ["src"] // 编译目标仅为 src 文件夹下的文件
}
复制代码

接着将默认的 package.json 中默认的 build 命令 tsc && vite build 改为 vite build && tsc -p tsconfig.build.json 即可。

"scripts": {
  "dev": "vite",
  "build": "vite build && tsc -p tsconfig.build.json"
}
复制代码

两者的区别是,后者执行 tsc 命令时使用的是 tsconfig.build.json 中的配置,以生成类型文件。需要注意,tsc 命令还有另一个重要功能,就是做 ts 代码的类型检查,而 Vite 仅执行 .ts 文件的转译工作,这也是为什么默认的 build 命令会包含 tsc 的原因。

最后,还需要在 package.json 中声明 es 模块和类型文件的位置,这样外部在引入这个库时才能找到相应的文件。

{
  "module": "./lib/index.es.js", // es 模块入口
  "types": "./lib/index.d.ts", // 类型文件位置
}
复制代码

(4)CSS

CSS 的处理可以参考官方说明 CSS。官方建议使用 PostCSS 插件来帮助编写符合未来规范的 CSS,不过如果想用 less,sass 这些预处理器也是很方便的,只需要安装对应的预处理器依赖。如使用 less 只需要 yarn add -D less 即可。

(5)eslint 配置

为了让我们编写的 JS 代码更规范,配置 eslint 是很有必要的。我们可以使用 npx eslint --init 命令来可视化选择我们需要的配置。

截屏2021-11-09 上午9.55.18

这样就不用我们一个个自己去配了。最后,我们再添加一个 react-hook 的语法检查插件就好了,👉 eslint-plugin-react-hooks

(6)StyleInject

默认的构建结果中 JS 和 CSS 文件是分开的,如下所示:

截屏2021-11-09 下午8.18.06

这样的做法就导致了在引入这个库时还需要额外引入它的CSS,如:

import Button from 'x-button';
import 'x-button/lib/style.css';
复制代码

另一种做法是直接将源码中的 CSS 样式用 style 标签导入,从而外部引入时就不需要额外引入 CSS 文件了。实现方式如下:

// rollup-plugin-postcss 插件中导入 CSS 的函数
function styleInject(css, ref) {
  if ( ref === void 0 ) ref = {};
  var insertAt = ref.insertAt;

  if (!css || typeof document === 'undefined') { return; }

  var head = document.head || document.getElementsByTagName('head')[0];
  var style = document.createElement('style');
  style.type = 'text/css';

  if (insertAt === 'top') {
    if (head.firstChild) {
      head.insertBefore(style, head.firstChild);
    } else {
      head.appendChild(style);
    }
  } else {
    head.appendChild(style);
  }

  if (style.styleSheet) {
    style.styleSheet.cssText = css;
  } else {
    style.appendChild(document.createTextNode(css));
  }
}

var css_248z = '.a{\n background:blue;}'
styleInject(css_248z);
复制代码

关于库模式下CSS 可否作为样式被注入有这样一个 issue,Can css be styleInjected in library mode?。当前 Vite 并没有对此进行支持,不过我们可以通过一些其他方法进行实现。

(7)导入 svg 为 React Component

即支持这样的导入方式:

import { ReactComponent as SendSvg } from './send.svg';
复制代码

这也有一个相关的 issue: import SVG directly as a React Component,Vite 开发者表明这是 React 中的特性,只能用插件来实现。

当前社区中有一个很多插件实现了这一功能,比如 vite-plugin-svgr,我们只需按文档引入即可。

(8)测试

我们使用 jestreact-testing-library 来进行 JS 和 React 组件的测试。按照 官方文档 安装相关依赖并配置相应文件即可。

setup.js

对于一些所有测试文件都需要的公共项,我们可以将其添加到 test/setup.js 中,如引用通用库,设置全局变量等。比如一个非常有用的库 jest-dom ,它可以用来判断一些 DOM 的状态,如 tobeVisible,toBeDisabled。我们就可以在 setup.js 中引入它:

import '@testing-library/jest-dom';
复制代码

并在 jest.config.js 中设置:

{
  setupFilesAfterEnv: ['<rootDir>/test/setup.js']
}
复制代码

mock 静态文件

在开发中我们可能会引入一些 JS 外的文件,如 css,svg 等,这些在测试中其实没有任何作用,所以我们可以安全的 mock 掉它们。

// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
  moduleNameMapper: {
    '\.svg': '<rootDir>/test/__mocks__/svgrMock.js',
    '\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
      '<rootDir>/test/__mocks__/fileMock.js',
    '\.(css|less)$': '<rootDir>/test/__mocks__/styleMock.js'
  }
};

// test/__mocks__/svgrMock.js
export default 'SvgrURL';
export const ReactComponent = 'div';
复制代码

(9)检查提交代码

一个好的开发实践是,在提交一个分支的代码前对修改的代码进行检查,以防止错误的代码被提交。

我们可以使用 huskylint-staged 来帮助我们完成这个工作。通过 husky 可以方便的添加 git 事件的钩子,比如在执行 git commit 时执行某些命令(测试/代码检查)。lint-staged 可以定位即将被 ”commit“ 的文件,从而只在修改了的文件上执行代码检查,这样可以缩短检查时间。

配置 husky

# 安装 husky 依赖
yarn add husky -D 

# 执行 hsuky install,这样 git hooks 才会生效。此命令会生成 .husky 文件夹
yarn husky install
复制代码

package.json 中添加 prepare 命令,这样每次运行 npm installyarn add 安装依赖时都会自动执行 husky install

"scripts": {
  "prepare": "husky install"
}
复制代码

最后,添加一个 commit hook,它将创建一个 .husky/pre-commit 文件。

npx husky add .husky/pre-commit "npm test"
复制代码

到这一步,我们在提交 commit 时就会自动执行 .husky/pre-commit 中的脚本了。

配置 lint-staged

package.json 中添加 “lint-staged”,如下:

{
  "scripts": {
    // ...
    "prepare": "husky install" // prepare 命令
    "lint": "tsc && eslint . --ext '.js,.jsx,.ts,.tsx'",
    "test": "jest",
  },
  "lint-staged": {
    "*.{js,ts,tsx,jsx}": "npm run lint" // 对 committed 的 js,ts,tsx,jsx 文件执行 lint 命令
  }
}
复制代码

接着将 .husky/pre-commit 中的 "npm test" 命令修改为下面的内容:

npm run test && npx lint-staged
复制代码

现在,在提交 commit 时,就会自动运行测试和语法检查了。

(10)开发预览&打包

参考一些开源库的做法,我们在 src 目录下编写源码,在 example 目录下编写使用示例。 在 Vite 中,我们需要设置 src/index.tsx 作为构建的入口,example/index.tsx 作为开发的入口。

// vite.config.js
{
  build: {
    lib: {
      entry: './src/index.tsx', // 构建入口
    }
  }
}

// index.html 
<script type="module" src="/example/index.tsx"></script> // 开发入口
复制代码

这样就可以运行 yarn dev 进行开发预览,运行 yarn build 来进行打包构建了。

(11)调试

在库的功能完成并打包构建后,我们可以通过 yarn link (或 npm link)来做最后的发布调试。

在当前库的根目录下执行 yarn link ,之后我们在其他项目中使用 yarn link package-name 后就可以正常导入这个库了。

截屏2021-11-14 下午10.13.35

"package-name" 和上图中的 x-button 即库的 package.json 中的 name 字段的值。

如果一个库存在外部依赖,如 React,那么我们还需要多做几步操作。比如需要调试的库是A,它有一个外部依赖 React,引入它的项目是 B,那么依次的操作为:

  1. 在库 A 中执行 yarn link
  2. 在库 B 中执行 yarn link A 安装库 A
  3. 在库 B 中执行 cd node_modules/react && yarn link ,来创建 B 中安装的 react 的链接
  4. 最后一步,在库 A 中执行 yarn link react 来链接库 B 中安装的 react

上述 yarn link 也可用于开源库的调试,在 clone 项目后,使用 yarn link,我们可以在其他项目中引入并调试开源库的代码了😺。

分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改