DEV Community

Bryan Ollendyke
Bryan Ollendyke

Posted on

2 2

replace-tag with="high-performance"

In our "performance book" we'll have examples of things you could readily implement. While you could do so with replace-tag, I realize that perhaps this is more of an idea jog than direct implementation. Consider this a creative use-case that you could reverse engineer, or adopt directly from npm if it meets your needs!

Background reading: The magic script (read the whole series) is great for developer workflow and integration is simple, but it seems like it could be even more performant.

I was reading a blog post about the idea of querying a phone for it's connection speed and went down the rabbit hole to this idea: combining the two. You probably have to watch this to understand where I'm going but consider the following:

  • A single tag could have dozens of dependencies
  • You might not see it till going down the page
  • But you've still had to trace the import ESM chain of files even if it's not rendering
  • Some devices (old, slow, low power) might not even be in a user context to WANT what you are making them download (just to make it visible when swiping down to reveal)

The code powering replace-tag

What replace-tag looks like

<replace-tag with="meme-maker"
  top-text="Web components"
  bottom-text="Its the platform"
  image-url="https://media2.giphy.com/media/3cB7aOM6347PW/giphy.gif"
></replace-tag>
Enter fullscreen mode Exit fullscreen mode

replace-tag works with the wc-registry.json file produced in the unbundled-webcomponents build routine.

Decision tree

replace-tag has the following checks in place:

  • Am I visible? NOW you can import my definition, then replace the replace-tag with whatever is listed in with=""
  • If I have the import-only attribute, then import() and
  • Am I a low performance device? If import-method="view" then still activate based on visibility; otherwise, don't import and replace until the user specifically clicks me to do so.

Performance benefits of this

  • low import chain of files (faster initial paint)
  • intersection still the primary driver of importing that chain of files
  • device detection can help save on data and battery in critical UX moments (user away from power source trying to access critical data of your site / app, but losing power fast)
  • When 1 tag gets imported (definition wise) then it detects and imports all others

Possible issues

  • Things that require being there at run-time
  • Certain event timing that depends on being on page, affixing to window level events
  • Potential FOUC / layout thrashing if you don't account for this as far as tag-name:not(:defined) but also then handling replace-tag[with="tag-name"]

What is a low performance device?

By our definition, it's any of the following being true:

  • Less than 1 gig of memory
  • Less than 4 core processor
  • 2g or 3g connection speed
  • user saying they want to save data
  • user's device having less than 25% power

Here's the cool block of code where we're able to do these detections using a series of navigator calls - PerformanceDetect.js with this specific block:

async updateDetails() {
    let details = {
      lowMemory: false,
      lowProcessor: false,
      lowBattery: false,
      poorConnection: false,
      dataSaver: false,
    };
    if (navigator) {
      // if less than a gig we know its bad
      if (navigator.deviceMemory && navigator.deviceMemory < 1) {
        details.lowMemory = true;
      }
      // even phones have multi-core processors so another sign
      if (navigator.hardwareConcurrency && navigator.hardwareConcurrency < 2) {
        details.lowProcessor = true;
      }
      // some platforms support getting the battery status
      if (navigator.getBattery) {
        navigator.getBattery().then(function (battery) {
          // if we are not charging AND we have under 25% be kind
          if (!battery.charging && battery.level < 0.25) {
            details.lowBattery = true;
          }
        });
      }
      // some things report the "type" of internet connection speed
      // for terrible connections lets save frustration
      if (
        navigator.connection &&
        navigator.connection.effectiveType &&
        ["slow-2g", "2g", "3g"].includes(navigator.connection.effectiveType)
      ) {
        details.poorConnection = true;
      }
      // see if they said "hey, save me data"
      if (navigator.connection && navigator.connection.saveData) {
        details.dataSaver = true;
      }
    }
    return details;
  }
Enter fullscreen mode Exit fullscreen mode

This function has to be async because getBattery() (which is not in all platforms) is a Promise().

Video version

Heroku

Built for developers, by developers.

Whether you're building a simple prototype or a business-critical product, Heroku's fully-managed platform gives you the simplest path to delivering apps quickly — using the tools and languages you already love!

Learn More

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, valued within the supportive DEV Community. Coders of every background are welcome to join in and add to our collective wisdom.

A sincere "thank you" often brightens someone’s day. Share your gratitude in the comments below!

On DEV, the act of sharing knowledge eases our journey and fortifies our community ties. Found value in this? A quick thank you to the author can make a significant impact.

Okay