You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3异步Web3实例事件监听及相关技术问题咨询

Vue3 Web3 组合式函数优化与事件监听问题解答

问题1:watch更新ref是否合适?有没有更高效的实现?

首先你的原代码存在逻辑错误:watch里写了if (isReady) return null,这会导致web3实例准备好时反而跳过chainId赋值,完全搞反了判断逻辑,正确逻辑应该是isReady为true时才执行chainId获取操作。

单说用watch监听isReady的方式本身是可行的,但并非最优解。更高效简洁的方式是直接在useAsyncState的异步回调中处理:拿到web3实例后,同步获取chainId并赋值,无需额外添加watch监听:

const useWallet = () => {
  const chainId = ref(null)

  const web3 = useAsyncState(async () => {
    const instance = await fooWeb3Instance();
    // 获取实例后直接同步获取chainId
    chainId.value = await instance.eth.getChainId();
    return instance;
  })

  return {
    web3,
    chainId
  }
}

如果后续有其他依赖isReady的逻辑,必须用watch的话,修正后的代码应该是:

watch(web3.isReady, async (isReady) => {
  if (isReady && web3.state.value) {
    chainId.value = await web3.state.value.eth.getChainId();
  }
})

这种方式和React监听状态变化执行副作用的思路一致,但直接在异步回调中处理能减少额外的监听开销,逻辑更紧凑。

问题2:事件监听应该加在什么位置?

对应React的useEffect,Vue3里可以用watch配合清理函数或watchEffect实现,核心是在web3实例可用时添加监听,且在实例变更、组件卸载时移除监听,避免内存泄漏。

实现方式1:watch + 清理函数

const useWallet = () => {
  const chainId = ref(null)

  const web3 = useAsyncState(async () => {
    const instance = await fooWeb3Instance();
    chainId.value = await instance.eth.getChainId();
    return instance;
  })

  // chainId变化的回调
  const handleChainChange = (nextChainId) => {
    chainId.value = nextChainId;
  }

  // 监听web3实例变化,添加/移除事件监听
  watch(
    () => web3.state.value,
    (newInstance, oldInstance) => {
      // 移除旧实例的监听
      if (oldInstance?.currentProvider) {
        oldInstance.currentProvider.removeListener('chainId', handleChainChange);
      }
      // 给新实例添加监听
      if (newInstance?.currentProvider) {
        newInstance.currentProvider.on('chainId', handleChainChange);
      }
    },
    { immediate: true } // 初始化时立即执行
  )

  // 组件卸载时清理监听
  onUnmounted(() => {
    if (web3.state.value?.currentProvider) {
      web3.state.value.currentProvider.removeListener('chainId', handleChainChange);
    }
  })

  return {
    web3,
    chainId
  }
}

实现方式2:watchEffect(更接近React useEffect逻辑)

const useWallet = () => {
  const chainId = ref(null)

  const web3 = useAsyncState(async () => {
    const instance = await fooWeb3Instance();
    chainId.value = await instance.eth.getChainId();
    return instance;
  })

  const handleChainChange = (nextChainId) => {
    chainId.value = nextChainId;
  }

  // 自动追踪依赖,依赖变化时自动清理旧监听
  watchEffect((onInvalidate) => {
    const instance = web3.state.value;
    if (!instance?.currentProvider) return;

    instance.currentProvider.on('chainId', handleChainChange);
    
    // 清理函数:依赖变化或组件卸载时执行
    onInvalidate(() => {
      instance.currentProvider.removeListener('chainId', handleChainChange);
    })
  })

  return {
    web3,
    chainId
  }
}

关键注意点:

  • 必须移除旧实例的监听,否则会导致重复监听、内存泄漏
  • 单独提取回调函数handleChainChange,确保移除监听时引用的是同一个函数实例
  • 组件卸载时要清理监听,避免残留事件绑定

内容的提问来源于stack exchange,提问作者Sartre

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 00:52:43