replace-tag with="high-performance"
Bryan Ollendyke
Posted on May 12, 2021
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)
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>
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, thenimport()
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 handlingreplace-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;
}
This function has to be async
because getBattery()
(which is not in all platforms) is a Promise()
.
Video version
Posted on May 12, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.