I notice many web-based libs/APIs focus on creation without reciprocal destruction mechanisms. They assume that cleanup happens when the "web page" is closed.
A long running, wanting-to-be-robust, web application might like to unload elements if it knows they are not being used on the page any more (f.e. it switched to another client-side route). It may like to lose references to classes, and can fetch the relevant JS (cached) later.
Would it make sense to be able to unregister elements, to free up memory?
It may. What we'd want to see before committing to this sort of work is memory profiles of popular custom element-using sites, showing that a large portion of their memory usage comes from no-longer-used custom element definitions. Are you interested in collecting such evidence?
I'm interested, but not sure how to find the sites. They should have many client-side routes that load route-specific JS which includes new elements for the route. At some point my own site will do that.
In my experience, with one of the heaviest components ecosystems that I know of, the component's registry is never the bottle neck, not even close. On top of that, this seems like a lot of work for so little reward. What if the component itself kicks in some services that will eventually attempt to create instances of itself? Who is in charge?
Closing, happy to reopen once there's a clearer demonstration this can be useful.
The fact that custom elements canāt be redefined essentially makes them unusable within Observableās reactive notebook environment. Our runtime is predicated on being able to re-evaluate cells whenever either the code in a cell changes, or a value it references changes. So, you canāt really use custom elements within Observable, because every time you change the code, you canāt cleanup the old definition before you re-evaluate the cell.
Redefining custom elements is also useful for:
@annevk Please reopen. It's silly to have an irreversible process.
I think https://github.com/w3c/webcomponents/issues/754#issuecomment-403100007 clearly states what is needed, no?
@annevk I think that comment is less relevant now, given the later comments.
Wouldn't most of use cases here be addressed by https://github.com/w3c/webcomponents/issues/716?
@annevk Look at mbostock and stevenvachon's comments. Our concern isn't about freeing up memory, it's about the ability to replace a custom element after it's already been defined.
And think realistically about what actually happens on a website. Many websites use vendor Javascript and sometimes vendors do things that collide with the main functionality of the page. In enterprises, a developer may not have a choice to use a different vendor as it is decided by the business. Having the ability to work around misguided code is invaluable for Javascript.
@rniwa Scoped custom elements are a partial solution, but still not when testing the default registry.
@rniwa Scoped custom elements are a partial solution, but still not when testing the default registry.
What you'd do in that situation is to override what window.customElements does.
@rniwa then my custom element would be atonomous.
What would happen to an object representing a custom element instance which has become āundefinedā? It presumably has the prototype associated with the custom element and likely has local state via own properties, private fields, or WM privacy. These things canāt be removed after the fact (the object may not even be extensible). Even if the object representing the node could be replaced somehow in the DOM, references to the original object may exist in closures. This doesnāt seem coherent to me.
In addition to defining & collecting compelling use cases, here are some of additional issues / edge cases that need to be sorted out off the top of my head:
I'm sure there is a lot more cases to consider that I haven't even thought about.
What would happen to an object representing a custom element instance which has become āundefinedā?
How about not changing anything for these existing instances, in the same way that if you call document.createElement("my-element") before customElements.define, you still have a generic HTMLElement after customElements.define, and customElements.define only affects newly created instances. (Apologies if this is a bad idea; Iām not especially familiar with the specification and Iām just trying to be help!)
I think youāre on the right track that it would need to behave something like that @mbostock. I.E. whatās being disassociated in the registry is āthis tag name points at this definitionā, like setting the _name_ and _local name_ parts of the definition to null. I think that would imply in turn that new OriginalConstructor would need to throw.
I think this would create the possible scenario of multiple elements with the same tag name occurring with two or more non-unknown meanings in the DOM. I donāt think that can currently happen with autonomous elements and am unsure what implications it might have.
I think that would imply in turn that new OriginalConstructor would need to throw.
That's a given; there totally needs to be behavior like that. š
This downgrade process may be complicated, but ability to clean up memory usage is a critical part of designing any API that creates memory usage.
Even if we (any one of us) haven't seen a use case where memory growth from defining new elements is a specific problem, just the act of designing any re-usable and repetitive API with irreversible memory growth is simply bad practice that should be avoided.
"Downgrading" elements has intricacies to think about, but it's probably something important to think about for the longevity of web-tech beyond simple short-running applications.
Hypothetical Scenario 1, Users with many windows and tabs:
Imagine everyone drops React/Angular/Vue for plain Web Components. Now imagine a user that has 30 Chrome windows open, with let's say an average of 10 tabs per window (I've seen worse than this in practice).
Now imagine that after loading custom elements in many tabs, and switching routes (etc), on average each web app in every tab uses 300 custom elements over the app life time, and on average only needs 20 custom elements at any moment.
Without disposing any custom elements, and with current state of APIs (no cleanup of definitions), this means we'll load 90,000 Custom Element definitions.
Now, if there was a way to clean things up, this number would be reduced to 6,000 loaded at any point. That's a lot better, and could improve user experience when switching tabs often, especially on mobile devices.
Hypothetical Scenario 2: Web-based OS
A web-based OS that loads "micro apps" by loading Custom Elements in a shared DOM (not the best idea, but still). Over time, memory growth from using many apps will grow and never shrink.
Hypothetical Scenario 3: low-power low-cost embedded devices
We need to squeeze things into small amounts of memory. Easy to imagine problems here...
Hypothetical Scenario 4: Long running game
Like Half-life, this game might be an infinite world game, and we'd need to unload the components as we leave various areas of the game. All the cool kids like efficient game engines and micro-optimizations that eventually add up.
Yeah, let's make FPS's with HTML!
Hypothetical Scenario 4.5: "Infinite-world" game
Similar to the previous idea, in an infinite world game where users can travel to many places and cover large amounts distances with changing scenery, landscapes, characters, and items, you can imagine how leaking every definition of every scene, landscape, character, and item could be a problem.
Yeah, let's make infinite-world games with HTML!
Hypothetical Scenario 5: Testing
It becomes is simpler to write tests when there's no naming conflicts.
Hypothetical Scenario 6: Live code editors
Replacing elements with new implementation on code changes. If we do something like add an incrementing number to the end of an element name, then the user CSS won't work unless you painstakingly write a system for that too, because selectors with tag names will break. And then it won't be "just CSS" anymore.
Hypothetical Scenario 7: Live code pushes/patches or HMR
Imagine an in-app purchase system upgrades some objects with 3rd-party extensions from an extension store. The objects reload (maybe they are covered with a loading icon for aesthetics), and now the objects have the new features.
Another use-case is with using micro-frontend architecture. We have a custom-elements library for visual components that is reused through many SPA. The library is integrated at build-time in the SPA, so each SPA can use a different version of the components. At runtime, the first spa that will load the custom-elements will define them and after that every other spa will use the custom-elements first registered even if they are incompatible.
In this context, I would like to be able to unregister all custom-elements when the micro-frontend switch from one SPA to another. It would be useful for :
@ptrot001 I think Scoped CustomElementRegistry is going to be a better solution there. Once a registry and all associated shadow roots are unreachable, a browser should be able to clean up definitions mostly non-observably (modulo any potential restrictions on classes being definable in at most one registry at a time).
Most helpful comment
That's a given; there totally needs to be behavior like that. š
This downgrade process may be complicated, but ability to clean up memory usage is a critical part of designing any API that creates memory usage.
Even if we (any one of us) haven't seen a use case where memory growth from defining new elements is a specific problem, just the act of designing any re-usable and repetitive API with irreversible memory growth is simply bad practice that should be avoided.
"Downgrading" elements has intricacies to think about, but it's probably something important to think about for the longevity of web-tech beyond simple short-running applications.
Hypothetical Scenario 1, Users with many windows and tabs:
Imagine everyone drops React/Angular/Vue for plain Web Components. Now imagine a user that has 30 Chrome windows open, with let's say an average of 10 tabs per window (I've seen worse than this in practice).
Now imagine that after loading custom elements in many tabs, and switching routes (etc), on average each web app in every tab uses 300 custom elements over the app life time, and on average only needs 20 custom elements at any moment.
Without disposing any custom elements, and with current state of APIs (no cleanup of definitions), this means we'll load 90,000 Custom Element definitions.
Now, if there was a way to clean things up, this number would be reduced to 6,000 loaded at any point. That's a lot better, and could improve user experience when switching tabs often, especially on mobile devices.
Hypothetical Scenario 2: Web-based OS
A web-based OS that loads "micro apps" by loading Custom Elements in a shared DOM (not the best idea, but still). Over time, memory growth from using many apps will grow and never shrink.
Hypothetical Scenario 3: low-power low-cost embedded devices
We need to squeeze things into small amounts of memory. Easy to imagine problems here...
Hypothetical Scenario 4: Long running game
Like Half-life, this game might be an infinite world game, and we'd need to unload the components as we leave various areas of the game. All the cool kids like efficient game engines and micro-optimizations that eventually add up.
Yeah, let's make FPS's with HTML!
Hypothetical Scenario 4.5: "Infinite-world" game
Similar to the previous idea, in an infinite world game where users can travel to many places and cover large amounts distances with changing scenery, landscapes, characters, and items, you can imagine how leaking every definition of every scene, landscape, character, and item could be a problem.
Yeah, let's make infinite-world games with HTML!
Hypothetical Scenario 5: Testing
It becomes is simpler to write tests when there's no naming conflicts.
Hypothetical Scenario 6: Live code editors
Replacing elements with new implementation on code changes. If we do something like add an incrementing number to the end of an element name, then the user CSS won't work unless you painstakingly write a system for that too, because selectors with tag names will break. And then it won't be "just CSS" anymore.
Hypothetical Scenario 7: Live code pushes/patches or HMR
Imagine an in-app purchase system upgrades some objects with 3rd-party extensions from an extension store. The objects reload (maybe they are covered with a loading icon for aesthetics), and now the objects have the new features.