wagmi `getClient` Action 详解:获取 Viem Client 实例的完整指南

wagmi getClient Action 详解:获取 Viem Client 实例的完整指南

【免费下载链接】wagmi Reactive primitives for Ethereum apps 【免费下载链接】wagmi 项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi

getClient 是 wagmi Core 提供的核心 Action,用于获取当前配置(Config)对应的 Viem Client 实例。在本仓库(wagmi monorepo)中,它是所有底层链上操作(读合约、发交易、查余额、估 Gas 等)的公共起点,正确理解它的参数、返回类型与底层缓存机制,能帮助你写出类型安全且高效的应用代码。读完本文,你将掌握 getClient 的导入方式、chainId 参数的精确语义、返回值类型的推导规则,以及它在 createConfig 内部如何按链缓存 Client 的实现原理。

什么是 getClient

在 wagmi 的架构中,Config(由 createConfig 创建)持有全局状态(当前链、连接、连接器)以及每个链对应的 Viem Client。getClient 就是把这个 Client 取出来的标准入口:

import { getClient } from '@wagmi/core'

它位于 @wagmi/core 包的 actions 目录下,核心实现见 getClient.ts。与之配套的还有 React 框架层的 useClient Hook(见 useClient.ts),适合在组件内响应式地获取 Client。

基础用法

getClient 的第一个参数是 config,即由 createConfig 创建并通常在各处导出的全局配置对象。最简单的调用如下:

// index.ts
import { getClient } from '@wagmi/core'
import { config } from './config'

const client = getClient(config)

配套的配置示例(本仓库文档使用的标准写法)位于 config.ts:

// config.ts
import { createConfig, http } from '@wagmi/core'
import { mainnet, sepolia } from '@wagmi/core/chains'

export const config = createConfig({
  chains: [mainnet, sepolia],
  transports: {
    [mainnet.id]: http(),
    [sepolia.id]: http(),
  },
})

上面的 config 配置了 mainnet 与 sepolia 两条链,并为每条链注册了 http() 传输层。未传 chainId 时,getClient(config) 返回的是当前链(config.state.chainId,默认是 chains 数组中的第一条链)对应的 Client。

参数详解

chainId

类型:config['chains'][number]['id'] | undefined

chainId 用于指定要获取哪个链的 Viem Client。若不传,则使用配置中当前的链 ID。当需要跨链操作(例如在主链上读余额、在测试网上发交易)时,显式传入 chainId 非常关键:

// index.ts
import { getClient } from '@wagmi/core'
import { mainnet } from '@wagmi/core/chains'
import { config } from './config'

const client = await getClient(config, {
  chainId: mainnet.id, // [!code focus]
})

注意示例中使用了 await——实际上 getClient 是同步函数,这里写成 await 也完全合法(对同步返回值取 await 会直接得到该值),但不传 chainId 时用法一致,无需异步等待。

从源码类型定义看,GetClientParameters 基于 ChainIdParameter 泛型约束实现(见 properties.ts):

export type ChainIdParameter<
  config extends Config,
  chainId extends
    | config['chains'][number]['id']
    | undefined = config['chains'][number]['id'],
> = {
  chainId?:
    | (chainId extends config['chains'][number]['id'] ? chainId : undefined)
    | config['chains'][number]['id']
    | undefined
}

这意味着 TypeScript 会强制 chainId 必须是 config.chains 中已配置的链 ID。若传入未配置的链 ID(例如 123456),类型系统会直接报错,且在运行时返回 undefined(详见下文"行为边界")。

其他参数

getClient 仅接受 config 与 chainId 两个输入。它不像 getBalance、call 等 Action 那样需要额外的 address、abi 等业务参数——它只是 Client 的取用入口。

返回值类型

getClient 的返回值类型为 GetClientReturnType,可导入自 @wagmi/core:

import { type GetClientReturnType } from '@wagmi/core'

正常情况下返回类型为 Viem Client 实例。但注意该返回类型是条件类型:当传入的 chainId 属于已配置链时,返回对应链的精确 Client<transport, chain> 类型;当链未配置(不满足约束)时,返回类型收窄为 undefined。

在 getClient.test-d.ts 的类型测试中可以看到这一推导的实际效果:

// 默认场景:返回类型与 config.chains 元素一致,transport 为 'http'
const client = getClient(config)
expectTypeOf(client.chain).toEqualTypeOf<(typeof config)['chains'][number]>()
expectTypeOf(client.transport.type).toEqualTypeOf<'http'>()

// 指定 chainId:返回类型精确到该链
const client = getClient(config, { chainId: chain.mainnet.id })
expectTypeOf(client.chain).toEqualTypeOf<typeof chain.mainnet>()
expectTypeOf(client.transport.type).toEqualTypeOf<'http'>()

