前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库Vue effectScope detached 分离作用域 内存泄漏
VuVue侦听与生命周期

Vue effectScope(true) 为什么容易泄漏,什么时候才该使用?

分离作用域不是“更强的作用域”,而是主动放弃上级替你收尾的关系。

前端进阶之旅 · 一题精讲更新于 2026.09.06
Vue#侦听与生命周期
先看核心答案读代码示例
理解线索

分离后需要重新回答所有权

  1. 父级停止只处理自己收集的效果与默认嵌套作用域
  2. 分离作用域继续活动直到自身 stop 被调用
  3. 共享服务用应用实例、引用计数或显式关闭界定寿命

只把 scope 变量导出并不等于所有权清楚,还要规定谁在什么事件中调用 stop。

核心回答

先记住这个答案

effectScope(true) 创建 detached 作用域,它不会被创建时的当前父作用域收集,因此父 scope 或所属组件结束时不会自动停止它。它适合生命周期确实独立于单个消费者的共享响应式服务,但必须有明确的 owner 和 stop 时机。若只是组件内部效果,使用分离作用域会让监听在组件离开后继续存在;模块级单例在 SSR 中还可能跨请求共享状态,应按应用实例或请求隔离。

  • detached 不参与父作用域级联停止
  • 独立生命周期必须配套明确 owner
  • SSR 中避免跨请求共享可变单例

true 参数切断父子回收关系

默认在活动 scope 内创建的子作用域会成为它的后代。传 true 后,新 scope 不登记到父级;这使它能够在局部调用者结束后继续提供共享状态,也意味着上级无法替它完成清理。

如果每次组件挂载都创建一个 detached scope,却从不保存或停止,离开页面后效果仍可能持有依赖和外部资源。重复进入页面时观察次数不断增长,是这类所有权遗漏的典型现象。

用可观察记录验证独立存活

示例在父作用域的 run 中同时创建默认子作用域和分离子作用域。停止父级后修改源,默认子作用域不再记录,分离子作用域仍然记录,直到单独调用 detached.stop。

这种行为是 API 设计目的,不能把它本身叫作框架泄漏。只有应用失去停止路径、效果寿命超出业务需求时才构成资源问题;评审重点应放在 owner 和终止事件是否具体。

父级停止不会结束 detached 子作用域TypeScript
import { effectScope, ref, watchEffect } from 'vue'

const source = ref(0)
const normal: number[] = []
const separate: number[] = []
const parent = effectScope()
let detached!: ReturnType<typeof effectScope>
parent.run(() => {
  effectScope().run(() => watchEffect(() => normal.push(source.value)))
  detached = effectScope(true)
  detached.run(() => watchEffect(() => separate.push(source.value)))
})

父级停止并把 source 改为一后,normal 保持初值零,separate 会出现一。单独停止 detached 再改为二,两边都不再增长,从而精确展示分离关系。

共享不等于进程全局

浏览器单页应用可以按 app 实例创建共享服务,在最后一个消费者释放或应用卸载时停止。引用计数必须处理重复释放与异常路径,避免计数降为负数或某个组件失败后永久占用。

服务端渲染中,模块顶层的可变单例可能同时服务多个请求。涉及用户数据、语言或权限时,应为每次应用创建独立容器并通过上下文传递,不能把 detached 当成跨请求缓存的默认方案。

回答前,多想一步

容易答错的地方

以为组件卸载总会停止所有 scope
组件只能停止归属自身作用域树的效果。detached 明确切断这条树关系,除非业务另有停止路径,否则组件卸载不能替它决定共享服务何时结束。
为了避免重复初始化就永不销毁
共享实例可以延迟初始化并复用,但仍应定义登出、应用关闭、最后消费者离开或测试结束时的释放事件。永久存在只是一个需证明合理的生命周期决策。
试着用自己的话回答

面试官还会怎么问?

普通子 scope 可以手动提前 stop 吗?

可以。子级能够在父级结束前独立停止;之后父级 stop 不应让已结束效果重新运行。提前结束适合功能局部关闭,同时父级仍负责其余效果。

detached scope 适合全局鼠标位置吗?

可以作为一种实现,但要让多个消费者共享同一监听、在最后消费者离开时移除全局事件,并处理 SSR 没有 window 的环境。是否分离取决于服务寿命,而非数据看起来是否全局。

测试中为什么要主动 stop?

独立作用域可能跨用例保留观察和状态,使后续断言受前一用例影响。每个测试结束时停止并重置共享容器,能够验证真实释放路径,也保证测试隔离。

从一道题,走向一组知识

把知识连起来

侦听与生命周期

如何用 Vue effectScope 实现跨组件共享且可回收的副作用?

共享单例需要把 detached 生命周期落实为引用计数或显式关闭。

侦听与生命周期

Vue 嵌套 effectScope 在父级停止时会怎样?

默认嵌套的级联行为是理解 detached 例外的对照。

侦听与生命周期

Vue 异步创建的 watch 为什么可能泄漏,如何正确销毁?

两种问题都要求在自动组件归属之外补上明确停止路径。

参考资料

  • Vue:effectScope API
  • Vue RFC:Detached Scopes

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. true 参数切断父子回收关系
  3. 用可观察记录验证独立存活
  4. 共享不等于进程全局
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