constructor() dynamic import()
Tags: import, esm, javascript
Published: 2021-04-12 14:41:03

Modern JavaScript [via ES Module imports](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import), provides us with two ways of handling modular JavaScript. There's `import` whatever from `where-ever` style, and then there's `import()`. While minor in syntax difference, did you know they have a direct impact on the loading of your application? Let's look:
```js
// knights-who.js
import "./the-parrot-sketch.js";
// really important class that says..
class KnightsWho extends HTMLElement {
  constructor() {
    super();
    if (this.getAttribute("say") != null) {
      let sketchTag = document.createElement("the-parrot-sketch");
      sketchTag.innerHTML = this.getAttribute("say");
      this.appendChild(sketchTag);
    }
  }
}
customElements.define("knights-who", KnightsWho);
```
Then your `main.html` document might reference this really important modular JavaScript as follows:
```html
<script type="module" src="knights-who.js"></script>
<knights-who say="Nee"></knights-who>
```
![We want... a shrubbery!](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lbi7z6flmy1ow1noyi84.gif)
 
In this syntax, the browser responds with the following data cascade timing...
1. GET `main.html`, start parsing
1. See script type="module" start requesting `knights-who.js`
1. Reads file for additional import references, finds `the-parrot-sketch.js` and requests that
1. Reads file for additional import references, endlessly until there are *no more additional modular references*
1. Completes modular chain of code, executes all at once,
1. `knights-who` tag will say Nee, wrapped in a `<the-parrot-sketch>` tag; horrifying.

This is how modular JavaScript works though, it spiders out looking for additional modular `import` references and then once all of them load, it executes all of them. This is great for developers to ship modular code, but what if you had ~100 references nested in other references?

## "One Weird Trick" Dynamic import()
A dynamic `import()` could be leveraged in our `constructor()` to visually look similar, yet have a very different execution timing.. Let's look.
```js
// knights-who.js
// really important class that says..
class KnightsWho extends HTMLElement {
  constructor() {
    super();
    if (this.getAttribute("say") != null) {
      let sketchTag = document.createElement("the-parrot-sketch");
      sketchTag.innerHTML = this.getAttribute("say");
      this.appendChild(sketchTag);
      setTimeout((e) => {
        import("./the-parrot-sketch.js");
      }, 0);
    }
  }
}
customElements.define("knights-who", KnightsWho);
```
In this setup, we use `import()` inside of our constructor(). By doing this, we get the following timing on spin up.
1. GET `main.html`, start parsing
1. See script type="module" start requesting `knights-who.js`
1. Reads file for additional import references, finds none.
1. Completes modular chain of code, executes all at once,
1. `knights-who` tag will say Nee, wrapped in a `<the-parrot-sketch>` tag (undefined).
So it starts to paint while in the background, delayed one microtask, `./the-parrot-sketch.js` read off endlessly until there are *no more additional modular references*, but the tag is imported on it's own schedule!

The key difference here is that we've started to paint potentially long before we would have otherwise by breaking our chain into multiple execution chains! While small in a single element, imagine building a whole application where every step of the way you handled info this way.

Here's a gif showing this happening at scale in HAXcms as loaded on [haxtheweb.org](haxtheweb.org). Loading has been throttled to 3G to demonstrate, but all the parts of the UI are web components and all parts load in via a series of broken up `import()` chains to optimize delivery.

![haxtheweb.org loading throttled](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kn62qrh89wlaafo012ap.gif)  

## Considerations
This breaks up timing so you could get a FOUC if there is a non-hydrated element that has spacing considerations (which is likely). In the `.gif` above a chunk was cut out that was just a white screen as we need to fix a our loading indicator's timing to avoid FOUC 😳. But, even with this, we don't actually flash unstyled content as we currently just have a loading bar that goes until the UI is ready. Individual UI elements then have sane sizing defaults using a css selector trick of `:not(:defined) {}` which helps w/ selecting web components that do not have a definition (yet).

The `import()` methodology is to speed up time to first paint (TTFP) and so you could use some sizing styles or css or stateful variables internal to the import in order to reduce FOUC. We'll go into dynamic import `Promise` later but here's a taste:
```js
  connectedCallback() {
    this.setAttribute("hidden", "hidden");
    import("./what-ever.js").then((m) => { this.removeAttribute("hidden")});
  }
```
While simplistic, this would allow the whole application / other elements to keep loading in the background while the user still obtains part of the experience. `connectedCallback` means we it is attached to the DOM and thus we can start setting attributes. This code would "paint" the element, then hide it, then when the internals of `what-ever.js` have loaded, it would reveal the entire element.