Component Model: Decorator Challenges
Back to Component Model.
The design goal for decorators can summarized as follows: allow runtime (dynamic) application and unapplication of presentation (and possibly behavior, associated with the presentation) to a DOM element, regardless of the current state of the element, in a way that’s performant (both in memory and time space), and intuitive for today’s Web developers.
The problem with decorators is not their application, but the unapplication.
If the decorator maintains state, this requirement of unapplication creates a difficulty of understanding when the state matters, when it no longer does, and even whether it’s the same state/instance as the one you left just a moment ago.
For example, running script inside of the decorator forces the decorator’s author to follow rules of engagement that differ from those in typical script development -- in non-obvious ways:
- registering an event listener with any DOM node outside of the decorator produces undesirable effect of a listener sticking around even after the decorator is unapplied.
- using setInterval/setTimeout/XHR, or anything that exits event loop produces an expectation of code running after the decorator is unapplied.
- even just storing state inside of a decorator is challenging; in situations where decorator is unapplied/reapplied quickly (like matching a hover pseudoclass), the state is lost without any warning.
Even if we attempt to separate the state of a decorator from the element and its document using an iframe- or worker-like machinery, we’ll still have similar issues of managing decorator instances that are no longer relevant, but don’t know that yet -- in addition to the performance costs of such high-isolation approach.
One way to remove some state uncertainty could be to pool all decorators instances and reuse them on reapplication, rather than creating new instances. Unfortunately, this means that the decorators may never be reclaimed, which impacts performance characteristics.
We could explicitly prevent the decorator from ever having a state or being able to change it. This places a set of unorthodox (at least from author’s perspective) constraints on the DOM tree, created by the decorator. All of these constraints stem from the need to halt DOM changes in a decorator shadow subtree and include not allowing:
- <script> elements,
- inline event handlers,
- custom elements, etc.
Conceptually, this type of arrangement is the one of DOM projection, where there’s only one instance of the decorator’s shadow DOM subtree in existence, and each application is just a rendering of that DOM tree. While the barrier for fully grokking the frozen-state DOM could be high for authors, this is probably the highest-performing solution, since application and unapplication of a decorator is just a matter of building and tearing down a box tree.
All approaches mentioned here have their drawbacks, and it appears we either needs to invent something better or play the game of picking lesser evils.