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 创建了 count 和 increment 变量,它们是在多个组件之间共享的。
源码分析
源码如下:
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 实例都可以获得最新的状态。