diff --git a/CHANGELOG.md b/CHANGELOG.md index 20ce987..a2c4c3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 1.4.3 + +- fix: memory leak caused by unused `@Setup` decorator ## 1.4.2 - fix: Type failure diff --git a/src/context.ts b/src/context.ts index 0cef07b..0717f06 100644 --- a/src/context.ts +++ b/src/context.ts @@ -1,5 +1,5 @@ import { getCurrentInstance, type VueInstance, isVue3 } from './vue'; -import { setupReference } from './setup-reference'; +import { bindTarget } from './setup-reference'; import { TargetName, Target } from './types'; import { SETUP_NAME, @@ -170,7 +170,7 @@ export class Context { const vm = getCurrentInstance(); this.$vm = vm ?? ({ $props: {}, $emit: emit } as any); this.$emit = this.$vm.$emit.bind(this.$vm) as E; - setupReference.add(this); + bindTarget(this); } public get $props() { return (this.$vm.$props ?? {}) as T; diff --git a/src/setup-reference.ts b/src/setup-reference.ts index 03a671f..977a64f 100644 --- a/src/setup-reference.ts +++ b/src/setup-reference.ts @@ -1,25 +1,39 @@ -class SetupReference { - private _count = 0; - public map = new Map(); - public count() { - this._count++; - } - public add(target: object) { - this.map.set(target, this._count); - this._count = 0; + +let count = 0; +let isOpen = false; + +export function add () { + if (isOpen) { + count = 1; + } else { + isOpen = true; + count++; } - public reduce(target: object) { - const { map } = this; - let count = map.get(target)!; +} +const weakMap = new WeakMap(); + +export function popTarget(target: object): boolean { + let count = weakMap.get(target); + if (typeof count === 'number') { count--; if (count) { - map.set(target, count); + weakMap.set(target, count) return false; + } else { + weakMap.delete(target); + isOpen = false + return true; } - map.delete(target); - return true; } + return false } -export const setupReference = new SetupReference(); +export function bindTarget(target: object) { + if (count > 0) { + weakMap.set(target, count); + count = 0; + } else { + console.warn(`The instance did not use the '@Setup' decorator`) + } +} diff --git a/src/setup.ts b/src/setup.ts index a91bdd1..b85c2ac 100644 --- a/src/setup.ts +++ b/src/setup.ts @@ -10,7 +10,7 @@ import { import { initComputed } from './computed'; import { getOptions, getSetupOptions } from './options'; import { initDefine } from './define'; -import { setupReference } from './setup-reference'; +import { add, popTarget } from './setup-reference'; import { getPropertyDescriptors } from './property-descriptors'; export type TargetConstructor = { @@ -68,9 +68,9 @@ function Setup(Target: T) { public static [SETUP_PROPERTY_DESCRIPTOR] = getPropertyDescriptors(Target); public constructor(...args: any[]) { - setupReference.count(); + add(); super(...args); - if (setupReference.reduce(this)) { + if (popTarget(this)) { // Vue3 needs to return, vue2 does not need to return return initHook(reactive(this)); }