Wagmi Vue 与 Viem 深度集成指南:自定义 Composable、Viem Actions 与私钥账户实战
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
Viem 是面向 Ethereum 的底层 TypeScript 接口库,而Wagmi Vue的所有核心 Composables 本质上都是对 Viem 的多链与连接器感知封装。本文以 site/vue/guides/viem.md 为主线,结合本仓库源码,讲解如何直接调用 Viem Actions 打造自定义 Query/Mutation Composable,以及如何在 Wagmi 生态中安全使用私钥与助记词账户,读完即可在现有 Wagmi Vue 项目中自由组合 Viem 的全部底层能力。
Viem 与 Wagmi Vue 的关系
Viem 是一个低层级的 TypeScript Ethereum 接口库,为开发者提供与 Ethereum 区块链交互所需的全部底层能力,包括:
- JSON-RPC API 抽象:屏蔽底层 RPC 细节,提供类型安全的调用封装;
- 智能合约交互:合约调用、事件监听、ABI 编解码等;
- 钱包与签名实现:私钥账户、助记词账户、HD 钱包等;
- 编码/解析工具:地址、哈希、十六进制数据、EIP-712 签名数据等编解码工具。
Wagmi Core本质上是 Viem 之上的一层封装,它通过 Wagmi Config 提供多链能力,并通过 Connectors 提供自动账户管理。这一架构在源码中体现得非常清晰:以getClient为例,packages/core/src/actions/getClient.ts 内部直接调用config.getClient(parameters),将链 ID 解析与 transport 选择交给 Config 处理,最终返回一个完整的 ViemClient实例。
利用 Viem Actions:两种操作类别
Wagmi Vue 的核心 Composables(完整列表见 site/vue/api/composables.md)全部是 Viem Actions 的友好封装——它们自动注入多链感知、连接器感知的 Wagmi Config。
但在某些场景下,你可能需要更深入地直接使用 Viem Actions,例如:
- Wagmi 中尚不存在某个对应的 Composable;
- 你需要对底层行为做更精细的控制;
- 你想基于 Viem Actions 构建团队内部抽象。
此时,你可以直接从viem/actions导入 Viem Actions,并把 useClient /useConnectorClient返回的 Viem Client 作为参数传入,构建自己的自定义 Composable。
Viem Actions 分为两类:
| 类别 | 说明 | 是否需要钱包 |
|---|---|---|
| Public Actions | 只读操作,如查询区块、读取合约状态、获取日志 | 否 |
| Wallet Actions | 与钱包交互的操作,如发送交易、签名消息、添加资产 | 是 |
虽然并非强制,但官方强烈建议将这两类 Actions 与useQuery或useMutation搭配使用,以充分利用 TanStack Query 的响应式与缓存能力——这正是 Wagmi 自身所有读写 Composables 的实现范式。
Public Actions:构建自定义useLogsComposable
以下示例演示如何用 Viem 的getLogsAction 配合useQuery,创建类似useLogs的自定义抽象:
<script setup lang="ts"> // 1. 导入模块。 import { useClient, useConnectorClient } from '@wagmi/vue' import { useMutation, useQuery } from '@wagmi/vue/query' import { getLogs, watchAsset } from 'viem/actions' // 2. 获取当前激活链对应的 Viem Client。 const client = useClient() // 3. 创建一个利用 Client 的 "自定义" Query Composable。 const { data: logs } = useQuery( computed(() => ({ queryKey: ['logs', client.value.uid], queryFn: () => getLogs(client.value) })) ) </script>这里有几个值得注意的底层细节:
useClient返回的是Ref类型(见 packages/vue/src/composables/useClient.ts),其实现通过useConfig拿到注入的 Config,先调用getClient(config, params)同步获取 Client,再通过watchClient订阅切换链、连接器等事件,实现 Client 的自动更新与响应式。这正是 Vue 场景下“无需手动刷新 Client”的原因。queryKey中使用了client.value.uid。每个 Viem Client 都有唯一uid,把它纳入 Query Key 可以确保切换链后自动失效并重新查询,避免拿到旧链的日志数据。这也符合 TanStack Query 的确定性标识设计(详见 packages/vue/src/utils/query.ts,Wagmi 的useQuery封装统一注入了queryKeyHashFn)。- 参数通过
computed(...)传入useQuery,让client.value的变化能驱动 Query 选项的响应式更新。
Wallet Actions:构建自定义useWatchAssetComposable
以下示例演示如何用 Viem 的watchAssetAction 配合useMutation,创建类似useWatchAsset的自定义抽象:
<script setup lang="ts"> // 1. 导入模块。 import { useConnectorClient } from '@wagmi/vue' import { useMutation } from '@wagmi/vue/query' import { watchAsset } from 'viem/actions' // 2. 获取当前激活链对应的 Viem Client。 const { data: connectorClient } = useConnectorClient() // 3. 创建一个利用 Client 的 "自定义" Mutation Composable。 const { mutate } = useMutation({ mutationFn: (asset) => watchAsset(connectorClient, asset) }) </script>注意这里使用的是useConnectorClient而非useClient。二者的区别是:
useClient:返回纯 Public Client,对应核心层getClient(packages/core/src/actions/getClient.ts),不包含钱包账户能力,适合只读 Public Actions;useConnectorClient:通过getConnectorClientQueryOptions解析当前连接器对应的 Client(见 packages/vue/src/composables/useConnectorClient.ts),它绑定了连接器的账户与签名器,因此watchAsset、sendTransaction这类需要钱包授权的 Wallet Actions 必须使用它。
从源码看,useConnectorClient本身就是一个基于useQuery的封装,且会监听连接地址变化:当账户断开时移除对应查询缓存,当地址变化时使缓存失效(见 packages/vue/src/composables/useConnectorClient.ts),确保 Client 始终与当前账户一致。
提示:在 Vue 组件中执行
mutate(asset)时,asset参数会被 TanStack Query 的类型系统推断为watchAsset所需的{ type, options }结构(如{ type: 'ERC20', options: { address, decimals, symbol } }),可直接获得完整的类型提示。
关于@wagmi/vue/query导入路径
示例中useQuery/useMutation来自@wagmi/vue/query,而不仅仅是 TanStack 的@tanstack/vue-query。从 packages/vue/src/exports/query.ts 可以看到,@wagmi/vue/query会完整导出@wagmi/core/query的全部内容(包括各类*MutationOptions工厂函数),并重新导出类型增强后的useMutation/useQuery(实现见 packages/vue/src/utils/query.ts)。因此在你需要构建自定义读写 Composable 时,应优先从@wagmi/vue/query导入,以保持与 Wagmi 内置 Composables 一致的响应式行为和 Query Key 哈希逻辑。
Private Key 与 Mnemonic 账户
Wagmi 支持直接使用 Viem 的 Private Key 与 Mnemonic Accounts(本地账户),方法是通过 Wagmi Actions 的account参数显式传入账户。典型场景包括服务端脚本、自动化任务,或是不希望依赖浏览器钱包扩展的测试环境。
以下示例演示如何使用privateKeyToAccount配合sendTransaction:
<script setup lang="ts"> import { privateKeyToAccount } from 'viem/accounts' import { useConfig } from '@wagmi/vue' import { sendTransactionMutationOptions, useMutation } from '@wagmi/vue/query' const config = useConfig() const { mutate: sendTransaction } = useMutation( sendTransactionMutationOptions(config) ) const account = privateKeyToAccount('0x...') // [!code hl] sendTransaction({ account, // [!code hl] to: '0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC', value: parseEther('0.001') }) </script>关键点拆解:
privateKeyToAccount来自viem/accounts,返回一个符合 ViemAccount类型的本地账户对象(支持私钥派生地址与签名能力);- 通过
account参数把账户显式传给 Wagmi Action,绕过连接器的自动账户管理; sendTransactionMutationOptions(config)是核心层导出的 Mutation 选项工厂函数(packages/core/src/query/sendTransaction.ts),它把mutationFn绑定到核心sendTransaction(config, variables),并设置mutationKey: ['sendTransaction']。仓库中的测试(packages/core/src/query/sendTransaction.test.ts)验证了该工厂函数默认返回mutationFn与mutationKey的结构;- 事实上,内置的 useSendTransaction 也正是通过
sendTransactionMutationOptions(config, parameters)构建选项后交给 TanStack 的useMutation(见 packages/vue/src/composables/useSendTransaction.ts)。因此手动调用该工厂函数与使用内置 Composable 保持了完全一致的语义。
重要限制:账户不能提升到 Config 顶层
Wagmi 目前不支持将 Private Key 与 Mnemonic Accounts 提升到 Wagmi Config 顶层——这意味着你必须在每一个 Action上显式传入account。若希望未来支持该特性,可在 Wagmi 的 Discussions 中发起功能提案。
安全提醒:私钥是账户的最终控制权凭证。在生产环境中切勿将私钥硬编码在前端代码或提交到版本库,建议通过环境变量、密钥管理服务等方式注入,并仅用于服务端等可信环境。
实践建议:何时自定义,何时使用内置
综合本仓库的架构(packages/vue/src/composables 下已有 90+ 个内置 Composables),给出如下选型建议:
| 场景 | 推荐方案 |
|---|---|
| Wagmi 已有对应 Composable(读链、合约、钱包操作等) | 直接使用内置 Composable,获得开箱即用的响应式与缓存 |
| Wagmi 尚无对应封装,且为只读操作 | 用useClient+viem/actions的 Public Action +useQuery自建 |
| Wagmi 尚无对应封装,且需钱包授权 | 用useConnectorClient+viem/actions的 Wallet Action +useMutation自建 |
| 服务端/自动化场景,需本地签名 | 用privateKeyToAccount/mnemonicToAccount+ 显式account参数 |
自定义 Composable 时,记得把链相关的标识(如client.value.uid、chainId)纳入 Query Key,并优先从@wagmi/vue/query导入useQuery/useMutation,这样你的自定义抽象才能与 Wagmi 内置能力在缓存、失效、预取等行为上保持一致,真正做到“写一次,处处复用”。
【免费下载链接】wagmiReactive primitives for Ethereum apps项目地址: https://gitcode.com/GitHub_Trending/wa/wagmi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考