-
-
Notifications
You must be signed in to change notification settings - Fork 33.8k
Description
Version
2.3.4
Reproduction link
https://jsfiddle.net/bartsidee/n1tvtwet/
Steps to reproduce
We identified that when a vnod is patched/replaced in the DOM e.g. after a vue-router route update the new vnode is marked with a DOM element reference (_refElm) of the previous vnode (in our case the previous route) causing a detached dom tree (memory leak)
Steps to reproduce:
- open attached jsfiddle
- open chrome dev tools -> memory tab
- select the 'Home' route
- make a heap snapshot
- select the 'Foo' route
- make a heap snapshot
- select the last snapshot and use the 'summary' tab
- in the search box enter 'detached'
- find the detached tree that has a retainer to '_refElm'
- if you hover the red HTMLDivElement you will notice it is the dom element of the 'Home' component
See below code reference in vuejs:
- "patch" method is triggered which trigger "patchVnode" to replace existing root component
Line 612 in 4733408
patchVnode(oldVnode, vnode, insertedVnodeQueue, removeOnly) - both root components have children, which triggers "updateChildren":
Line 484 in 4733408
if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly) - there are no transitions so a new element is created, triggering "createElm":
Line 409 in 4733408
createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm) - important is that "createElm" here is passed a DOM element reference "oldStartVnode.elm" of the old vnode (previous route) as 4th argument
- this in turn is link that the old dom element to the new vnode as "_refElm" property creating the detached binding causing the leak
Line 109 in 4733408
function createElm (vnode, insertedVnodeQueue, parentElm, refElm, nested) {
Line 84 in 12b7122
opts._refElm = options._refElm
What is expected?
we would expect that VueJS would be conservative of memory usage and prevent detached dom nodes
What is actually happening?
when patching the vdom and replacing the root node, e.g. using the vue-router we see that the dom element of the previous route is indirectly linked to the new route causing a detached dom tree.
The leak has a limited impact for normal devices as the reference is only to the last component that is patched, but can have a large impact on for example embedded devices as it prevents to browser to garbage collect to last route increasing the overall memory needed to run the app.
So to be clear if we have 3 components: component 1, component 2, component 3 and component 1 is patched with component 2, vue will keep a detached dom of component 1. If component 2 is patched with component 3, component 2 will also be detached, but component 1 is in this case cleared because only the 'elm' reference transitions from component 2 to component 3.