mirror of https://github.com/vuejs/core.git
fix(reactivity): fix memory leak from dep instances of garbage collected objects
close #11979 close #11971
This commit is contained in:
parent
5c8b76ed6c
commit
235ea4772e
|
@ -82,6 +82,13 @@ export class Dep {
|
||||||
*/
|
*/
|
||||||
subsHead?: Link
|
subsHead?: Link
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For object property deps cleanup
|
||||||
|
*/
|
||||||
|
target?: unknown = undefined
|
||||||
|
map?: KeyToDepMap = undefined
|
||||||
|
key?: unknown = undefined
|
||||||
|
|
||||||
constructor(public computed?: ComputedRefImpl | undefined) {
|
constructor(public computed?: ComputedRefImpl | undefined) {
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
this.subsHead = undefined
|
this.subsHead = undefined
|
||||||
|
@ -218,7 +225,8 @@ function addSub(link: Link) {
|
||||||
// which maintains a Set of subscribers, but we simply store them as
|
// which maintains a Set of subscribers, but we simply store them as
|
||||||
// raw Maps to reduce memory overhead.
|
// raw Maps to reduce memory overhead.
|
||||||
type KeyToDepMap = Map<any, Dep>
|
type KeyToDepMap = Map<any, Dep>
|
||||||
const targetMap = new WeakMap<object, KeyToDepMap>()
|
|
||||||
|
export const targetMap: WeakMap<object, KeyToDepMap> = new WeakMap()
|
||||||
|
|
||||||
export const ITERATE_KEY: unique symbol = Symbol(
|
export const ITERATE_KEY: unique symbol = Symbol(
|
||||||
__DEV__ ? 'Object iterate' : '',
|
__DEV__ ? 'Object iterate' : '',
|
||||||
|
@ -249,6 +257,9 @@ export function track(target: object, type: TrackOpTypes, key: unknown): void {
|
||||||
let dep = depsMap.get(key)
|
let dep = depsMap.get(key)
|
||||||
if (!dep) {
|
if (!dep) {
|
||||||
depsMap.set(key, (dep = new Dep()))
|
depsMap.set(key, (dep = new Dep()))
|
||||||
|
dep.target = target
|
||||||
|
dep.map = depsMap
|
||||||
|
dep.key = key
|
||||||
}
|
}
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
dep.track({
|
dep.track({
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { extend, hasChanged } from '@vue/shared'
|
import { extend, hasChanged } from '@vue/shared'
|
||||||
import type { ComputedRefImpl } from './computed'
|
import type { ComputedRefImpl } from './computed'
|
||||||
import type { TrackOpTypes, TriggerOpTypes } from './constants'
|
import type { TrackOpTypes, TriggerOpTypes } from './constants'
|
||||||
import { type Link, globalVersion } from './dep'
|
import { type Link, globalVersion, targetMap } from './dep'
|
||||||
import { activeEffectScope } from './effectScope'
|
import { activeEffectScope } from './effectScope'
|
||||||
import { warn } from './warning'
|
import { warn } from './warning'
|
||||||
|
|
||||||
|
@ -418,14 +418,20 @@ function removeSub(link: Link) {
|
||||||
dep.subsHead = nextSub
|
dep.subsHead = nextSub
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dep.subs && dep.computed) {
|
if (!dep.subs) {
|
||||||
// last subscriber removed
|
// last subscriber removed
|
||||||
|
if (dep.computed) {
|
||||||
// if computed, unsubscribe it from all its deps so this computed and its
|
// if computed, unsubscribe it from all its deps so this computed and its
|
||||||
// value can be GCed
|
// value can be GCed
|
||||||
dep.computed.flags &= ~EffectFlags.TRACKING
|
dep.computed.flags &= ~EffectFlags.TRACKING
|
||||||
for (let l = dep.computed.deps; l; l = l.nextDep) {
|
for (let l = dep.computed.deps; l; l = l.nextDep) {
|
||||||
removeSub(l)
|
removeSub(l)
|
||||||
}
|
}
|
||||||
|
} else if (dep.map) {
|
||||||
|
// property dep, remove it from the owner depsMap
|
||||||
|
dep.map.delete(dep.key)
|
||||||
|
if (!dep.map.size) targetMap.delete(dep.target!)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue