vueuse 源码: createEventHook

317 阅读3分钟

VueUse 基于 Vue 组合式 API 的实用函数库,提供了一组高效且易用的组合式 API,用于简化 Vue 3 开发中的常见需求。

本文我们来看下 createEventHook 这个函数的实现 猛击访问文档 主要函数仅不到 30 行,它是一个用于创建事件钩子的函数,单看这段话其实很难理解它是用来干什么的!

先来看一下官方的示例

逻辑封装

import { createEventHook } from '@vueuse/core'

export function useMyFetch(url) {
  const fetchResult = createEventHook<Response>()
  const fetchError = createEventHook<any>()

  fetch(url)
    .then(result => fetchResult.trigger(result))
    .catch(error => fetchError.trigger(error.message))

  return {
    onResult: fetchResult.on,
    onError: fetchError.on,
  }
}

使用

<script setup lang="ts">
import { useMyFetch } from './my-fetch-function'

const { onResult, onError } = useMyFetch('my api url')

onResult((result) => {
  console.log(result)
})

onError((error) => {
  console.error(error)
})
</script>

这段逻辑是对请求做了一层封装返回 onResult、onError 函数,当请求成功触发 onResult 、失败时触发 onError 函数,相当于是执行了对应的回调函数

当然你会说直接返回 Promise 不就行了吗?这只是一个 🌰 告诉你它能做什么!

即可以将原有需要在组合式函数传入的回调函数直接暴露出去!

看下源码

export function createEventHook<T = any>(): EventHook<T> {
  // 存储注册的的方法
  const fns: Set<Callback<T>> = new Set()

  // 移除注册的方法
  const off = (fn: Callback<T>) => {
    fns.delete(fn)
  }

  // 注册方法
  const on = (fn: Callback<T>) => {
    fns.add(fn)
    const offFn = () => off(fn)

    tryOnScopeDispose(offFn)

    return {
      off: offFn,
    }
  }

  // 触发所有注册的方法
  const trigger: EventHookTrigger<T> = (...args) => {
    return Promise.all(Array.from(fns).map(fn => fn(...(args as [T, ...unknown[]]))))
  }

  return {
    on,
    off,
    trigger,
  }
}

首先看到返回了三个函数 on(注册方法)、off(移除注册的方法)、trigger(触发所有注册的方法)

存储注册的的方法

const fns: Set<Callback<T>> = new Set() 使用 Set 来存储绑定的方法

Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用 猛击直达 mdn

delete 移除 Set 中与这个值相等的元素,返回一个布尔值。

addSet 对象尾部添加一个元素。返回该 Set 对象。

image.png

移除注册的方法

方法是通过 Set 存储的,需要传入绑定的函数进行删除

const off = (fn: Callback<T>) => { 
    fns.delete(fn) 
}

注册方法

查看 on 函数的实现,可以理解为何使用 Set 来存储绑定的方法。on 函数允许绑定多个方法,而 Set 的特性能够确保同一个方法即使多次绑定,也只会触发一次。此外,on 函数返回一个 off 方法,用于移除已注册的方法。

大家可能注意到 on 函数里调用了一个 tryOnScopeDispose 他是用来干什么的呢?

先来看下它的实现

import type { Fn } from '../utils'
import { getCurrentScope, onScopeDispose } from 'vue-demi'

/**
 * Call onScopeDispose() if it's inside an effect scope lifecycle, if not, do nothing
 *
 * @param fn
 */
export function tryOnScopeDispose(fn: Fn) {
  if (getCurrentScope()) {
    onScopeDispose(fn)
    return true
  }
  return false
}

看起来是根据 getCurrentScope 的值决定是否执行 onScopeDispose 让我们看下官方文档对这两个方法的解释!

image.png

到这里 tryOnScopeDispose 的作用也就显而易见了 在当前的 Vue 响应式作用域中注册一个清理函数。如果不在作用域内,则什么也不做。

on 方法里调用是传递了 offFn 函数,所以在 on 方法里调用 tryOnScopeDispose 的作用是:在当前作用域卸载时移除注册的方法!

触发所有注册的方法

trigger 方法会触发所有已绑定的方法,使用 Array.fromSet 类型的数据转换成数组然后使用 Promise.all 执行所有方法。

总结

本文讲解了 vueuse 中 createEventHook、tryOnScopeDispose 两个工具函数的作用以及实现!

其中 createEventHook 虽然只有不到 30 行代码但实现思路仍值得我们学习!