From 0af4bf85fc88d1761336aac2314da2022f7be3e8 Mon Sep 17 00:00:00 2001 From: Matt Lavallee Date: Tue, 4 Sep 2018 12:42:02 -0400 Subject: [PATCH 1/3] fix(component): clean up memory leak after loading async component completes --- src/core/vdom/helpers/resolve-async-component.js | 8 ++++++++ test/unit/features/transition/transition-mode.spec.js | 2 +- test/unit/modules/vdom/create-component.spec.js | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/core/vdom/helpers/resolve-async-component.js b/src/core/vdom/helpers/resolve-async-component.js index 99ce5ce47ed..f9632707baf 100644 --- a/src/core/vdom/helpers/resolve-async-component.js +++ b/src/core/vdom/helpers/resolve-async-component.js @@ -64,6 +64,14 @@ export function resolveAsyncComponent ( const forceRender = () => { for (let i = 0, l = contexts.length; i < l; i++) { contexts[i].$forceUpdate() + + const currContext = contexts[i] + const contextIdx = contexts.indexOf(currContext) + if (contextIdx >= 0) { + currContext.$nextTick(() => { + contexts.splice(contextIdx, 1) + }) + } } } diff --git a/test/unit/features/transition/transition-mode.spec.js b/test/unit/features/transition/transition-mode.spec.js index 6e4f5be06ca..f8afca82f5e 100644 --- a/test/unit/features/transition/transition-mode.spec.js +++ b/test/unit/features/transition/transition-mode.spec.js @@ -457,7 +457,7 @@ if (!isIE9) { setTimeout(() => { resolve({ template: '

component B

' }) Vue.nextTick(next) - }, (duration + buffer) * 1.5) + }, (duration + buffer) * 1.7) } }, data: { diff --git a/test/unit/modules/vdom/create-component.spec.js b/test/unit/modules/vdom/create-component.spec.js index db0c59e3746..e69fca0eff3 100644 --- a/test/unit/modules/vdom/create-component.spec.js +++ b/test/unit/modules/vdom/create-component.spec.js @@ -58,6 +58,7 @@ describe('create-component', () => { vnode = createComponent(async, data, vm, vm) expect(vnode.isComment).toBe(true) // not to be loaded yet. expect(vnode.asyncFactory).toBe(async) + expect(vnode.asyncFactory.contexts.length).toEqual(1) } function loaded () { vnode = createComponent(async, data, vm, vm) @@ -68,6 +69,7 @@ describe('create-component', () => { expect(vnode.elm).toBeUndefined() expect(vnode.ns).toBeUndefined() expect(vnode.context).toEqual(vm) + expect(vnode.asyncFactory.contexts.length).toEqual(0) expect(vm.$forceUpdate).toHaveBeenCalled() done() } From 0c57049fa2b935675933b6d9036eec984a180a22 Mon Sep 17 00:00:00 2001 From: Matt Lavallee Date: Tue, 4 Sep 2018 13:11:26 -0400 Subject: [PATCH 2/3] fix(async component): accounting for async components with loading property --- .../vdom/helpers/resolve-async-component.js | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/core/vdom/helpers/resolve-async-component.js b/src/core/vdom/helpers/resolve-async-component.js index f9632707baf..fd4d7f525a5 100644 --- a/src/core/vdom/helpers/resolve-async-component.js +++ b/src/core/vdom/helpers/resolve-async-component.js @@ -61,17 +61,18 @@ export function resolveAsyncComponent ( const contexts = factory.contexts = [context] let sync = true - const forceRender = () => { + const forceRender = (renderCompleted: boolean) => { for (let i = 0, l = contexts.length; i < l; i++) { contexts[i].$forceUpdate() - const currContext = contexts[i] - const contextIdx = contexts.indexOf(currContext) - if (contextIdx >= 0) { - currContext.$nextTick(() => { - contexts.splice(contextIdx, 1) - }) + if (!renderCompleted) { + continue } + + const contextIdx = i + contexts[i].$nextTick(() => { + contexts.splice(contextIdx, 1) + }) } } @@ -81,7 +82,7 @@ export function resolveAsyncComponent ( // invoke callbacks only if this is not a synchronous resolve // (async resolves are shimmed as synchronous during SSR) if (!sync) { - forceRender() + forceRender(true) } }) @@ -92,7 +93,7 @@ export function resolveAsyncComponent ( ) if (isDef(factory.errorComp)) { factory.error = true - forceRender() + forceRender(true) } }) @@ -119,7 +120,7 @@ export function resolveAsyncComponent ( setTimeout(() => { if (isUndef(factory.resolved) && isUndef(factory.error)) { factory.loading = true - forceRender() + forceRender(false) } }, res.delay || 200) } From b7d0b804eacbffc9948511d960ac0cf09c486ab8 Mon Sep 17 00:00:00 2001 From: Matt Lavallee Date: Wed, 5 Sep 2018 14:49:38 -0400 Subject: [PATCH 3/3] refactor(component): simplifying memory cleanup logic --- src/core/vdom/helpers/resolve-async-component.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/core/vdom/helpers/resolve-async-component.js b/src/core/vdom/helpers/resolve-async-component.js index fd4d7f525a5..44ca238854f 100644 --- a/src/core/vdom/helpers/resolve-async-component.js +++ b/src/core/vdom/helpers/resolve-async-component.js @@ -64,15 +64,10 @@ export function resolveAsyncComponent ( const forceRender = (renderCompleted: boolean) => { for (let i = 0, l = contexts.length; i < l; i++) { contexts[i].$forceUpdate() + } - if (!renderCompleted) { - continue - } - - const contextIdx = i - contexts[i].$nextTick(() => { - contexts.splice(contextIdx, 1) - }) + if (renderCompleted) { + contexts.length = 0 } }