fix(reactivity): avoid cross-component dependency leaks in setup()

This commit is contained in:
Evan You 2020-02-17 23:14:07 -05:00
parent 202532c301
commit d9d63f21b1
5 changed files with 27 additions and 7 deletions

View File

@ -95,11 +95,13 @@ function run(effect: ReactiveEffect, fn: Function, args: unknown[]): unknown {
if (!effectStack.includes(effect)) { if (!effectStack.includes(effect)) {
cleanup(effect) cleanup(effect)
try { try {
enableTracking()
effectStack.push(effect) effectStack.push(effect)
activeEffect = effect activeEffect = effect
return fn(...args) return fn(...args)
} finally { } finally {
effectStack.pop() effectStack.pop()
resetTracking()
activeEffect = effectStack[effectStack.length - 1] activeEffect = effectStack[effectStack.length - 1]
} }
} }
@ -116,15 +118,23 @@ function cleanup(effect: ReactiveEffect) {
} }
let shouldTrack = true let shouldTrack = true
const trackStack: boolean[] = []
export function pauseTracking() { export function pauseTracking() {
trackStack.push(shouldTrack)
shouldTrack = false shouldTrack = false
} }
export function resumeTracking() { export function enableTracking() {
trackStack.push(shouldTrack)
shouldTrack = true shouldTrack = true
} }
export function resetTracking() {
const last = trackStack.pop()
shouldTrack = last === undefined ? true : last
}
export function track(target: object, type: TrackOpTypes, key: unknown) { export function track(target: object, type: TrackOpTypes, key: unknown) {
if (!shouldTrack || activeEffect === undefined) { if (!shouldTrack || activeEffect === undefined) {
return return

View File

@ -21,8 +21,11 @@ export {
export { export {
effect, effect,
stop, stop,
trigger,
track,
enableTracking,
pauseTracking, pauseTracking,
resumeTracking, resetTracking,
ITERATE_KEY, ITERATE_KEY,
ReactiveEffect, ReactiveEffect,
ReactiveEffectOptions, ReactiveEffectOptions,

View File

@ -9,7 +9,7 @@ import { ComponentPublicInstance } from './componentProxy'
import { callWithAsyncErrorHandling, ErrorTypeStrings } from './errorHandling' import { callWithAsyncErrorHandling, ErrorTypeStrings } from './errorHandling'
import { warn } from './warning' import { warn } from './warning'
import { capitalize } from '@vue/shared' 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' export { onActivated, onDeactivated } from './components/KeepAlive'
@ -39,7 +39,7 @@ export function injectHook(
setCurrentInstance(target) setCurrentInstance(target)
const res = callWithAsyncErrorHandling(hook, target, type, args) const res = callWithAsyncErrorHandling(hook, target, type, args)
setCurrentInstance(null) setCurrentInstance(null)
resumeTracking() resetTracking()
return res return res
}) })
if (prepend) { if (prepend) {

View File

@ -1,5 +1,10 @@
import { VNode, VNodeChild, isVNode } from './vnode' import { VNode, VNodeChild, isVNode } from './vnode'
import { ReactiveEffect, shallowReadonly } from '@vue/reactivity' import {
ReactiveEffect,
shallowReadonly,
pauseTracking,
resetTracking
} from '@vue/reactivity'
import { import {
PublicInstanceProxyHandlers, PublicInstanceProxyHandlers,
ComponentPublicInstance, ComponentPublicInstance,
@ -341,12 +346,14 @@ function setupStatefulComponent(
currentInstance = instance currentInstance = instance
currentSuspense = parentSuspense currentSuspense = parentSuspense
pauseTracking()
const setupResult = callWithErrorHandling( const setupResult = callWithErrorHandling(
setup, setup,
instance, instance,
ErrorCodes.SETUP_FUNCTION, ErrorCodes.SETUP_FUNCTION,
[propsProxy, setupContext] [propsProxy, setupContext]
) )
resetTracking()
currentInstance = null currentInstance = null
currentSuspense = null currentSuspense = null

View File

@ -1,7 +1,7 @@
import { VNode } from './vnode' import { VNode } from './vnode'
import { Data, ComponentInternalInstance, Component } from './component' import { Data, ComponentInternalInstance, Component } from './component'
import { isString, isFunction } from '@vue/shared' 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' import { callWithErrorHandling, ErrorCodes } from './errorHandling'
type ComponentVNode = VNode & { type ComponentVNode = VNode & {
@ -60,7 +60,7 @@ export function warn(msg: string, ...args: any[]) {
console.warn(...warnArgs) console.warn(...warnArgs)
} }
resumeTracking() resetTracking()
} }
function getComponentTrace(): ComponentTraceStack { function getComponentTrace(): ComponentTraceStack {