VueUse 学习 —— createSharedComposable

1,386 阅读3分钟

createSharedComposable 是一个可以用于将一个组合函数(composable)转换为可共享的函数的 Vue 3 工具函数。它的作用是使多个组件可以共享同一个实例。

使用 createSharedComposable 创建的函数可以在多个 Vue 组件之间共享数据,即使这些组件没有共同的父组件也能共享数据。其实现方式是使用了 Vue 3 提供的 provide/inject API。

下面是 createSharedComposable 函数的使用示例:

import { createSharedComposable, ref } from 'vueuse'

const useCounter = createSharedComposable(() => {
  const count = ref(0)
  const increment = () => {
    count.value++
  }
  return {
    count,
    increment,
  }
})

useCounter 函数可以在多个组件之间共享,示例如下:

import { defineComponent } from 'vue'
import { useCounter } from './useCounter'

export default defineComponent({
  setup() {
    const { count, increment } = useCounter()

    return {
      count,
      increment,
    }
  },
})

在上面的组件中,使用 useCounter 创建了 countincrement 变量,它们是在多个组件之间共享的。

源码分析

源码如下:

import type { EffectScope } from 'vue-demi'
import { effectScope } from 'vue-demi'
import { tryOnScopeDispose } from '../tryOnScopeDispose'
import type { AnyFn } from '../utils'

/**
 * Make a composable function usable with multiple Vue instances.
 *
 * @see https://vueuse.org/createSharedComposable
 */
export function createSharedComposable<Fn extends AnyFn>(composable: Fn): Fn {
  let subscribers = 0
  let state: ReturnType<Fn> | undefined
  let scope: EffectScope | undefined

  const dispose = () => {
    subscribers -= 1
    if (scope && subscribers <= 0) {
      scope.stop()
      state = undefined
      scope = undefined
    }
  }

  return <Fn>((...args) => {
    subscribers += 1
    if (!state) {
      scope = effectScope(true)
      state = scope.run(() => composable(...args))
    }
    tryOnScopeDispose(dispose)
    return state
  })
}

tryOnScopeDispose 方法源码如下:

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

/**
 * 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
}

这段代码定义了一个 tryOnScopeDispose 函数,用于在 Vue 3 的 effectScope 生命周期中调用 onScopeDispose 函数。

函数接收一个参数 fn,它是一个函数类型(Fn),表示要执行的回调函数。该函数的作用是将 fn 注册为当前 effectScope 的销毁回调函数,以便在该 effectScope 销毁时调用。

tryOnScopeDispose 函数首先通过 getCurrentScope() 获取当前的 effectScope,如果存在,则通过 onScopeDispose(fn)fn 注册为销毁回调函数,然后返回 true。如果不存在,则返回 false,表示没有注册销毁回调函数。

通过这个函数,可以方便地将一个回调函数注册为当前 effectScope 的销毁回调函数,以便在该 effectScope 销毁时自动调用。这样就可以避免手动管理回调函数的销毁,提高了代码的可维护性和可读性。

再看看 createSharedComposable 的源码:

这段代码实现了一个函数 createSharedComposable,用于将一个 composable 函数转换为可以在多个 Vue 实例中共享的形式。该函数的实现过程如下:

首先定义了一些变量:

  • subscribers 用于记录当前使用该 composable 函数的 Vue 实例数;
  • state 用于记录该 composable 函数的返回值;
  • scope 用于记录该 composable 函数使用的 effect scope。

然后返回一个函数,该函数在每次被调用时会进行以下操作:

  • subscribers 的值 +1,表示有一个新的 Vue 实例在使用该 composable 函数;
  • 如果 state 还未被初始化,则创建一个新的 effect scope,并在其中运行原始的 composable 函数,将返回值赋给 state
  • 在每次调用时,都会调用 tryOnScopeDispose 函数并将 dispose 函数作为参数传入。tryOnScopeDispose 函数会判断当前是否处于 effect scope 生命周期中,如果是,则将 dispose 函数传入 onScopeDispose 中以便在 effect scope 结束时被调用;如果不是,则什么也不做;
  • 最后返回 state

当所有使用该函数的 Vue 实例都停止使用时,由于 subscribers 的值为 0,所以会停止 effect scope 并将 state 重置为 undefined,下次再有 Vue 实例使用该函数时,会重新创建 effect scope 并重新运行 composable 函数,以此保证每个使用该函数的 Vue 实例都可以获得最新的状态。