随笔

实现前端组件库

组件库这东西之前倒是实现过,也发布到 npm 上去过,但进来技术变化很快,索性就梳理一遍目前组件库的实现。

一般实现

首先,用 Vite 创建一个 Library 项目,其实和普通的 TypeScript 项目没啥差别,主要在于配置 build 上。

build: {
  lib: {
    entry: './src/index.ts',
    name: 'index',
    fileName: 'index',
    formats: ['es']
  }
}

告诉 Vite 需要打包的格式,文件名就可以了,另外需要装一个插件 vite-plugin-dts,用来生成 .d.ts 类型声明文件的一个插件,如果不使用这个,打包出来后引用方将看不到类型。

还有 package.json 中要告诉使用方如何使用自己:

"type": "module",
"files": [
  "dist"
],
"module": "./dist/index.js",
"types": "./dist/index.d.ts",

需要注意的是如果要兼容 CommonJS 需要修改 format 中打包出来的格式,以及 package 中需要增加 main 字段指向打包出来的 cjs 文件。

依赖组件 peerDependencies

用来指定自身所依赖的库,避免依赖方重复安装依赖

package.json 中声明所依赖的包

"peerDependencies": {
  "dayjs": "^1.11.12"
}

然后到 Vite 修改打包配置,如果此处不修改,会将依赖的包一起打包进去

build: {
  rollupOptions: {
    external: ['dayjs'],
  }
}

React 组件库

同上,先在 peerDependencies 中加上 react, react-dom, 然后修改 Vite 配置文件,这里的 react/jsx-runtime 是一个特殊情况,React 最新版拆分了 jsx 的 runtime,所以此处需要单独列出来(17 之前使用 React.createElement,之后用 jsx,并且编译器支持自动注入,不再需要单独 import 某个东西)

build: {
  rollupOptions: {
    external: ['react', 'react-dom', 'react/jsx-runtime']
  },
}

如果用的 typescript 需要修改 tsconfig.json

compilerOptions: {
  "jsx": "react-jsx"
}

另外,这里我并没有安装 Vite 支持 react 的插件 @vitejs/plugin-react,有需要可以自行安装

{
  plugins: [react()]
}

Monorepo

现在基本各个组件库,业务项目也都在用,此处我们只说组件库,因为业务上多项目好用也会带来一定的问题,这里也不讲概念,只讲用法。

现在用的是 pnpm workspace,之前是 lerna 已经停止维护了

// pnpm-workspace.yaml
packages:
  - 'packages/*'

monorepo 可以共享很多东西,比如在根目录下安装包,那么子项目下都可以直接使用不再需要单独安装,比如 eslint 和 prettier。

比如现在有 componentshooks 两个子项目,我可以把双方都依赖的 @types/react 安装到根目录

命令使用上也有一点区别,都是从根目录上使用,不用进入子项目单独处理

# install types as workspace
pnpm i @types/react -w -D

# install 
pnpm --filter @example/component i dayjs

# install from workspace
pnpm --filter @example/component i @example/hook

# build all
pnpm run build 

# build item
pnpm --filter=@example/hook run build 

# publish item
pnpm publish ./packages/components --access=public

需要注意的是子项目互相依赖处理,需要修改 .npmrc 文件,不然 pnpm 默认会先去网络上查找包

link-workspace-packages=true

安装使用的是 workspace 协议引用,在打包发布时才会替换成真实版本

"dependencies": {
  "@example/hook": "workspace:^"
}

另外如果需要记录变更日志,有个叫 changeset 的工具不错,可以根据选择自动更新包版本,以及修改更新的日志。

先执行 changeset 会让选择变更的类型,比如大版本小版本,以及输入变更日志,然后会生成一个变更文件,再执行 changeset version 会消费前面生成的文件,自动 patch 到项目的 README 文件以及 package.json 文件更新日志和版本信息。

本地调试/测试

第一个方式是 npm link 创建软链的方式,但是目前 monorepo 使用下来有点不方便
第二种方式是 yalc 工具,可以本地模拟真实的发包下载包过程
第三种方式是利用 alias 直接重定向到组件源码位置

本文链接:https://note.lilonghe.net/post/implement-frontend-library.html

-- EOF --