组件库打包与按需加载

组件库打包与按需加载

这篇笔记还在编辑中,内容可能会继续补充、调整或重写。

从消费者环境出发

组件库不是必须同时输出 ESM 和 CJS。模块格式应由消费者决定:现代 bundler 项目通常可以只消费 ESM;仍需支持旧 Node.js、CommonJS 工具或特定测试环境时,才考虑双产物及其额外验证成本。

双产物可能产生同一个包被加载两次、默认导入语义不同或状态不共享等 dual-package hazard。发布前必须在真实消费者环境验证,不能只确认包自身能够构建。

设计发布入口

使用 package.jsonexports 明确公共入口,阻止消费者依赖内部文件:

{
  "name": "@example/ui",
  "type": "module",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js"
    },
    "./button": {
      "types": "./dist/button.d.ts",
      "import": "./dist/button.js"
    },
    "./style.css": "./dist/style.css"
  },
  "files": ["dist"]
}

如果确实输出 CJS,再增加经过验证的 require 条件。不要让 mainmoduleexports 和类型入口分别指向语义不同的实现。

按需加载成立的条件

  • 入口与子路径使用静态 ESM 导出
  • 组件之间没有隐藏的全局注册
  • sideEffects 正确保留必要的 CSS 和初始化代码
  • 框架、渲染器等宿主依赖声明为合理的 peer dependency
  • 每个入口的类型和样式都能独立解析

按需加载最终要通过消费应用的产物图验证。仅仅提供 import { Button } 语法,不代表未使用组件一定会被删除。

类型声明

类型声明必须与实际 JavaScript 入口一一对应。库的模块解析规则可能与应用 bundler 不同,尤其要验证 Node.js nodenext 消费者、bundler 消费者和声明文件中的相对导入。

消费者测试

发布前先生成 tarball,并在至少两个最小项目中安装:

  1. 当前支持的主流 bundler 应用
  2. 声明的最低 Node.js、TypeScript 和框架版本组合

执行类型检查、单元测试和生产构建,检查包中实际发布的文件,而不是工作区源码别名。

验收标准

  • exports 只暴露承诺维护的公共入口
  • ESM/CJS 策略有明确消费者依据
  • 未使用组件不会进入消费应用产物
  • 必要 CSS 不会被错误 Tree Shaking
  • 框架依赖不会在消费应用中重复打包
  • tarball 消费测试覆盖类型、样式和生产构建

参考资料