Nested & dynamic custom element composition

We have a 3.2M LOC application (107 top-level routes, dynamic composition only within each of those routes) that we’re migrating from Aurelia 1.0 to 2.0 (the application actually began back in the days of Durandal, but it was much smaller then). As the first step in the journey, I used npx makes aurelia to scaffold a default Aurelia application (the first option).

I’m encountering issues that are discussed in this topic by others (minus the dynamic composition aspect). To synopsize, use of the @customElement decorator prevents the leaking of memory from one route to the next, but sacrifices nested custom elements. Omitting @customElement and simply running with conventions as discussed by @huochunpeng brings back nested custom elements, but causes the leaking of memory.

I can both cause and resolve these issues at will by going back and forth between @customElement on each component, and commenting them out. This is true without even using au-compose.

I’m using the nightly build (“dev”) of Aurelia, but the problem exists even in the “latest” build.

From main.js (configured for direct routing):

import Aurelia from 'aurelia';
import { MyApp } from './my-app';
import { RouterConfiguration } from '@aurelia/router';

Aurelia.register(RouterConfiguration.customize({ useUrlFragmentHash: false }))
    .app(MyApp)
    .start();

From my-app.html:

<template>
    <import from="./t1-shell"></import>

    <t1-shell></t1-shell>
</template>

From t1-shell.html:

<template>
    <import from="./some-component"></import>
    <import from="./another-component"></import>

    <a load="/some-component">Some Component</a>
    <a load="/another-component">Another Component</a>

    <au-viewport></au-viewport>
</template>

There is nothing significant about another-component.html, but some-component.html sports a nested custom element, like so:

<template>
    <import from="./nested-component"></import>

    <div class="some-component">We're in some component!</div>
    <nested-component></nested-component>
</template>

SCENARIO 1: Using @customElement on each component:

Decorator @customElement present

You can see above when I navigate to the some-component route, nested-component does not show up. Note the heap snapshots:

Heap snapshots with @customElement present

Off to the left (offscreen), I’m going to each route, and then making sure the previous route doesn’t show up in the snapshot.

So, then, with the decorator present, we don’t leak. Consider the next scenario.

SCENARIO 2: Commenting out @customElement

Decorator commented out (conventional approach)

You can see above the nested custom element when I navigate to some-component. However, take a look at the heap snapshots:

Heap snapshots (no decorator)

So, then, without the decorator, the nested component is present, but we leak from route to route.

(Just to clarify, when I typed in the component name to check the heap, I was typing in the component I navigated away from).

CONCLUSION

This seems like a bug to me (or perhaps I’m missing something). The same is true if I use dynamic composition. But I thought that for the sake of this post, I would keep it simple.