愈发简单的 JS 库开发

是的,再不看书,书里的知识就过时了

关于目录

  1. 实践
  2. 礼仪
  3. Q&A

关于实践

古法初始化项目

话不多说,先来一波古法新建文件以及文件夹

bash 复制代码
# 新建项目 && 进入项目
mkdir js-lib-temp & cd js-lib-temp
# 初始化
npm init -y
# 新建文件
mkdir -p src && touch src/index.js

打开 src/index.js,写点简单的代码

js 复制代码
export default function () {
  console.log('hello world')
}

ok,咱的库只输出个 hello world,接下来就开始到处 say hi 呗

古法打包配置初始化

目标是在三种模块规范 CommonJS、 ESModule、UMD 中正常使用,目前最为流行的就是通过 rollupjs 来打包

bash 复制代码
npm install --save-dev rollup

# 新建 config 文件夹,以及相关的配置文件
mkdir -p config

# 新建 UMD 配置
touch config/rollup.config.aio.js
# 新建 ESM 配置
touch config/rollup.config.esm.js
# 新建 commonjs 配置
touch config/rollup.config.js
# 新建整体配置
touch config/rollup.js

# 一行搞定,也不是不行
# mkdir -p config && touch config/rollup.config.aio.js config/rollup.config.esm.js config/rollup.config.js config/rollup.js

还需要下载一些插件 babel 与 rollup 相关的插件

bash 复制代码
# Babel 相关
# Babel 的核心库,负责将 ES6+ 代码转换为兼容旧环境的 JavaScript
npm install --save-dev @babel/core
# 优化 Babel 转译结果,减少重复的辅助代码,提升包体积和运行效率。
npm install --save-dev @babel/plugin-transform-runtime
# Babel 预设,自动根据目标环境转译最新的 JavaScript 语法
npm install --save-dev @babel/preset-env
# Babel 运行时依赖
# 配合 @babel/plugin-transform-runtime 使用,避免辅助函数重复
npm install --save-dev @babel/runtime


# rollup 相关
# 在 Rollup 打包时集成 Babel,支持源码转译。
npm install --save-dev @rollup/plugin-babel
# 让 Rollup 能处理 CommonJS 模块(如 npm 包),转换为 ES6 模块。
npm install --save-dev  @rollup/plugin-commonjs
# 让 Rollup 能解析 node_modules 里的第三方依赖。
npm install --save-dev  @rollup/plugin-node-resolve

古法打包逻辑代码

JS 复制代码
// config/rollup.js
const {
  babel,
} = require('@rollup/plugin-babel')
const pkg = require('../package.json')

const version = pkg.version
/**
 * 构建输出文件的头部注释信息
 * @type {string}
 */
let banner = `/*!
 * ${pkg.name} ${version}
 * Licensed under MIT
 */
`

/**
 * 获取 Babel 编译器插件配置
 * @param {object} [opt] - 可选参数(当前未使用)
 * @returns {import('@rollup/plugin-babel').BabelInputPlugin} Babel 插件实例
 */
function getCompiler(_opt) {
  return babel({
    babelrc: false, // 不读取外部 .babelrc 文件,使用此处配置
    presets: [
      [
        '@babel/preset-env',
        {
          targets: {
            browsers: '> 0.5%, last 2 versions, not dead', // 浏览器兼容性目标
            node: 'current', // LTS or 版本号
          },
          modules: false, // 不转换模块类型
          loose: false, // 严格模式
        },
      ],
    ],
    plugins: [
      [
        '@babel/plugin-transform-runtime',
        {
          corejs: 2, // 使用 core-js@2 polyfill
          // helpers: false,  // 注释掉,默认 helpers 为 true
          regenerator: false, // 不转换 generator
        },
      ],
    ],
    babelHelpers: 'runtime', // 使用 runtime helpers
    exclude: 'node_modules/**', // 排除 node_modules
  })
}

/**
 * 插件名称
 * @type {string}
 */
exports.name = 'js-lib-temp'

/**
 * 构建输出文件的头部注释信息
 * @type {string}
 */
exports.banner = banner

/**
 * 获取 Babel 编译器插件配置
 * @type {Function}
 */
exports.getCompiler = getCompiler
JS 复制代码
// config/rollup.config.js
const common = require('./rollup.js')

module.exports = {
  input: 'src/index.js',
  output: {
    file: 'dist/index.js',
    format: 'cjs',
    banner: common.banner,
  },
  plugins: [common.getCompiler()],
}
JS 复制代码
// config/rollup.config.esm.js
const common = require('./rollup.js')

module.exports = {
  input: 'src/index.js',
  output: {
    file: 'dist/index.esm.js',
    format: 'es',
    banner: common.banner,
  },
  plugins: [common.getCompiler()],
}
JS 复制代码
// config/rollup.config.aio.js
const commonjs = require('@rollup/plugin-commonjs')
const nodeResolve = require('@rollup/plugin-node-resolve')

const common = require("./rollup.js")

module.exports = {
  input: 'src/index.js',
  output: {
    file: 'dist/index.aio.js',
    format: 'umd',
    name: common.name,
    banner: common.banner,
  },
  plugins: [
    nodeResolve({
      main: true,
      extensions: ['.js'],
    }),
    commonjs({
      include: 'node_modules/**',
    }),
    common.getCompiler(),
  ],
}

