createSharedComposable源码解读

在 Vue 的开发中,组合式函数(Composables)是实现逻辑复用的核心工具。然而,有时我们需要在多个组件或实例之间共享同一个组合式函数的状态和副作用,而不希望每次调用都创建新的实例。VueUse 提供的 createSharedComposable 正是为此设计的工具。本文将深入剖析其源码,探索它的实现细节和应用价值。

一、功能概述

createSharedComposable 是一个高阶函数,它将普通的组合式函数转化为一个"共享"的组合式函数。它的核心特性是:

  • 在多个调用者之间共享同一份状态和副作用。
  • 自动管理副作用的作用域(Effect Scope),并在不再需要时清理资源。

使用示例

javascript 复制代码
import { createSharedComposable } from '@vueuse/core'
import { ref } from 'vue'

// 定义一个普通的组合式函数
function useCounter() {
  const count = ref(0)
  const increment = () => count.value++
  return { count, increment }
}

// 创建共享版本
const useSharedCounter = createSharedComposable(useCounter)

// 在多个组件中使用
const { count, increment } = useSharedCounter()

无论在多少个组件中调用 useSharedCounter,它们共享同一个 count 实例。

二、源码结构与参数

函数签名

typescript 复制代码
export function createSharedComposable<Fn extends AnyFn>(composable: Fn): Fn
  • composable: 一个普通的组合式函数,类型为 AnyFn(任意函数)。
  • 返回值: 一个新的函数,类型与 composable 一致,但具有共享特性。

三、核心实现解析

1. 状态管理

typescript 复制代码
let subscribers = 0
let state: ReturnType<Fn> | undefined
let scope: EffectScope | undefined
  • subscribers: 记录当前使用该共享组合式函数的订阅者数量。
  • state: 存储 composable 的返回值,即共享的状态。
  • scope: 一个 EffectScope 实例,用于管理副作用的作用域。

这些变量定义在函数闭包中,确保在多次调用共享函数时保持单一实例。

2. 清理逻辑 dispose

typescript 复制代码
const dispose = () => {
  subscribers -= 1
  if (scope && subscribers <= 0) {
    scope.stop()
    state = undefined
    scope = undefined
  }
}
  • 每次调用者取消订阅时,subscribers 减 1。
  • 当订阅者数量降至 0 时:
    • 调用 scope.stop() 停止副作用作用域。
    • 清空 statescope,释放资源。

3. 主逻辑

typescript 复制代码
return <Fn>((...args) => {
  subscribers += 1
  if (!scope) {
    scope = effectScope(true)
    state = scope.run(() => composable(...args))
  }
  tryOnScopeDispose(dispose)
  return state
})
  • 订阅计数 : 每次调用时,subscribers 加 1。
  • 初始化 : 如果 scope 不存在(即首次调用):
    • 创建一个新的 EffectScope(通过 effectScope(true),表示独立作用域)。
    • 在该作用域内运行 composable,将其返回值赋值给 state
  • 自动清理 : 使用 tryOnScopeDispose 在当前作用域销毁时调用 dispose,确保资源管理。
  • 返回值 : 返回共享的 state,而不是每次重新计算。

四、工作原理

闭包与单例

createSharedComposable 利用闭包特性,将 subscribersstatescope 保存在函数外部作用域中。这意味着无论调用多少次返回的函数,它们共享同一份数据和副作用。

EffectScope 的作用

Vue 3 提供了 effectScope API,用于管理一组副作用(如 refcomputedwatch)的生命周期。createSharedComposable 使用它来:

  • 确保 composable 中的响应式副作用(如 watch)被绑定到一个独立的作用域。
  • 在所有订阅者取消时,通过 scope.stop() 一次性清理所有副作用。

资源管理

通过 tryOnScopeDisposedispose 被绑定到调用者的生命周期。当组件销毁时,自动减少订阅计数,并在必要时清理资源。

五、使用场景与优势

场景

  1. 全局状态管理

    在不需要复杂状态管理库(如 Pinia 或 Vuex)时,共享简单的全局状态。

    javascript 复制代码
    const useSharedModal = createSharedComposable(() => {
      const isOpen = ref(false)
      const toggle = () => (isOpen.value = !isOpen.value)
      return { isOpen, toggle }
    })
  2. 跨组件事件监听

    共享一个事件监听器,避免重复绑定。

    javascript 复制代码
    const useSharedClick = createSharedComposable(() => {
      const clicks = ref(0)
      window.addEventListener('click', () => clicks.value++)
      return { clicks }
    })

优势

  • 性能优化: 避免重复创建状态和副作用。
  • 简洁性: 无需引入额外的状态管理库。
  • 生命周期管理: 自动清理资源,防止内存泄漏。

六、注意事项

  1. 作用域依赖

    如果 composable 内部依赖外部的响应式数据,确保这些数据是全局可访问的,否则可能导致意外行为。

  2. 线程安全

    该实现不考虑并发问题,在大多数单线程 JavaScript 环境中是安全的,但在特殊场景下(如 SSR)需谨慎使用。

  3. 调试复杂性

    由于状态是共享的,调试时可能需要额外注意状态来源。

七、总结

createSharedComposable 是 VueUse 中一个优雅的工具,它通过闭包和 EffectScope 实现了组合式函数的共享与生命周期管理。它的设计既简单又强大,特别适合需要轻量级全局状态管理的场景。通过这篇源码解读,希望你能更深入理解它的原理,并在项目中灵活运用!

相关推荐
SuperherRo6 小时前
Web开发-JS应用&微信小程序&源码架构&编译预览&逆向调试&嵌套资产&代码审计
前端·javascript·微信小程序·源码·逆向
大专哥6 小时前
基于vite官方开源脚手架预设,实现一个 npm create template-vue3-ts-preset(2):分析入口文件
webpack·开源·源码
ak啊12 小时前
Webpack 构建阶段:模块解析流程
前端·webpack·源码
布多13 小时前
AutoreleasePool:iOS 内存管理乐章中的隐秘旋律
ios·源码阅读
luoluoal13 小时前
java项目之基于ssm的医院门诊挂号系统(源码+文档)
java·mysql·mybatis·ssm·源码
好_快16 小时前
Lodash源码阅读-baseIsEqualDeep
前端·javascript·源码阅读
好_快16 小时前
Lodash源码阅读-assignValue
前端·javascript·源码阅读
好_快16 小时前
Lodash源码阅读-copyArray
前端·javascript·源码阅读
好_快16 小时前
Lodash源码阅读-initCloneObject
前端·javascript·源码阅读
好_快16 小时前
Lodash源码阅读-baseCreate
前端·javascript·源码阅读