From d9d63f21b1e6f99f2fb63d736501095b131e5ad9 Mon Sep 17 00:00:00 2001 From: Evan You Date: Mon, 17 Feb 2020 23:14:07 -0500 Subject: [PATCH] fix(reactivity): avoid cross-component dependency leaks in setup() --- packages/reactivity/src/effect.ts | 12 +++++++++++- packages/reactivity/src/index.ts | 5 ++++- packages/runtime-core/src/apiLifecycle.ts | 4 ++-- packages/runtime-core/src/component.ts | 9 ++++++++- packages/runtime-core/src/warning.ts | 4 ++-- 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 46969d3c33c..bffcc8024bc 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -95,11 +95,13 @@ function run(effect: ReactiveEffect, fn: Function, args: unknown[]): unknown { if (!effectStack.includes(effect)) { cleanup(effect) try { + enableTracking() effectStack.push(effect) activeEffect = effect return fn(...args) } finally { effectStack.pop() + resetTracking() activeEffect = effectStack[effectStack.length - 1] } } @@ -116,15 +118,23 @@ function cleanup(effect: ReactiveEffect) { } let shouldTrack = true +const trackStack: boolean[] = [] export function pauseTracking() { + trackStack.push(shouldTrack) shouldTrack = false } -export function resumeTracking() { +export function enableTracking() { + trackStack.push(shouldTrack) shouldTrack = true } +export function resetTracking() { + const last = trackStack.pop() + shouldTrack = last === undefined ? true : last +} + export function track(target: object, type: TrackOpTypes, key: unknown) { if (!shouldTrack || activeEffect === undefined) { return diff --git a/packages/reactivity/src/index.ts b/packages/reactivity/src/index.ts index 80e9e622703..040dd8c673d 100644 --- a/packages/reactivity/src/index.ts +++ b/packages/reactivity/src/index.ts @@ -21,8 +21,11 @@ export { export { effect, stop, + trigger, + track, + enableTracking, pauseTracking, - resumeTracking, + resetTracking, ITERATE_KEY, ReactiveEffect, ReactiveEffectOptions, diff --git a/packages/runtime-core/src/apiLifecycle.ts b/packages/runtime-core/src/apiLifecycle.ts index 8733ecd2bde..4ee86b60c21 100644 --- a/packages/runtime-core/src/apiLifecycle.ts +++ b/packages/runtime-core/src/apiLifecycle.ts @@ -9,7 +9,7 @@ import { ComponentPublicInstance } from './componentProxy' import { callWithAsyncErrorHandling, ErrorTypeStrings } from './errorHandling' import { warn } from './warning' import { capitalize } from '@vue/shared' -import { pauseTracking, resumeTracking, DebuggerEvent } from '@vue/reactivity' +import { pauseTracking, resetTracking, DebuggerEvent } from '@vue/reactivity' export { onActivated, onDeactivated } from './components/KeepAlive' @@ -39,7 +39,7 @@ export function injectHook( setCurrentInstance(target) const res = callWithAsyncErrorHandling(hook, target, type, args) setCurrentInstance(null) - resumeTracking() + resetTracking() return res }) if (prepend) { diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index d90249a9823..dc7d3a24c56 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -1,5 +1,10 @@ import { VNode, VNodeChild, isVNode } from './vnode' -import { ReactiveEffect, shallowReadonly } from '@vue/reactivity' +import { + ReactiveEffect, + shallowReadonly, + pauseTracking, + resetTracking +} from '@vue/reactivity' import { PublicInstanceProxyHandlers, ComponentPublicInstance, @@ -341,12 +346,14 @@ function setupStatefulComponent( currentInstance = instance currentSuspense = parentSuspense + pauseTracking() const setupResult = callWithErrorHandling( setup, instance, ErrorCodes.SETUP_FUNCTION, [propsProxy, setupContext] ) + resetTracking() currentInstance = null currentSuspense = null diff --git a/packages/runtime-core/src/warning.ts b/packages/runtime-core/src/warning.ts index 083bda553e0..ef6168d242b 100644 --- a/packages/runtime-core/src/warning.ts +++ b/packages/runtime-core/src/warning.ts @@ -1,7 +1,7 @@ import { VNode } from './vnode' import { Data, ComponentInternalInstance, Component } from './component' import { isString, isFunction } from '@vue/shared' -import { toRaw, isRef, pauseTracking, resumeTracking } from '@vue/reactivity' +import { toRaw, isRef, pauseTracking, resetTracking } from '@vue/reactivity' import { callWithErrorHandling, ErrorCodes } from './errorHandling' type ComponentVNode = VNode & { @@ -60,7 +60,7 @@ export function warn(msg: string, ...args: any[]) { console.warn(...warnArgs) } - resumeTracking() + resetTracking() } function getComponentTrace(): ComponentTraceStack {