wagmi Vue 开发指南:useBalance 组合式函数实现原生代币余额查询
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
本篇技术指南聚焦@wagmi/vue包中的useBalance组合式函数(Composable),它是基于 TanStack Query 封装的响应式查询工具,用于在 Vue 应用中获取链上地址的原生币(Native Currency,如 ETH)余额。读完本文,你将掌握useBalance的完整参数体系(地址、区块号、区块标签、链 ID、作用域键等)、查询配置与返回结构,并能从源码层面理解它如何与@wagmi/core的getBalanceaction 协作,最终在真实 Vue 应用中写出可复用的余额查询逻辑。
useBalance 是什么
useBalance是@wagmi/vue提供的用于获取原生货币余额(native currency balance)的组合式函数。所谓"原生货币"即链上的基础代币——以太坊主网的 ETH、测试网的测试币、以及任何 EVM 链的 native gas 代币,而非 ERC-20 合约代币(后者应使用useReadContract配合 ERC-20 ABI 查询)。
在 useBalance.ts 源码中,其返回类型定义如下:
export type UseBalanceReturnType<selectData = GetBalanceData> = UseQueryReturnType<selectData, GetBalanceErrorType>其中GetBalanceData的结构在@wagmi/core的 getBalance action 中定义:
export type GetBalanceReturnType = { decimals: number symbol: string value: bigint }即每次查询成功后会得到一个包含**小数位数(decimals)、代币符号(symbol)、余额数值(value,以 bigint 表示的最小单位数量)**的对象。以测试用例 useBalance.test.ts 中的断言为例,当查询chain.mainnet2链上某账户余额时,返回{ decimals: 18, symbol: 'WAG', value: 69000000000000000000n },这正是parseEther('69')对应的最小单位数值——value是原始 wei 值,展示时需要结合decimals自行格式化。
安装与导入
useBalance由@wagmi/vue包导出,导入方式如下:
import { useBalance } from '@wagmi/vue'该导出在 exports/index.ts 中登记(第 28 行useBalance,),同时也提供了配套的类型导出:
import { type UseBalanceParameters } from '@wagmi/vue' import { type UseBalanceReturnType } from '@wagmi/vue'在 Nuxt 等框架集成场景下,useBalance同样被 Nuxt 模块自动桥接导出(见 nuxt/module.ts),可无缝配合自动导入使用。
快速上手:查询一个地址的余额
useBalance最基本的用法是传入一个address,组合式函数会自动从最近的WagmiPlugin提供上下文中取出 Config,并使用当前激活链发起查询:
<!-- index.vue --> <script setup lang="ts"> import { useBalance } from '@wagmi/vue' const result = useBalance({ address: '0x4557B18E779944BFE9d78A672452331C186a9f48', }) </script>上面的示例依赖一个已经通过WagmiPlugin注册的 wagmi 配置,配置示例参见 config.ts:
import { createConfig, http } from '@wagmi/vue' import { mainnet, sepolia } from '@wagmi/vue/chains' export const config = createConfig({ chains: [mainnet, sepolia], transports: { [mainnet.id]: http(), [sepolia.id]: http(), }, })result是一个响应式查询对象,你可以在模板中这样渲染余额:
<template> <div v-if="result.isSuccess"> {{ result.data?.value }} ({{ result.data?.symbol }}) </div> <div v-else-if="result.isPending">加载中…</div> <div v-else-if="result.isError">{{ result.error?.message }}</div> </template>参数详解
useBalance的参数类型为UseBalanceParameters,它由@wagmi/core的GetBalanceOptions与ConfigParameter组合而来,并且通过DeepMaybeRef支持深度响应式参数——这意味着你可以传入ref()包裹的值,组合式函数内部会用deepUnref自动解包(见 useBalance.ts 第 32 行const params = computed(() => deepUnref(parameters)))。各参数说明如下。
address
- 类型:
Address | undefined - 说明:要查询余额的地址,必填。当
address为undefined时,查询的enabled会被置为false,即查询不会自动执行。
<script setup lang="ts"> import { useBalance } from '@wagmi/vue' import { mainnet } from '@wagmi/vue/chains' const result = useBalance({ address: '0x4557B18E779944BFE9d78A672452331C186a9f48', }) </script>这一"禁用逻辑"在@wagmi/core的 getBalance.ts 查询选项中实现:
enabled: Boolean(options.address && (options.query?.enabled ?? true)),也正因如此,address是支持响应式"从无到有"的:先传入undefined挂起查询,待地址可用(如用户连接钱包后)再更新,查询会自动启用。测试behavior: address: undefined -> defined(useBalance.test.ts)验证了这一点:初始时fetchStatus为idle,设置地址后自动发起查询并成功返回数据。
blockNumber
- 类型:
bigint | undefined - 说明:查询指定区块高度处的余额,用于历史快照场景。
<script setup lang="ts"> import { useBalance } from '@wagmi/vue' const result = useBalance({ address: '0x4557B18E779944BFE9d78A672452331C186a9f48', blockNumber: 17829139n, }) </script>注意blockNumber与blockTag互斥:在 getBalance action 中,二者通过三元表达式分别透传给 viem:
const value = await action( blockNumber !== undefined ? { address, blockNumber } : { address, blockTag }, )即指定了blockNumber就按该区块高度查询,否则按blockTag查询。
blockTag
- 类型:
'latest' | 'earliest' | 'pending' | 'safe' | 'finalized' | undefined - 说明:查询指定区块标签处的余额。默认由 viem 使用
'latest';'safe'与'finalized'仅在支持相应 RPC 方法的链上可用(如以太坊 PoS 后的合并相关标签)。
<script setup lang="ts"> import { useBalance } from '@wagmi/vue' const result = useBalance({ address: '0x4557B18E779944BFE9d78A672452331C186a9f48', blockTag: 'latest', }) </script>chainId
- 类型:
config['chains'][number]['id'] | undefined - 说明:指定查询目标链的 ID。默认使用
useChainId提供的当前激活链;显式传入chainId可以查询与当前链不同的其他链上的余额。
<script setup lang="ts"> import { useBalance } from '@wagmi/vue' import { mainnet } from '@wagmi/vue/chains' const result = useBalance({ address: '0x4557B18E779944BFE9d78A672452331C186a9f48', chainId: mainnet.id, }) </script>在实现层面(useBalance.ts 第 34-40 行),组合式函数通过useChainId获得当前链 ID,并在组装查询选项时执行chainId: params.value.chainId ?? chainId.value,实现"未指定则回退到当前链"的语义。测试parameters: chainId验证了跨链查询:传入chain.mainnet2.id后得到该测试链上的symbol: 'WAG'与余额值。
config
- 类型:
Config | undefined - 说明:显式传入
Config以替代从最近的WagmiPlugin上下文自动获取的配置。适用于脱离插件上下文、手动管理配置的场景(如测试、库开发)。
<script setup lang="ts"> import { useBalance } from '@wagmi/vue' import { config } from './config' const result = useBalance({ address: '0x4557B18E779944BFE9d78A672452331C186a9f48', config, }) </script>scopeKey
- 类型:
string | undefined - 说明:将查询缓存限定到指定上下文。具有相同 context(含相同
scopeKey及其他参数)的 Hook 会共享同一份缓存,适用于多个组件查询相同数据时避免重复请求,或为不同业务场景隔离缓存。
<script setup lang="ts"> import { useBalance } from '@wagmi/vue' const result = useBalance({ address: '0x4557B18E779944BFE9d78A672452331C186a9f48', scopeKey: 'foo', }) </script>query 选项
useBalance还支持在query字段中传入 TanStack Query v5 的查询参数(完整列表见 query-options.md 的共享文档),常用的包括:
| 选项 | 类型 | 说明 |
|---|---|---|
enabled | boolean \| undefined | 设为false可禁用查询自动运行;可用于依赖查询场景 |
gcTime | number \| Infinity \| undefined | 未使用/非活跃缓存数据的保留时间,默认5 * 60 * 1000(5 分钟),SSR 期间为Infinity;设为Infinity禁用垃圾回收 |
staleTime | number \| Infinity \| undefined | 数据被视为过期的毫秒数,默认0;设为Infinity则永不过期 |
refetchInterval | number \| false \| function | 定时轮询间隔(毫秒),可用于余额自动刷新 |
refetchOnWindowFocus | boolean \| 'always' \| function | 窗口聚焦时是否重新拉取,默认true |
retry | boolean \| number \| function | 失败重试策略,客户端默认3次,服务端默认0次 |
retryDelay | number \| function | 重试延迟,可传指数退避函数attempt => Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000) |
initialData | TData \| (() => TData) \| undefined | 初始缓存数据(会被持久化到缓存);默认视为过期,除非设置了staleTime |
placeholderData | TData \| function \| undefined | 挂起状态下的占位数据(不会持久化到缓存) |
select | (data: TData) => unknown | 转换/挑选返回数据,只影响组件拿到的data,不影响缓存内容 |
notifyOnChangeProps | string[] \| 'all' \| function | 控制组件仅在指定属性变化时重新渲染 |
networkMode | 'online' \| 'always' \| 'offlineFirst' \| undefined | 网络模式,默认'online' |
queryClient | QueryClient \| undefined | 使用自定义 QueryClient,否则使用最近上下文中的实例 |
meta | Record<string, unknown> \| undefined | 附加到缓存条目的元信息,可在queryFn的上下文中访问 |
注意:
queryFn与queryKey由 wagmi 内部使用,不可覆盖;其余 TanStack Query 参数均可用。
例如,实现"每 15 秒自动刷新余额":
<script setup lang="ts"> import { useBalance } from '@wagmi/vue' const result = useBalance({ address: '0x4557B18E779944BFE9d78A672452331C186a9f48', query: { refetchInterval: 15_000, }, }) </script>select 转换数据
通过query.select可以直接从查询结果中提取你关心的字段,其类型会随之收窄。类型测试 useBalance.test-d.ts 演示了这一点:
const result = useBalance({ query: { select(data) { return data?.value }, }, }) // result.data 的类型被推断为 Ref<bigint> | Ref<undefined>返回类型
useBalance的返回值UseBalanceReturnType是 TanStack Query 的响应式结果(UseQueryReturnType),核心字段(完整说明见 query-result.md)如下:
| 字段 | 类型 | 说明 |
|---|---|---|
data | { decimals: number; symbol: string; value: bigint } \| undefined | 最近一次成功解析的数据,默认undefined |
error | null \| GetBalanceErrorType | 查询抛出的错误对象,默认null |
status | 'error' \| 'pending' \| 'success' | 查询状态:pending无缓存且未完成、error失败、success成功 |
fetchStatus | 'fetching' \| 'idle' \| 'paused' | 是否正在拉取;fetching表示queryFn执行中,idle表示未拉取 |
isPending/isError/isSuccess | boolean | 由status派生的布尔标识 |
isLoading | boolean | 首次拉取进行中,等价于isFetching && isPending |
isFetching/isRefetching | boolean | 是否正在拉取 / 是否在后台重新拉取 |
isStale | boolean | 缓存数据是否已失效或超出staleTime |
refetch | function | 手动重新拉取,可传{ cancelRefetch, throwOnError } |
failureCount/failureReason | number/null \| error | 失败次数与失败原因 |
dataUpdatedAt/errorUpdatedAt | number | 数据/错误最近更新时间戳 |
注意data.value是 bigint 类型的最小单位数值,若要显示为带小数的可读金额,需要结合decimals自行格式化(如使用 viem 的formatUnits)。
底层实现原理
理解useBalance的内部工作方式,有助于排查缓存、链切换与响应式更新等问题。其实现(useBalance.ts)可分为三层:
第一层:Vue 响应式封装。useBalance将入参包在computed(() => deepUnref(parameters))中,从而把ref、嵌套响应式对象等深度解包为普通值;随后通过useConfig(params)获取 Config(若参数中未显式传入config,则从WagmiPlugin上下文获取),通过useChainId({ config })订阅当前链 ID 的变化(见 useChainId.ts,它内部用watchChainId监听链切换并在组件作用域销毁时自动取消订阅)。
第二层:查询选项组装。组合式函数调用getBalanceQueryOptions(config, { ...params, chainId: params.chainId ?? chainId.value })生成 TanStack Query 选项(query/getBalance.ts)。该函数做了两件关键事:
- 计算
enabled:Boolean(options.address && (options.query?.enabled ?? true)),地址缺失时整条查询静默禁用; - 生成查询键
['balance', filterQueryOptions(options)],参数变化会改变查询键,从而触发自动重新请求。
第三层:底层 action 调用。真正发请求的是@wagmi/core的getBalanceaction(actions/getBalance.ts)。它通过config.getClient({ chainId })按链 ID 获取 viem 客户端,再用getAction(client, viem_getBalance, 'getBalance')调用 viem 的getBalance,最后从config.chains或client.chain中取出该链的nativeCurrency元数据,将 viem 返回的裸数值补充为{ decimals, symbol, value }结构。这也解释了为什么返回值中的symbol与decimals来自链配置而非 RPC 响应。
此外,@wagmi/core/query还导出了配套的getBalanceQueryKey、getBalanceQueryOptions、GetBalanceData、GetBalanceOptions等类型与工具(详见 query-imports.md),供需要在 query 层面做更精细控制的进阶场景使用:
import { type GetBalanceData, type GetBalanceOptions, type GetBalanceQueryFnData, type GetBalanceQueryKey, getBalanceQueryKey, getBalanceQueryOptions, } from '@wagmi/vue/query'测试验证与边界行为
仓库测试(useBalance.test.ts)覆盖了该组合式函数的几个关键行为,可作为使用时的行为参考:
- 默认查询:传入
address后查询成功,返回结构包含decimals、symbol、value三个字段。 - 链参数:显式传入
chainId可查询指定链的余额,返回该链的原生币符号与精确数值。 - 地址从无到有:
address初始为undefined时查询处于idle挂起状态;地址变为有效值后自动启用并成功返回——这正是钱包连接后余额自动加载的典型交互模式。 - 缺少必要属性时禁用:仅传地址、缺少其他条件时查询保持
idle,不会发起无效请求。
更多资源
- 底层 action 文档:
getBalance - 配置对象说明:
createConfig与WagmiPlugin - Vue 组合式函数总览:composables
- 参考实现源码:useBalance.ts、getBalance.ts、getBalance.ts
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考