- 区块链
- Web3
【免费下载链接】ethers.js
Complete Ethereum library and wallet implementation in JavaScript.
本篇指南围绕 ethers.js 仓库中 misc/basedirs/lib.esm/README.md 展开,系统讲解lib.esm/目录在 ESM(ES Module)项目中的角色、它如何通过npm run build与tsconfig.esm.json生成,以及作为"构建产物只读目录"在使用与维护上应遵循的规则。读完本文,你将掌握:ESM 与 CommonJS 两种构建产物在 ethers.js 中如何共存、package.json的exports双入口如何路由到lib.esm、浏览器环境下的模块替换机制,以及如何在自己的 ESM 项目中正确importethers。
lib.esm 是什么:为 ESM 项目准备的构建产物目录
在 ethers.js 仓库中,lib.esm/目录存放的是使用 ES Module 语法(import/export)编写的编译产物,专门服务于采用 ESM 模块体系的 JavaScript/TypeScript 项目。与它并行的还有一个 lib.commonjs/ 目录,存放使用require的 CommonJS 版本,两者内容同源,只是模块格式不同。
对照两个目录各自的 README(lib.esm/README.md 与 lib.commonjs/README.md),可以看到结构高度一致,都明确写着:"The contents of this folder are for usingimport(或require)in ESM(或 CommonJS)projects"。
从目录实际内容看,lib.esm/与lib.commonjs/保持着镜像式的模块布局,均包含abi/、address/、constants/、contract/、crypto/、hash/、providers/、transaction/、utils/、wallet/、wordlists/等子模块目录,每个模块下同时产出.js、.js.map(source map)、.d.ts、.d.ts.map文件。这种一一对应的结构保证了:无论消费者使用哪种模块体系,都能以相同的路径结构访问到同一套 API。
与 CommonJS 版本的差异
| 维度 | lib.esm(ESM) | lib.commonjs(CommonJS) |
|---|---|---|
| 模块语法 | import/export | require/module.exports |
| 适用项目 | ESM 项目("type": "module"、.mjs、现代打包器) | CommonJS 项目(require) |
| 生成命令 | npm run build | npm run build-commonjs |
| 编译配置 | tsconfig.esm.json | tsconfig.commonjs.json |
| 声明文件 | 随 ESM 产物生成.d.ts与 source map | 随 CommonJS 产物生成.d.ts与 source map |
值得注意的是,lib.esm/package.json 中声明了"type": "module",这会让 Node.js 将目录内的.js文件一律按 ESM 解析;而lib.commonjs目录则保持默认的 CommonJS 语义。这是双格式共存时让 Node.js 正确区分模块类型的关键配置。
生成机制:npm run build 与 tsconfig.esm.json
lib.esm/不是手写维护的源码,而是由构建工具自动生成的产物。这一点在 misc/basedirs/lib.esm/README.md 中讲得很清楚:
- 目录内容通过
npm run build目标生成,底层调用的是tsc(TypeScript 编译器)与/tsconfig.esm.json配置; - 不要手动修改该目录中的任何文件,因为它们在
build-clean时会被整体删除并重新生成。
构建命令与脚本链路
查看 package.json 中的 scripts 可以还原完整的构建链路:
{ "scripts": { "build": "tsc --project tsconfig.esm.json", "build-commonjs": "tsc --project tsconfig.commonjs.json", "build-all": "npm run build && npm run build-commonjs", "build-clean": "npm run clean && npm run build && node lib.esm/_admin/update-version.js && node lib.esm/_admin/update-changelog.js && npm run build-all && npm run _build-dist && npm run _dist-stats", "clean": "rm -rf dist lib.esm lib.commonjs && cp -r misc/basedirs/* ." } }其中关键点是:
npm run build等价于tsc --project tsconfig.esm.json,产出lib.esm/;npm run build-commonjs等价于tsc --project tsconfig.commonjs.json,产出lib.commonjs/;npm run build-clean是"全量重建":先执行clean(删除dist、lib.esm、lib.commonjs三个目录,并从misc/basedirs/复制基础文件回来——这正是两个 README 模板的来源),再依次执行 ESM 构建、版本号更新、CHANGELOG 更新、CommonJS 构建、dist 打包与体积统计;auto-build(npm run build -- -w)开启 TypeScript 的 watch 模式,适合开发期间自动增量编译。
tsconfig.esm.json 的编译配置
tsconfig.esm.json 本身非常精简,通过extends继承公共基础配置:
{ "extends": "./tsconfig.base.json", "compilerOptions": { "module": "es2020", "outDir": "./lib.esm" } }它只覆盖了两个关键项:
"module": "es2020":指定输出 ES2020 模块语法(即标准的import/export),这是 ESM 产物与 CommonJS 产物(tsconfig.commonjs.json 中为"module": "commonjs")的根本区别;"outDir": "./lib.esm":指定输出目录。
其余编译选项统一继承自 tsconfig.base.json,包括"target": "es2022"、"strict": true、"declaration": true(生成.d.ts)、"declarationMap": true、"sourceMap": true(生成.js.map)、"rootDir": "./src.ts"(源码根目录为src.ts/)、"importHelpers": true(复用 tslib 减少重复代码)等。也就是说,lib.esm中的每个.js文件都对应src.ts/下的一个 TypeScript 源文件,.d.ts与.js.map则分别服务于类型提示和调试时的源码映射。
产物目录的只读属性
README 中特别强调了两条维护规则:
- 不要修改
lib.esm/内的文件——它们是编译输出,任何手改都会在下一次build或build-clean时被覆盖; - 如需修改
lib.esm/README.md本身,应修改其模板源 misc/basedirs/lib.esm/README.md(README 中原文指向/output/post-build/lib.esm,结合clean脚本中的cp -r misc/basedirs/* .可知,misc/basedirs/就是这些模板文件的实际存放位置),该目录在build-clean时会被重新复制回lib.esm/。
正确的做法是:改动一律落在src.ts/源码或misc/basedirs/模板中,然后重新构建。
双入口路由:package.json 如何把 ESM 消费者指向 lib.esm
lib.esm/之所以能在发布后直接供 ESM 项目使用,依靠的是根 package.json 中的exports字段——它是 Node.js 与现代打包器(如 Rollup、webpack、Vite)解析包内模块入口的依据。
{ "main": "./lib.commonjs/index.js", "module": "./lib.esm/index.js", "exports": { ".": { "import": "./lib.esm/index.js", "default": "./lib.commonjs/index.js" }, "./abi": { "import": "./lib.esm/abi/index.js", "default": "./lib.commonjs/abi/index.js" }, "./address": { "import": "./lib.esm/address/index.js", "default": "./lib.commonjs/address/index.js" }, "./wallet": { "import": "./lib.esm/wallet/index.js", "default": "./lib.commonjs/wallet/index.js" } } }解析逻辑如下:
- ESM 消费者(使用
import,或 Node.js 检测到"type": "module")命中"import"条件,加载./lib.esm/index.js; - CommonJS 消费者(使用
require)命中"default"条件,加载./lib.commonjs/index.js; - 细粒度子路径导入:
exports为ethers/wallet、ethers/providers、ethers/utils等子模块分别声明了 ESM 与 CommonJS 双入口,便于按需引入。官方文档 docs.wrm/getting-started.wrm 中的示例正是这种用法:import { HDNodeWallet } from "ethers/wallet";; "main"与"module"字段则作为旧工具链的兼容回退:main指向 CommonJS 版本,module指向 ESM 版本,供不支持exports的打包器使用。
值得注意的是,package.json 还声明了"sideEffects": false,配合 ESM 产物使用,打包器可安全地对lib.esm中的模块进行 tree-shaking(按需摇树优化),只保留实际用到的导出。
浏览器环境适配:lib.esm 内的平台替换
lib.esm/不仅服务于 Node.js 的 ESM 项目,也服务于浏览器端的 ESM 打包场景。根 package.json 与 lib.esm/package.json 中都配置了browser字段,把依赖 Node.js 内置能力(或 Node 专有 API)的实现替换为浏览器版:
{ "browser": { "./lib.esm/crypto/crypto.js": "./lib.esm/crypto/crypto-browser.js", "./lib.esm/providers/provider-ipcsocket.js": "./lib.esm/providers/provider-ipcsocket-browser.js", "./lib.esm/providers/ws.js": "./lib.esm/providers/ws-browser.js", "./lib.esm/utils/base64.js": "./lib.esm/utils/base64-browser.js", "./lib.esm/utils/geturl.js": "./lib.esm/utils/geturl-browser.js", "./lib.esm/wordlists/wordlists.js": "./lib.esm/wordlists/wordlists-browser.js" } }这六组映射覆盖了典型的 Node-only 模块:
- 加密原语(
crypto.js→crypto-browser.js,使用 Web Crypto / 浏览器安全随机源); - IPC Socket 提供者(
provider-ipcsocket.js→provider-ipcsocket-browser.js,浏览器中不存在本地 IPC 通道); - WebSocket 实现(
ws.js→ws-browser.js,替换 Node 的ws包为浏览器原生 WebSocket); - Base64 编解码(
base64.js→base64-browser.js,使用atob/btoa); - HTTP 请求(
geturl.js→geturl-browser.js,使用fetch/XMLHttpRequest); - 词库加载(
wordlists.js→wordlists-browser.js)。
根package.json中的映射键带./lib.esm/前缀,与子包内自带的映射互为补充,共同保证打包器在浏览器目标下替换掉这些模块。这解释了为何一个仓库能同时支撑 Node 服务端与浏览器端的 ESM 应用。
在 ESM 项目中实际使用 lib.esm
理解了生成机制与路由规则后,在真实的 ESM 项目中消费 ethers 就非常直接。安装与导入方式如下(示例源自 docs.wrm/getting-started.wrm):
# 安装 ethers npm install ethers// 方式一:整体导入,所有 API 挂载在 ethers 对象上 import { ethers } from "ethers"; // 方式二:按需导入少量对象 import { BrowserProvider, parseUnits } from "ethers"; // 方式三:从细粒度子路径导入(对应 package.json 的 exports 子入口) import { HDNodeWallet } from "ethers/wallet";在浏览器中,也可以直接用<script type="module">从 CDN 引入 ESM 构建:
<script type="module"> import { ethers } from "https://cdnjs.cloudflare.com/ajax/libs/ethers/6.7.0/ethers.min.js"; // Your code here... </script>当你的项目满足以下任一条件时,Node.js 或打包器就会命中lib.esm分支:
- 项目
package.json声明了"type": "module"; - 使用
.mjs扩展名; - 使用 Rollup、Vite、webpack 等以 ESM 为目标的现代打包器,并通过
"import"条件解析。
lib.esm中各子模块的入口文件(如 lib.esm/wallet/index.js、lib.esm/providers/index.js)会进一步导出该模块的完整 API 集合;而 lib.esm/ethers.js 汇总了全部公共导出(从 ABI 编解码、地址处理、合约、密码学到 Provider/Signer、交易、钱包与词库),lib.esm/index.js 则同时提供ethers命名空间对象与平铺导出两种形式。版本信息由 lib.esm/_version.js 导出(当前为6.17.0),该文件同样由构建流程中的update-version.js自动维护,不应手改。
小结
lib.esm/是 ethers.js 为 ESM 项目生成的只读构建产物,与lib.commonjs/一一对应,共用同一套 TypeScript 源码(src.ts/);- 它由
npm run build(即tsc --project tsconfig.esm.json)生成,tsconfig.esm.json通过"module": "es2020"与"outDir": "./lib.esm"决定输出格式与位置; - 根 package.json 的
exports字段将import消费者路由到lib.esm/index.js,将require消费者路由到lib.commonjs/index.js,并提供ethers/wallet等子路径细粒度入口; browser字段在浏览器打包场景下把 Node 专属实现替换为浏览器版(crypto、ws、base64、geturl、ipcsocket、wordlists);- 维护规则:不要手改
lib.esm/内文件,如需调整 README 模板应改 misc/basedirs/lib.esm/README.md,所有功能改动落在src.ts/后重新构建。
对源码级细节感兴趣的读者,可以继续深入 src.ts/index.ts、src.ts/ethers.ts(ESM 产物的编译输入)以及 lib.commonjs/README.md(对照理解 CommonJS 一侧的生成规则)。
- 区块链
- Web3
【免费下载链接】ethers.js
Complete Ethereum library and wallet implementation in JavaScript.
相关推荐
三步掌握noteDigger:零基础玩转智能音乐扒谱
三步掌握noteDigger:零基础玩转智能音乐扒谱 noteDigger是一款创新的纯前端音乐扒谱工具,通过智能频谱分析技术,帮助音乐创作者和爱好者轻松将音频
区块链Web3Automatisch 集成(App)开发指南:深入解析应用目录结构与各模块职责
Automatisch 集成(App)开发指南:深入解析应用目录结构与各模块职责 导读 本文以 Automatisch 官方文档《Folder Structur
工作流自动化后端前端低代码任务调度深入解析 ASP.NET Core(aspnetcore 仓库)构建产物 Artifacts 目录结构与使用指南
深入解析 ASP.NET Core(aspnetcore 仓库)构建产物 Artifacts 目录结构与使用指南 导读 从源码构建 aspnetcore htt
后端Web框架
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考