// 未配置的链:返回类型为 undefined
const client = getClient(config, { chainId: 123456 })
expectTypeOf(client).toEqualTypeOf<undefined>()

也就是说,只要你的 chains 与 transports 配置正确,从 getClient 取出的 Client 天然携带精确的链类型与传输层类型,后续传给 viem 的只读/写操作时无需任何类型断言。

底层实现与缓存机制

getClient Action 本身只是一个轻量封装(见 getClient.ts 第 40-52 行):

export function getClient<
  config extends Config,
  chainId extends config['chains'][number]['id'] | number | undefined,
>(
  config: config,
  parameters: GetClientParameters<config, chainId> = {},
): GetClientReturnType<config, chainId> {
  try {
    return config.getClient(parameters) as GetClientReturnType<config, chainId>
  } catch {
    return undefined as never
  }
}

关键逻辑在 config.getClient 中,其实现位于 createConfig.ts,核心流程如下:

  1. 解析目标链:chainId = parameters.chainId ?? store.getState().chainId,即显式传参优先,否则使用 store 中的当前链。
  2. 校验链是否配置:在 chains 列表中查找该 chainId;若显式指定了 chainId 但链未配置,抛出 ChainNotConfiguredError(错误类定义见 config.ts)。getClient Action 的 try/catch 正是把这种异常吞掉并返回 undefined。
  3. 兜底当前链:如果目标链未配置(未显式指定 chainId 时),则回退返回当前链的 Client。
  4. 内存缓存(memoization):clients 是一个 Map<number, Client>,以链 ID 为键缓存已创建的 Client。命中缓存直接返回,避免重复构建。
  5. 懒创建:缓存未命中时,如果 createConfig 传入了自定义 client 工厂函数,则调用 rest.client({ chain });否则基于 transports 与继承的 viem Client 配置(如 batch、pollingInterval 等)调用 viem 的 createClient 创建新 Client,然后写入缓存。

从源码结构可以看出两个重要设计:

  • 按链隔离与共享:每条链持有独立 Client,但同一条链的所有调用共享同一个实例(通过缓存),避免重复创建带来的资源浪费;
  • 默认开启 batch:当未显式配置 batch 时,createClient 使用 batch: { multicall: true }(createConfig.ts),让 viem 默认聚合多个调用。

行为边界:未配置链返回 undefined

这是 getClient 最容易踩坑的一点:传入未配置的 chainId 时,getClient 返回 undefined 而不是抛出异常。这一点由 getClient.test.ts 直接验证:

test('default', () => {
  expect(getClient(config)).toBeDefined()
})

test('behavior: unconfigured chain', () => {
  expect(
    getClient(config, {
      // @ts-expect-error
      chainId: 123456,
    }),
  ).toBeUndefined()
})

因此在使用返回值前建议做判空处理,或将 chainId 限定在配置链集合内(类型系统会帮助你)。

在生态中的位置:众多 Action 的公共入口

getClient 的价值不仅在于直接使用,更在于它是 wagmi 几乎所有链上 Action 的底层依赖。从源码搜索可以看到,大量 Action 在内部通过 config.getClient({ chainId }) 获取 Client 后再调用 viem,例如:

这也解释了为什么 getClient 是构建自定义链上逻辑的基石:当内置 Action 无法覆盖你的需求时,可以先用 getClient(config, { chainId }) 拿到精确类型的 Client,再直接调用 viem 的任意功能。

React 场景:使用 useClient Hook

在 React 应用中,更推荐通过框架层 Hook 获取 Client。useClient(useClient.ts)会自动从 WagmiProvider 注入的 config 中取值,并随当前链变化响应式更新,示例:

import { useClient } from 'wagmi'

function Component() {
  const client = useClient()
  // client 即为当前链的 Viem Client
}

它同样支持传入 chainId 参数。对于需要在组件外(事件回调、服务端逻辑)获取 Client 的场景,则使用本文的 getClient。

实践建议

  • 优先复用 config:getClient 要求显式传入 config,请确保全局只创建一份配置并统一导出,避免多次 createConfig 导致状态与缓存分裂。
  • 善用 chainId 实现多链逻辑:需要操作非当前链时,显式传入目标链 ID;同时利用类型约束,让编译器帮你拦截"链未配置"的低级错误。
  • 注意 undefined 判空:在动态 chainId(如来自用户输入)场景下,对 getClient 的返回值做防御性判断。
  • 深入阅读:完整的配置创建逻辑见 createConfig.ts,类型定义见 properties.ts,行为测试见 getClient.test.ts 与 getClient.test-d.ts,框架集成示例可参考 config.ts。

【免费下载链接】wagmi Reactive primitives for Ethereum apps 【免费下载链接】wagmi 项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值