Open jinwangchina opened 7 years ago
There isn't a standard way to do this. We need to add one and we'd certainly welcome your contribution. As a work around, there's a semi-private property named root
that is the root view. You can call detached
and then unbind
on that to clean things up.
Thanks EisenbergEffect, it works.
Also check this link about this subject.
Can we make root public? If there was a method .reset() or .destroy() Would it be enough to call .detached() and .unbind() on root? From what I learned playing with .enhance(), Aurelia stores the Host after the first call to .enhance(). There is also a boolean hostInitialized or something. So if I'm enhancing an iframe's document, the consecutive enhance will not work, so I have to clear the host and the safeguard. At this point I'm concerned about memory leaks. Is it enough of a clean up? Or there are some other structures we need to tear down?
I'm thinking of a .reset() method that resets Aurelia up until after all the configuration was done. And a .destroy() method which should remove everything.
I'm thinking about something like aurelia-testing for ehnancing brown field apps. just .enhance is not enough. Got to be able to enhance iframe on load, tear down on iframe navigate. And utilize mutation observer for inseparability with legacy dom manipulations
@EisenbergEffect
Hello. This would be a great addition and I guess it shouldn't be hard to implement for some core contributor.
The use case is a micro-frontend application that should be created and destroyed multiple times according to conditions (URL match or any DOM event, etc.).
I have tried what you proposed (calling detached & unbind
), it invoked lifecycles hooks but didn't remove DOM nodes, the code looks like this:
aurelia.root.view.removeNodes();
aurelia.root.detached();
aurelia.root.unbind();
Calling removeNodes()
did the trick and it removes DOM nodes. But other resources are not disposed, this can be seen when making heap snapshot.
I don't know what is the correct approach of doing this since there is no information regarding that. Can this feature be implemented? Or can any code be shared of how to do this?
Thanks in advance.
@arturovt can you help point out what's not disposed from your snapshot?
@bigopon
I've got such simple code. This is index.ejs
:
<body>
<app></app>
<button class="mount">Mount</button>
<button class="unmount">Unmount</button>
</body>
And this is main.ts
:
import { bootstrap } from 'aurelia-bootstrapper';
let ref: Aurelia;
function mount() {
bootstrap(async (aurelia: Aurelia) => {
aurelia.use.standardConfiguration().developmentLogging();
ref = await (await aurelia.start()).setRoot(
PLATFORM.moduleName('app'),
document.querySelector('app')
);
});
}
function unmount() {
const root = ref['root'];
root.view.removeNodes();
root.detached();
root.unbind();
ref = null;
}
document.querySelector('.mount').addEventListener('click', mount);
document.querySelector('.unmount').addEventListener('click', unmount);
I actually could create a minimal reproducible example and push it to some GitHub repo, but I'm sure you understand what's going on here w/o a doubt.
It works. I mean if I click mount
button then it will render an application and if I click unmount
it will call lifecycle hooks and unrender app.
The problem is that if I click mount
multiple times then it seems like that Aurelia keeps configuring plugins but doesn't dispose previous ones. For instance, ConsoleAppender
keeps logging multiple times:
And if I open a Chrome snapshot then there are 4 ConsoleAppender
instances and they keep creating but don't get GCed.
Any ideas of how to do this in the right way?
Thanks for the detailed explanation. Currently we lack a teardown API in v1. I'd imagine such api works by providing a hook during configuration so that plugins can register their corresponding dispose functions:
import { FrameworkConfiguration } from 'aurelia-framework';
export function configure(config: FrameworkConfiguration) {
config.use....
config.disposeTask(() => {
// do dispose work here
});
}
and new API on Aurelia
:
aurelia.stop();
Or, we can do it much simpler way, as the only thing that mis behave at the moment is the console appender, since it's quite a static API. Which means:
let hasAppender = false;
...
...
aurelia.use.standardConfiguration();
if (!hasAppender) {
aurelia.use.developmentLogging();
}
cc @fkleuver @EisenbergEffect We have this in v2 already, though pinging you just in case & awareness
@bigopon thank you very much, I will try it today after the job and will give feedback.
Btw aurelia.stop()
seems like to be undocumented, I just googled it and searched through Aurelia API docs. That would be very helpful to expose it as a public API and add it to docs.
it's a new API, to be added if we decide to go with a new API. The API name comes from v2. In v2, it's accounted for this very scenario, where you could start/stop multiple times repeatedly, confidentially, thanks to @fkleuver
@arturovt Thanks for your comment! It helped me with creating a teardown/unmount feature for this package.
I ended up using the Container
class to fetch the Aurelia
instance outside of the configure()
method while maintaining a clean scope.
import { Aurelia } from 'aurelia-framework';
import { Container } from 'aurelia-dependency-injection';
const aurelia = Container.instance.get(Aurelia)
I manually deleted the root tag "
" by manipulating DOM directly, but found no detached() called in my custom-element.I wonder if there is a way to destroy aurelia manually? for example, a api like aurelia.destroy() which will trigger detached() to be called in all components.