Blog micro-optimization
My last proper blog post about making a custom frame bag was very image-heavy, which has the downside of making the page heavy. I don't want inefficient pages on my website.
So I took the opportunity to tweak my optimization script. This is the script that minifies HTML, inlines critical CSS, and optimizes images. The optimization part for images looks like this now:
/**
* @param {Number} i - the order of this image in the file
*/
async function processImage($, im, i) {
const img = $(im);
const src = img.attr("src");
if (!src.startsWith("/images/")) return;
const ext = path.extname(src);
if (!(ext === ".jpg" || ext === ".jpeg")) return;
const input = sharp(path.join("_site/", src));
const metadata = await input.metadata();
const webpSrc = src.replace(ext, `.webp`);
const cachePath = `./.image_cache/${path.basename(webpSrc)}`;
if (fs.existsSync(cachePath)) {
fs.renameSync(cachePath, path.join("_site/", webpSrc));
timings.imageCacheHits++;
} else {
await Promise.all([input.webp().toFile(path.join("_site/", webpSrc))]);
timings.imageCacheMisses++;
}
const $picture = cheerio.load(`<picture>
<source srcset="${webpSrc}" type="image/webp" />
</picture>`);
img.attr("width", String(metadata.width));
img.attr("height", String(metadata.height));
if (i > 2) {
img.attr("loading", "lazy");
}
$picture("picture").append(img.parent().html());
return $picture("body").html();
}So this now does quite a bit to images! Images beyond the first two on a page get loading="lazy" - Browser-level image lazy loading, and it now adds width and height attributes to all of the images it processes so that their lazy-loading doesn't cause page-height jitter or reflow. Works pretty well! The syntax of the <picture> element always throws me for a loop a bit: the <img> inside of the <picture> essentially becomes the element shown on the page, so the lazy and dimension attributes should still go on the img element, but the other <source> elements essentially override that img element's src attribute.
As a sidenote, WebP is almost everywhere, so maybe I'll drop the picture element soon and only serve WebP.