Viem 与 Ethers.js 对比 Viem 与 Ethers.js 都能完成以太坊 RPC 调用、合约交互、日志解析和交易签名。选择时应关注项目现有技术栈、类型安全要求、插件兼容性与团队经验,而不是简单判断某个库更先进。
设计差异
维度
Viem
Ethers.js
API 风格
函数式客户端与显式账户
面向对象的 Provider、Signer 与 Contract
类型系统
基于 ABI 推导参数和返回类型
类型支持完善,可配合 TypeChain
模块组织
公共客户端与钱包客户端分离
读取与签名能力通过对象组合
包体与导入
支持细粒度导入
核心入口统一,使用直观
生态
常与 Wagmi 等现代前端工具配合
历史项目、脚本和教程覆盖广泛
迁移成本
API 差异较大,需要重写调用层
版本升级也需关注破坏性变化
1 2 3 4 5 6 7 8 9 10 flowchart LR A[应用业务层] --> B{客户端库} B --> C[Viem publicClient] B --> D[Ethers Provider] C --> E[JSON-RPC 节点] D --> E A --> F[Viem walletClient] A --> G[Ethers Signer] F --> H[钱包或本地账户] G --> H
Viem 示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import { createPublicClient, createWalletClient, http, parseEther } from "viem" ;import { mainnet } from "viem/chains" ;import { privateKeyToAccount } from "viem/accounts" ;const account = privateKeyToAccount (process.env .PRIVATE_KEY as `0x${string } ` );const publicClient = createPublicClient ({ chain : mainnet, transport : http () });const walletClient = createWalletClient ({ account, chain : mainnet, transport : http (), }); const balance = await publicClient.getBalance ({ address : account.address });const hash = await walletClient.sendTransaction ({ to : "0x000000000000000000000000000000000000dEaD" , value : parseEther ("0.001" ), }); await publicClient.waitForTransactionReceipt ({ hash });
Ethers.js 示例 1 2 3 4 5 6 7 8 9 10 11 import { ethers } from "ethers" ;const provider = new ethers.JsonRpcProvider (process.env .RPC_URL );const signer = new ethers.Wallet (process.env .PRIVATE_KEY as string , provider);const balance = await provider.getBalance (signer.address );const transaction = await signer.sendTransaction ({ to : "0x000000000000000000000000000000000000dEaD" , value : ethers.parseEther ("0.001" ), }); await transaction.wait ();
两个示例的业务含义相同。差异主要体现在对象模型、类型推导和读取与签名职责的组织方式。
Hardhat 项目选择 初始化模板提供 Viem 或 Ethers.js 选项时,应优先选择与项目插件和已有测试一致的组合。已有 Ethers.js 代码库无需仅为追求新工具而迁移。新建 TypeScript 项目如果重视 ABI 类型推导,并计划使用 Wagmi,可以优先评估 Viem。若团队依赖大量 Ethers.js 示例、TypeChain 或既有部署脚本,选择 Ethers.js 更稳妥。
测试示例 1 2 3 4 5 6 7 8 9 10 import assert from "node:assert/strict" ;it ("returns the stored value" , async () => { const value = await publicClient.readContract ({ address : contractAddress, abi, functionName : "value" , }); assert.equal (value, 42n ); });
无论选择哪个库,都应固定主版本,集中封装 RPC 客户端,显式处理链 ID、确认数、交易替换和重试,并避免在前端代码中保存私钥。
结论 Viem 更强调函数式组合、客户端职责分离和 ABI 驱动的类型推导。Ethers.js 提供成熟直观的对象模型,并在历史项目中具有较广覆盖。面试或技术评审中,应结合实际约束说明选择依据,不应把生态规模或行业采用情况表述为永久不变的结论。