An Method to Lazy Loading Customized Parts | CSS-Methods

on

|

views

and

comments

[ad_1]

We’re followers of Customized Parts round right here. Their design makes them significantly amenable to lazy loading, which could be a boon for efficiency.

Impressed by a colleague’s experiments, I lately set about writing a easy auto-loader: At any time when a {custom} ingredient seems within the DOM, we wanna load the corresponding implementation if it’s not obtainable but. The browser then takes care of upgrading such components from there on out.

Chances are high you gained’t really want all this; there’s normally a less complicated method. Used intentionally, the strategies proven right here may nonetheless be a helpful addition to your toolset.

For consistency, we wish our auto-loader to be a {custom} ingredient as properly — which additionally means we will simply configure it through HTML. However first, let’s establish these unresolved {custom} components, step-by-step:

class AutoLoader extends HTMLElement {
  connectedCallback() {
    let scope = this.parentNode;
    this.uncover(scope);
  }
}
customElements.outline("ce-autoloader", AutoLoader);

Assuming we’ve loaded this module up-front (utilizing async is right), we will drop a <ce-autoloader> ingredient into the <physique> of our doc. That can instantly begin the invention course of for all baby components of <physique>, which now constitutes our root ingredient. We might restrict discovery to a subtree of our doc by including <ce-autoloader> to the respective container ingredient as an alternative — certainly, we would even have a number of situations for various subtrees.

In fact, we nonetheless should implement that uncover methodology (as a part of the AutoLoader class above):

uncover(scope) {
  let candidates = [scope, ...scope.querySelectorAll("*")];
  for(let el of candidates) {
    let tag = el.localName;
    if(tag.consists of("-") && !customElements.get(tag)) {
      this.load(tag);
    }
  }
}

Right here we examine our root ingredient together with each single descendant (*). If it’s a {custom} ingredient — as indicated by hyphenated tags — however not but upgraded, we’ll try to load the corresponding definition. Querying the DOM that manner may be costly, so we must be somewhat cautious. We will alleviate load on the primary thread by deferring this work:

connectedCallback() {
  let scope = this.parentNode;
  requestIdleCallback(() => {
    this.uncover(scope);
  });
}

requestIdleCallback just isn’t universally supported but, however we will use requestAnimationFrame as a fallback:

let defer = window.requestIdleCallback || requestAnimationFrame;

class AutoLoader extends HTMLElement {
  connectedCallback() {
    let scope = this.parentNode;
    defer(() => {
      this.uncover(scope);
    });
  }
  // ...
}

Now we will transfer on to implementing the lacking load methodology to dynamically inject a <script> ingredient:

load(tag) {
  let el = doc.createElement("script");
  let res = new Promise((resolve, reject) => {
    el.addEventListener("load", ev => {
      resolve(null);
    });
    el.addEventListener("error", ev => {
      reject(new Error("didn't find custom-element definition"));
    });
  });
  el.src = this.elementURL(tag);
  doc.head.appendChild(el);
  return res;
}

elementURL(tag) {
  return `${this.rootDir}/${tag}.js`;
}

Be aware the hard-coded conference in elementURL. The src attribute’s URL assumes there’s a listing the place all {custom} ingredient definitions reside (e.g. <my-widget>/parts/my-widget.js). We might provide you with extra elaborate methods, however that is ok for our functions. Relegating this URL to a separate methodology permits for project-specific subclassing when wanted:

class FancyLoader extends AutoLoader {
  elementURL(tag) {
    // fancy logic
  }
}

Both manner, word that we’re counting on this.rootDir. That is the place the aforementioned configurability is available in. Let’s add a corresponding getter:

get rootDir() {
  let uri = this.getAttribute("root-dir");
  if(!uri) {
    throw new Error("can not auto-load {custom} components: lacking `root-dir`");
  }
  if(uri.endsWith("https://css-tricks.com/")) { // take away trailing slash
    return uri.substring(0, uri.size - 1);
  }
  return uri;
}

You may be pondering of observedAttributes now, however that doesn’t actually make issues simpler. Plus updating root-dir at runtime looks as if one thing we’re by no means going to want.

Now we will — and should — configure our components listing: <ce-autoloader root-dir="/parts">.

With this, our auto-loader can do its job. Besides it solely works as soon as, for components that exist already when the auto-loader is initialized. We’ll in all probability wish to account for dynamically added components as properly. That’s the place MutationObserver comes into play:

connectedCallback() {
  let scope = this.parentNode;
  defer(() => {
    this.uncover(scope);
  });
  let observer = this._observer = new MutationObserver(mutations => {
    for(let { addedNodes } of mutations) {
      for(let node of addedNodes) {
        defer(() => {
          this.uncover(node);
        });
      }
    }
  });
  observer.observe(scope, { subtree: true, childList: true });
}

disconnectedCallback() {
  this._observer.disconnect();
}

This manner, the browser notifies us at any time when a brand new ingredient seems within the DOM — or moderately, our respective subtree — which we then use to restart the invention course of. (You may argue we’re re-inventing {custom} components right here, and also you’d be form of appropriate.)

Our auto-loader is now totally useful. Future enhancements may look into potential race circumstances and examine optimizations. However likelihood is that is ok for many eventualities. Let me know within the feedback you probably have a special method and we will evaluate notes!

[ad_2]

Supply hyperlink

Share this
Tags

Must-read

Google Presents 3 Suggestions For Checking Technical web optimization Points

Google printed a video providing three ideas for utilizing search console to establish technical points that may be inflicting indexing or rating issues. Three...

A easy snapshot reveals how computational pictures can shock and alarm us

Whereas Tessa Coates was making an attempt on wedding ceremony clothes final month, she posted a seemingly easy snapshot of herself on Instagram...

Recent articles

More like this

LEAVE A REPLY

Please enter your comment!
Please enter your name here