web3.js 4.x 包体积优化实战:按官方 Tree Shaking 指南裁剪依赖的完整教程
【免费下载链接】web3.jsCollection of comprehensive TypeScript libraries for Interaction with the Ethereum JSON RPC API and utility functions.项目地址: https://gitcode.com/gh_mirrors/we/web3.js
本文对应仓库文档 docs/docs/guides/13_advanced/tree_shaking.md,是 web3.js 官方向导中"高级(Advanced)"章节下的 Tree Shaking 支持指南。web3.js 4.x 采用 monorepo 模块化架构,把全部功能打包进一个"总包"会显著增大产物体积;本指南将带你通过四个明确步骤(开启 production 模式、声明 sideEffects、升级 tsconfig 模块标准、按需引入子包)让打包器准确剔除未使用代码,并给出可复用的 webpack 配置与验证方法,帮助你为 DApp 构建出最小化的生产包。
为什么 Tree Shaking 对 web3.js 如此重要:模块化架构带来的体积挑战
web3.js 4.x 不再是一个单一的大库,而是由分布在 packages/ 下的十几个独立子包组成的 monorepo。以总包 packages/web3/package.json 的dependencies为例,它依赖了web3-core、web3-eth、web3-eth-abi、web3-eth-accounts、web3-eth-contract、web3-eth-ens、web3-eth-iban、web3-eth-personal、web3-net、web3-providers-http、web3-providers-ws、web3-rpc-methods、web3-rpc-providers、web3-types、web3-utils、web3-validator等 16 个以上的运行时依赖。
更关键的是,总包的入口 packages/web3/src/index.ts 会把所有子模块统一 re-export:既有export { Web3Eth } from 'web3-eth'这样的具名导出,也有export * as core from 'web3-core'、export * as utils from 'web3-utils'、export * from 'web3-types'这类"全量再导出"。从源码结构可以推断:如果你只从web3这一个包导入,打包器在无法静态证明哪些导出未被使用时,往往会保留大量实际用不到的代码。
因此,web3.js 官方在13_advanced章节中专门给出了 4 个标准步骤,配合打包器(尤其是 webpack)的 Tree Shaking 能力,把"用不到的功能"从最终 bundle 中剔除。下面我们逐一展开。
第一步:开启生产模式(mode: 'production')
Tree Shaking 依赖打包器做静态分析,而 webpack 只有在production模式下才会默认启用一连串优化,包括代码压缩(minification)与未使用导出剔除。在项目根目录的webpack.config.js中设置:
module.exports = { mode: 'production', // ...其余配置 };设置mode: 'production'后,webpack 会默认开启(5.x 及以上):
- TerserPlugin 代码压缩:移除未使用代码、空白与死代码分支;
optimization.usedExports:标记并剔除未被使用的具名导出(即 Tree Shaking 的核心机制);DefinePlugin注入process.env.NODE_ENV = 'production':让依赖库剪掉开发分支逻辑;sideEffects标记生效:允许跳过"未使用模块"的整体加载(与第二步配合)。
值得注意的是,web3.js 仓库自己在构建浏览器发行版时同样使用production模式:共享构建配置 webpack.base.config.js 中写死了mode: 'production',并配置了 packages/web3/webpack.config.js 生成web3.min.js。也就是说,官方生产包本身就是按生产模式压缩产物,这从侧面印证了该步骤的必要性。
第二步:在 package.json 中声明 sideEffects
在你自己项目的package.json中加入:
{ "name": "my-dapp", "sideEffects": false }sideEffects: false告诉打包器:本项目的模块在导入时不会产生任何副作用(不修改全局对象、不注册副作用),因此打包器可以安全地跳过"未被引用"模块的整个加载过程,只保留被使用到的导出。
需要注意两点:
- 如果你有真正的副作用代码(例如全局样式
import './global.css'、polyfill、全局事件监听),应改用数组形式精确声明:
{ "sideEffects": ["*.css", "./src/polyfills.ts"] }- 好消息是:web3.js 的若干子包已经自带该声明。例如 packages/web3-utils/package.json 第 2~3 行就明确写了
"sideEffects": false,所以当你从web3-utils按需导入工具函数时,打包器可直接跳过未用模块;web3-eth等包同样通过module/exports字段提供了可供 tree shaking 的 ESM 产物(见 packages/web3-eth/package.json)。
关于sideEffects更细粒度的语义,可参考 webpack 官方 Tree Shaking 指南(原文档中亦附有该链接),核心结论是:声明得越明确,剔除得越彻底。
第三步:将 tsconfig 的 module 设为 ES2015 或更高
Tree Shaking 依赖ES Modules(import/export)的静态结构,而require()是运行时动态加载,无法被静态分析。因此需要把 TypeScript 编译目标调整为 ESM:
{ "compilerOptions": { "module": "ES2015" } }ES2015及其以上(如ES2020、ESNext)都会把 TypeScript 编译成标准 ESM 语法,使打包器可以逐导出分析依赖图。
从 web3.js 仓库自身也能看到这套实践:所有子包都同时产出 CJS 与 ESM 两份构建,例如 packages/web3/tsconfig.esm.json 将module设置为es2020并把输出写到lib/esm/;而总包的 packages/web3/package.json 通过exports字段区分条件:
"exports": { ".": { "types": "./lib/types/index.d.ts", "import": "./lib/esm/index.js", "require": "./lib/commonjs/index.js" } }这意味着:当你用 ESMimport引入 web3.js 时,打包器会自动命中import条件的 ESM 产物,从而具备 tree shaking 的前提;只有当你用require()时才会落入 CJS 产物。仓库的esm_black_box与cjs_black_box两套测试(见 packages/web3/test/esm_black_box 与 packages/web3/test/cjs_black_box)正是对这两种消费方式的回归验证。
第四步:按需引入你真正需要的子包
这是四步中收益最直接的一步:不要从总包web3导入所有功能,而是只安装并导入你需要的子包。
场景一:只需要web3.eth能力
原文档给出的示例,JavaScript 版本:
const { Web3Eth } = require('web3-eth'); // ...直接使用 Web3Eth 实例TypeScript 版本:
import { Web3Eth } from 'web3-eth'; // ...直接使用 Web3Eth 实例web3-eth提供了与以太坊区块链及智能合约交互的完整能力(Web3Eth类),见 packages/web3-eth/src/web3_eth.ts。只依赖这一个包,就不会把账户、ENS、IBAN、Personal 等模块的代码一并拖入 bundle。
场景二:只需要web3-utils里的几个函数
如果只用到进制转换工具,逐个具名导入即可:
const { numberToHex, hexToNumber } = require('web3-utils'); // ...import { numberToHex, hexToNumber } from 'web3-utils'; // ...得益于web3-utils自带的"sideEffects": false,这种按函数导入的方式可以被打包器精确裁剪,只保留这两个函数及其传递依赖,而不是整个工具库(工具函数实现位于 packages/web3-utils/src/converters.ts 等文件)。
可选子包速查
仓库 packages/ 下按功能拆分的可用子包包括:web3-eth(以太坊交互)、web3-eth-contract(合约)、web3-eth-accounts(账户与签名)、web3-eth-abi(ABI 编解码)、web3-eth-ens(ENS)、web3-eth-iban(IBAN)、web3-eth-personal(Personal 接口)、web3-net(网络属性)、web3-utils(工具函数)、web3-validator(数据校验)、web3-providers-http/web3-providers-ws/web3-providers-ipc(各类 Provider)、web3-rpc-methods(RPC 封装)、web3-rpc-providers(公共 RPC Provider)、web3-core(核心上下文与请求管理)、web3-errors(错误体系)、web3-types(类型定义)、web3-account-abstraction(账户抽象)等。按需选择最小依赖集,是控制包体积的根本手段。
完整示例:在 React 应用中配置 webpack 启用 Tree Shaking
原指南末尾提到官方提供了一个专门演示 Tree Shaking 的 React 示例应用(web3js-example-react-app)。下面把它对应的四步整合成一个可直接落地的完整配置。
1.package.json声明副作用并引入所需依赖:
{ "name": "my-web3-dapp", "private": true, "sideEffects": false, "dependencies": { "web3-eth": "^4.11.1", "web3-utils": "^4.3.3" }, "devDependencies": { "webpack": "^5", "webpack-cli": "^5", "ts-loader": "^9", "typescript": "^5" } }2.tsconfig.json使用 ESM 模块标准:
{ "compilerOptions": { "target": "ES2020", "module": "ES2015", "moduleResolution": "node", "strict": true, "esModuleInterop": true, "jsx": "react" } }3.webpack.config.js开启生产模式:
const path = require('path'); module.exports = { mode: 'production', // 关键:启用压缩与 tree shaking entry: './src/index.tsx', output: { path: path.resolve(__dirname, 'dist'), filename: 'bundle.[contenthash].js', }, resolve: { extensions: ['.ts', '.tsx', '.js'], }, module: { rules: [ { test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/, }, ], }, // production 模式下 webpack 已默认开启 usedExports; // 配合 "sideEffects": false 即可实现文件级与导出级双重裁剪 optimization: { usedExports: true, minimize: true, }, };4. 业务代码只导入所需功能:
import { useEffect, useState } from 'react'; import { Web3Eth } from 'web3-eth'; import { numberToHex } from 'web3-utils'; const web3Eth = new Web3Eth('https://eth-mainnet.example.com'); export function Balance({ address }: { address: string }) { const [balance, setBalance] = useState<string>(); useEffect(() => { web3Eth .getBalance(address) .then((wei) => setBalance(numberToHex(wei))); }, [address]); return <div>balance(hex): {balance}</div>; }构建后检查dist/bundle.*.js的大小,再对比"从web3总包导入"的基线版本,即可直观看到 Tree Shaking 带来的体积差异。
如何验证 Tree Shaking 是否真的生效
配置完成后,建议从以下角度验证裁剪效果:
- 使用打包分析工具:web3.js 仓库自身就提供了分析入口 packages/web3/webpack.analyze.js,并通过
build:web:analyze脚本(见 packages/web3/package.json)调用,生成依赖体积分布报告。你可以用同样的思路接入webpack-bundle-analyzer,检查 bundle 中是否还残留未使用的 web3 模块。 - 对比基线:分别以"从
web3导入"和"从具体子包导入"构建两次,对比产物大小,确认第四步的收益。 - 检查产物内容:在压缩产物中搜索未使用模块的特征字符串(如
eth_personal相关方法名),若已消失则说明已被剔除。 - 确认构建链路:确认
mode为production、package.json中有sideEffects声明、tsconfig 的module为 ES2015 以上、且全程未使用require()引入 web3 相关模块。
常见误区与注意事项
require()无法触发 Tree Shaking:即使mode: 'production',用 CommonJS 引入也无法做导出级静态裁剪。请统一使用 ESMimport,并保证 tsconfig 的module不低于ES2015。- 从总包
web3导入会拉入更多依赖:由于 packages/web3/src/index.ts 做了命名空间全量再导出,从web3导入时很难保证裁剪彻底;能直接导入子包就优先导入子包。 sideEffects不能盲目声明为false:仅当你的模块确实无副作用时才可如此声明;有样式、polyfill 等全局副作用的模块要用数组形式精确列出,否则可能出现"功能被莫名剔除"的运行期问题。- 浏览器直引与打包器无关:如果直接以
<script>方式引入 packages/web3/package.json 中的browser产物dist/web3.min.js,那是整包 UMD 构建,本身不参与 tree shaking;只有走 ESM 打包链路才能享受上述优化。
小结
web3.js 4.x 的模块化架构为包体积优化提供了天然条件:开启production模式 → 声明sideEffects: false→ 使用 ES2015+ 模块标准 → 按需导入具体子包,四步缺一不可。本指南的四步配置与源码证据(总包的 re-export 结构、各子包的 ESM 产物与sideEffects声明、共享 webpack 生产配置)共同保证了:你的 DApp 最终只会携带真正用到的 web3 代码,在功能完整的前提下拿到尽可能小的生产 bundle。更多细节可回到原始指南 docs/docs/guides/13_advanced/tree_shaking.md 与同目录下的 custom_RPC.md、extend.md 继续探索高级用法。
【免费下载链接】web3.jsCollection of comprehensive TypeScript libraries for Interaction with the Ethereum JSON RPC API and utility functions.项目地址: https://gitcode.com/gh_mirrors/we/web3.js
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考