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
相关产品推荐
相关产品推荐