修改 package.json 相关内容

  • main:CommonJS 规范的入口文件
  • module:ES Module 规范的入口文件,
json 复制代码
{
  "main": "dist/index.js", // 指定 CommonJS 规范的入口文件
  "module": "dist/index.esm.js", // 指定 ES Module 规范的入口文件
  "scripts": {
    "build:self": "rollup -c config/rollup.config.js",
    "build:esm": "rollup -c config/rollup.config.esm.js",
    "build:aio": "rollup -c config/rollup.config.aio.js",
    "build": "npm run build:self && npm run build:esm && npm run build:aio",
  }
}

运行 npm run build 后,即可在 CommonJS、ESModule、UMD 三个环境中运行了,调用示例:

关于调用

CommonJS (Node.js 环境)
js 复制代码
// test-cjs.js
const sayHi = require('./dist/index.js');
sayHi(); // 输出: hello world
ESModule (现代打包工具或浏览器支持 ESModule)
js 复制代码
// test-esm.mjs
import sayHi from './dist/index.esm.js';
sayHi(); // 输出: hello world
UMD (浏览器全局引用)
html 复制代码
<script src="./dist/index.aio.js"></script>
<script>
  // 假设 package.json 的 name 字段为 js-lib-temp
  jsLibTemp(); // 输出: hello world
</script>

关于礼仪

完善对应的目录与注释部份

bash 复制代码
├── README.md               # 项目说明文档
├── TODO.md                 # 待办事项记录(可选
├── CHANGELOG.md            # 变更日志(可选
├── config                   # Rollup 配置文件夹
│   ├── rollup.config.aio.js   # UMD 打包配置(适用于浏览器全局引用)
│   ├── rollup.config.esm.js   # ES Module 打包配置(适用于现代打包工具)
│   ├── rollup.config.js       # CommonJS 打包配置(适用于 Node.js)
│   └── rollup.js              # 公共 Rollup 配置与工具函数
├── dist                    # 打包输出目录
│   ├── index.aio.js            # UMD 格式输出文件
│   ├── index.esm.js            # ES Module 格式输出文件
│   └── index.js                # CommonJS 格式输出文件
├── package.json            # 项目依赖与配置信息
└── src                     # 源码目录
    └── index.js                # 主入口文件
README通常包含以下内容:
  1. 库介绍:简要说明库的功能、解决的问题和适用场景,帮助用户快速了解项目价值。
  2. 使用指南:提供安装、引入、调用等基础用法示例,让用户能快速上手。
  3. 贡献指南:说明如何参与贡献,包括代码规范、提 issue、提 PR 的流程,鼓励社区参与。

关于开源协议

选择合适的开源协议对于 JS 库的传播和社区贡献非常重要。常见的协议有 MIT、BSD、Apache 等,不同协议在专利授权、商标使用、责任承担等方面略有差异。下表对比了三种主流协议的常见权利:

MIT BSD Apache
商业用途 [x] [x] [x]
可以修改 [x] [x] [x]
可以分发 [x] [x] [x]
授予专利许可 [x]
私人使用 [x] [x] [x]
商标使用 [ ]
承担责任 [ ] [ ] [ ]

一般来说,MIT 协议最为宽松,适合大多数 JS 库项目。

Q&A

Q:Vite 与 Rollup 的关系、分别在什么场景下使用

A:Vite 和 Rollup 都是现代前端构建工具。Rollup 专注于库/包的打包,支持多种模块格式(如 CJS、ESM、UMD),适合开发 npm 包和 JS 库。Vite 则是基于 Rollup 的开发服务器和构建工具,主打开发体验(如热更新、极速冷启动),适合开发应用(如 SPA、组件库 demo),但底层生产构建依然用 Rollup。简单来说,开发库用 Rollup,开发应用用 Vite。

Q:为什么要分别输出 CJS、ESM、UMD 三种格式?

A:不同的使用场景和工具链对模块格式有不同要求。CJS 主要用于 Node.js 环境,ESM 适合现代打包工具和浏览器原生支持,UMD 兼容性最好,适合直接在浏览器通过 script 标签引入。三种格式都输出,能让你的库被更多场景和用户使用。

Q:如何让库支持 TypeScript?

A:可以在 src 目录下使用 TypeScript(如 src/index.ts),并安装相关依赖(typescript、@rollup/plugin-typescript 等)。Rollup 配置中引入 typescript 插件即可。建议同时生成类型声明文件(.d.ts),方便 TypeScript 用户使用。

Q:如何保证库的体积最小?

A:可以通过合理配置 Babel(如只引入需要的 polyfill)、使用 Rollup 的 tree-shaking、external 配置排除外部依赖、压缩输出(如使用 terser 插件)等方式减小包体积。

总结

感谢阅读

本文属于是阅读完《现代 JS 库开发:原理、技术与实践》的读后感

相关推荐
崔庆才丨静觅5 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了6 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅6 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅6 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅7 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊7 小时前
jwt介绍
前端
爱敲代码的小鱼7 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax