wagmi 框架适配指南:基于 Wagmi Core 构建 Svelte、Solid.js 等框架的响应式集成
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
Wagmi Core 是纯 VanillaJS 实现的以太坊交互核心层,天然可以在任意 JavaScript 框架中使用。本文基于 wagmi 仓库中的官方框架适配指南(site/core/guides/framework-adapters.md),系统讲解如何将 Wagmi Core 与 React、Vue 之外的框架(如 Svelte、Solid.js、Angular)做深度集成:包括依赖注入、响应式订阅、TanStack Query 桥接、测试策略与代理导出五个核心主题。读完本文,你将掌握复用 Wagmi Core 的完整方法论,并能在自己的框架里复刻useConnection、useChainId、useClient、useConnectorClient这类关键原语,让链切换、账户连接等状态变化自动驱动整个应用的 UI 更新。
为什么框架适配是必要的
Wagmi Core 本身不依赖任何框架。从源码结构看,packages/core 中的createConfig、getConnection、getChainId、watchConnection等 actions 全部是纯函数式 API,任何框架都可以直接调用。官方文档的答复很明确:你现在就可以用——Wagmi Core 就是一份可嵌入任意框架的 VanillaJS 库。
但对许多开发者来说,这种"裸用"并不够爽快。以 React 和 Vue 为代表的官方框架包(packages/react、packages/vue)之所以体验好,是因为它们把 Core 与框架的响应式系统做了深度绑定:
- React:通过 Hooks 把状态订阅收敛到组件内,链切换、连接账户后组件自动重渲染;
- Vue:通过 composables 把同样的能力封装成响应式 ref。
如果你希望 Svelte、Solid.js、Angular 等框架也获得同等的集成体验,就需要自己动手做"适配层"。官方在文档中坦言,核心团队目前没有足够时间高质量地维护更多框架包,但社区成员完全可以主导这项工作——这正是本文要帮你做到的事。
依赖注入:让 Config 贯穿你的框架
为什么需要注入
创建 Wagmi Config 之后,框架内的所有高级函数(React 的 Hooks、Vue 的 composables)都需要访问它。如果不做注入,使用者每次调用都要手动传 Config,体验很差。Wagmi 的做法是通过框架自带的依赖注入机制把 Config 放进全局作用域:
- React 使用 React Context);
- Vue 使用
app.provide(对应实现见 packages/vue/src/plugin.ts)。
React 的注入实现
packages/react/src/context.ts 中,WagmiContext是一个默认值为undefined的 Context,WagmiProvider通过WagmiContext.Provider把 Config 提供给整棵组件树:
export const WagmiContext = createContext< ResolvedRegister['config'] | undefined >(undefined) export function WagmiProvider(parameters: React.PropsWithChildren<WagmiProviderProps>) { const { children, config } = parameters const props = { value: config } return createElement( Hydrate, parameters, createElement(WagmiContext.Provider, props, children), ) }而消费端 useConfig 的逻辑非常直白:优先使用显式传入的parameters.config,否则从 Context 中读取;读不到就抛出WagmiProviderNotFoundError:
export function useConfig<config extends Config>(parameters = {}) { const config = parameters.config ?? useContext(WagmiContext) if (!config) throw new WagmiProviderNotFoundError() return config }Vue 的注入实现
packages/vue/src/plugin.ts 用 Symbol 作为注入键,通过 Vue 的插件机制把 Config 挂到应用上:
export const configKey = Symbol() export const WagmiPlugin = { install(app, options) { const { config, reconnectOnMount = true } = options app.provide(configKey, config) const { onMount } = hydrate(config, { ...options, reconnectOnMount }) onMount() }, }注意 Vue 插件在安装时还会调用hydrate的onMount()处理挂载重连(reconnect)与状态水合。在你的框架里,依赖注入这一步要完成两件事:一是让组件/合成函数能拿到 Config;二是在 Config 变化时(比如用户重新创建 Config)让持有方同步更新。
响应式层:订阅 Config 状态变化
核心原则:跟踪 Config 的变化
每个框架的响应式哲学都不同:React 靠不可变状态 + 重渲染,Vue 靠响应式 ref,Solid.js 靠 signal,Svelte 靠编译期响应式。把 Wagmi Core 与框架打通,最关键的一点是:让 Config 的状态变化被框架追踪。只有这样,"切换链"或"连接/断开账户"这类操作才能在整个应用中传播并更新 UI 状态。
Wagmi Core 为此提供了完整的watch*系列订阅 API(都在 packages/core/src/actions 下),它们都基于config.subscribe实现,并返回取消订阅函数:
- watchConnection:订阅连接状态,内部用
deepEqual比较,并单独比对 connector 的id与uid; - watchChainId:订阅当前链 ID,直接取
state.chainId; - watchClient:订阅当前链的 viem Client,用
uid做相等性比较; - watchConnectors:订阅连接器列表,同样基于
deepEqual。
官方推荐的四个关键原语
文档特别点名了四个"最值得优先实现"的 Hook,因为它们在生态里被大量复用,是很多内部机制的地基:
| 原语 | 对应 Core 订阅 API | 用途 |
|---|---|---|
useConnection | watchConnection/getConnection | 获取当前账户地址与连接器,几乎所有账户相关逻辑的基础 |
useChainId | watchChainId/getChainId | 获取当前链 ID,驱动链相关查询与切换 |
useClient | watchClient/getClient | 获取当前链的 viem Client,是读写链上数据的总入口 |
useConnectorClient | getConnectorClientQueryOptions | 获取当前连接器的签名 Client,用于发送交易、签名消息 |
React 版 useConnection(subscribe + getSnapshot 模式)
useConnection 是标准的"外部 store 接入 React"范式:把watchConnection的订阅函数传给useSyncExternalStoreWithTracked,getSnapshot返回getConnection(config):
export function useConnection(parameters = {}) { const config = useConfig(parameters) return useSyncExternalStoreWithTracked( (onChange) => watchConnection(config, { onChange }), () => getConnection(config), ) }React 版 useChainId
useChainId 结构类似,订阅watchChainId,并额外传入getServerSnapshot以便 SSR 场景使用:
export function useChainId(parameters = {}) { const config = useConfig(parameters) return useSyncExternalStore( (onChange) => watchChainId(config, { onChange }), () => getChainId(config), () => getChainId(config), ) }Solid.js 版 useChainId(signal + effect + cleanup)
有趣的是,wagmi 仓库里其实已经有一个官方维护的 Solid 包(packages/solid),可以作为"框架适配"的绝佳活教材。Solid 版 useChainId 用createSignal保存状态、createEffect建立订阅、onCleanup释放订阅:
export function useChainId(parameters = () => ({})) { const config = useConfig(parameters) const [chainId, setChainId] = createSignal(getChainId(config())) createEffect(() => { const _config = config() setChainId(() => getChainId(_config)) const unsubscribe = watchChainId(_config, { onChange(data) { setChainId(() => data) }, }) onCleanup(() => unsubscribe()) }) return chainId }注意它的参数是Accessor<SolidParameters<config>>(即() => ({})),返回Accessor<GetChainIdReturnType<config>>——这就是把 Core 的同步 API 翻译成 Solid 响应式访问器的完整思路。从 packages/solid/src/primitives 的useBalance、useBlockNumber等文件可以看到,useChainId是被大量复用的基础原语。
给你的框架落地时的自查清单
useConnection/useChainId/useClient/useConnectorClient四个原语是否已实现且语义对齐?- 订阅是否在组件/合成函数卸载时正确取消(防止内存泄漏)?
- Config 对象本身变化时,是否重新建立订阅(参考 Solid 版在
createEffect内重新订阅的做法)? - SSR 场景下是否提供
getServerSnapshot之类的同构取值路径?
通过 TanStack Query 获得缓存与去重能力
Wagmi 在 React 和 Vue 中依赖 TanStack Query 实现缓存、请求去重、持久化、重试等能力。好消息是:TanStack Query 不止支持 React/Vue——截至官方文档撰写时,它还支持 Svelte、Solid、Angular。这意味着你不需要为框架另找一套数据请求库,直接用 TanStack Query 的对应 adapter 即可。
从@wagmi/core/query导入现成能力
关键入口是'@wagmi/core/query'这个子路径导出。它把 Core 的 action 包装成可直接塞进 TanStack Query 的 query options / mutation functions:
- 查询类:
getBalanceQueryOptions、getBlockNumberQueryOptions、getChainIdQueryOptions等; - 变更类:
connectMutationOptions、disconnectMutationOptions、sendTransactionMutationOptions、signMessageMutationOptions等。
从 packages/core/src/exports/query.ts 可以看到,这些函数从 packages/core/src/query 目录批量 re-export。以getConnectorClientQueryOptions为例,它同时产出了queryKey、queryFn等完整配置,React 版 useConnectorClient 正是把这份 options 原样交给useQuery:
export function useConnectorClient(parameters = {}) { const config = useConfig(parameters) const chainId = useChainId({ config }) const { address, connector } = useConnection({ config }) const options = getConnectorClientQueryOptions(config, { ...parameters, chainId: parameters.chainId ?? chainId, connector: parameters.connector ?? connector, query: parameters.query as any, }) return useQuery(options) as any }注意useConnectorClient组合了useChainId与useConnection的结果来推导默认参数——这就是文档所说的"连接、链 ID 等原语驱动了大量内部机制"的具体体现。
在你的框架中接入
在你的框架里,安装并配置对应的 TanStack Query adapter(Svelte/Solid/Angular),然后把'@wagmi/core/query'导出的xxxQueryOptions/xxxMutationOptions直接传给 adapter 的useQuery/useMutation等价函数即可,无需重写请求逻辑。
给库作者的泛型提醒
如果你在构建一个面向公众的库,必须正确接好泛型,才能保证类型推断与类型安全。例如上面 React 版的参数类型:
export type UseConnectorClientParameters< config extends Config = Config, chainId extends config['chains'][number]['id'] = config['chains'][number]['id'], selectData = GetConnectorClientData<config, chainId>, > = Compute< GetConnectorClientOptions<config, chainId, selectData> & ConfigParameter<config> >它把 Core 的GetConnectorClientOptions与框架自身的ConfigParameter交叉组合,既保留了 Core 的完整类型约束,又暴露了框架层的默认参数。想确认自己的泛型接得对不对,最直接的方法就是对照 packages/react/src/hooks 下的既有实现逐项比对。仓库中还提供了 packages/register-tests 这类跨框架类型注册测试,可作为类型安全的参考基准。
测试策略:参照 React 的 Hook 测试
如果你在构建库,测试必不可少。Wagmi 官方用React Testing Library测试 Hooks(参见 packages/react/src/hooks 下的*.test.ts/*.test-d.ts文件),而 Testing Library 本身也支持 Svelte、Solid 等多个框架。
可借鉴的测试模式
- 行为测试(
.test.ts):渲染一个包着WagmiProvider的测试组件,断言 Hook 返回值随链切换、连接/断开账户而变化;仓库中的测试基建可参考 packages/react/test/setup.ts 与 packages/core/test/setup.ts。 - 类型测试(
.test-d.ts):用类型断言验证泛型推断是否正确,比如 useChainId.test-d.ts 这类文件,专门守护库的公共类型契约。
测试时重点关注:链切换后订阅是否触发、连接器变更后useConnection是否更新、组件卸载后订阅是否清理。这些行为正是响应式层正确性的直接证据。
代理导出:简化使用者的导入路径
Wagmi 会直接代理(re-export)Wagmi Core 与 Viem 的导出,让使用者可以从一个包名里拿到所有常用能力,避免从多个包手动拼装导入。这个行为非常值得你在自己的框架包里模仿。
从源码看,packages/react/src/exports 下的index.ts、actions.ts、hooks.ts等文件集中完成 re-export;packages/core/src/exports 中也有actions.ts、query.ts、internal.ts等细分导出面(例如internal.ts导出ConfigParameter、QueryParameter等供框架层使用的内部类型)。仓库中的 scripts/generateConnectorExports.ts 与 scripts/generateProxyPackages.ts 脚本,进一步说明 wagmi 是用脚本自动生成这些代理导出的,以保持各包导出面的同步与一致。
给框架作者的建议:为你的包设计好导出面划分(如index、actions、hooks/composables/primitives、query),把 Core 和 Viem 的常用符号一并代理出去,能显著降低使用者的学习与接入成本。
结语
把 Wagmi Core 接入新框架并不需要重写任何链上逻辑——Core 本身是框架无关的。你要做的,是用框架的依赖注入机制传递 Config,用watch*系列订阅 API 把状态变化接进框架的响应式系统,再通过'@wagmi/core/query'复用 TanStack Query 的缓存与去重能力,最后用 Testing Library 和代理导出把体验打磨完整。
仓库中 packages/react、packages/vue、packages/solid 三个官方框架包,就是最好的"参考答案":以 useConnection 为连接原语范本、useChainId 为订阅范本、useConnectorClient 为 TanStack Query 集成范本,你的框架适配工作就有了完整的实现蓝图。
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考