Re: [WICG/webcomponents] Lazy Custom Element Definitions (#782)

I'm not sure this API should force its users into delaying their definitions until the next microtask checkpoint or changing the construction / upgrade order of trees depending on whether or not they contain undefined custom elements, if those behaviors aren't required. If `lazyDefine`'s callback's return value[^1] was allowed to be either a custom element class or `undefined`, then it would leave the user the choice of if and how to handle definition order and timing.

[^1]: Or, some sync mechanism during an event listener if this was an event.

This could be useful if you have multiple large trees that you expect to insert into the page as a result of different user actions and you want to preload definitions used in those trees after the initial page is idle but defining them all immediately after the preload completes would be too expensive. If this API doesn't allow you to provide a definition synchronously, then your large trees that have definitions that could have been supplied immediately will all have to contend with being forced to upgrade in definition order. If the API does allow you to provide a definition synchronously, then you can structure things such that your tree is still upgraded in tree order, if that's important to you.

Tree order might be important if your heavy tree contains components that expect to signal each other using events once they're connected. In the example below, maybe `<collapsible-directory>` intends to display summary information about its descendants inside its own row (e.g. total size or number of items). If the definitions can't be provided synchronously then you're have to give them one at a time to `customElements.define`, so either all `<collapsible-directory>`s have to upgrade before all `<file-row>`s or vice versa.

```js
const definitionCache = new Map();

customElements.lazyDefine('collapsible-directory', () => definitionCache.get('collapsible-directory'));
customElements.lazyDefine('file-row', () => definitionCache.get('file-row'));

const readyToShowHeavyDialog = import('./definitionsUsedInHeavyDialog.js')
    .then(({definitions /*: Map<string, CustomElementConstructor> */}) => {
  definitions.forEach((k, v) => definitionCache.set(k, v));
});

someButton.addEventListener("click", async () => {
  await readyToShowHeavyDialog;
  const dialog = document.createElement("dialog");
  // This will upgrade in tree order because all of the definitions
  // were supplied synchronously:
  dialog.innerHTML = `
    <collapsible-directory>
      <span slot="name">outerDir</span>
      <file-row>file1</file-row>
      <file-row>file2</file-row>
      <collapsible-directory>
        <span slot="name">innerDir</span>
        <file-row>file1</file-row>
        <file-row>file2</file-row>
        <file-row>file3</file-row>
      </collapsible-directory>
      <file-row>file3</file-row>
    </collapsible-directory>
  `;
  document.body.appendChild(dialog);
  dialog.showModal();
});
```

Also, I know that upgrade candidate lists were removed from the initial version of the custom elements spec, but IIRC that was mostly because the lifetime of those candidate lists was unbounded. With an API like this, where you could supply a definition synchronously, you could use candidate lists to avoid full-document tree walks because they would only need to exist for the time it takes the callback to return. The callback is called only because no element with the given tag name has been seen in an upgradable context before, so that means that if the callback returned a definition synchronously, then the only other elements with that tag name that would need to be upgraded would be those that would have attempted to upgrade during that callback, letting you avoid the full-document walk at the end. (The browser might have to sort them though.) If the callback returns `undefined` instead, then just drop the candidate list and let the `define` call's full-document walk do its normal thing whenever that happens.

-- 
Reply to this email directly or view it on GitHub:
https://github.com/WICG/webcomponents/issues/782#issuecomment-1524121422
You are receiving this because you are subscribed to this thread.

Message ID: <WICG/webcomponents/issues/782/1524121422@github.com>

Received on Wednesday, 26 April 2023 22:33:48 UTC