What is lazy loading and why you dont need a framework or a library that implements it.
Lazy Loading
"Lazy loading is a strategy to identify resources as non-blocking (non-critical) and load these only when needed." MDN, Dec 20, 2023
The Lazy Loading is a popular and important technique for frontend development with Javascript.This technique is nothing more than loading and processing code on demand in large and complex applications.
Modern frontend frameworks and libraries, such as React and Vue, support lazy loading, but in addition to the feature not being their focus, their implementation may vary.
The fact is that lazy loading is a concept agnostic to any framework, and because it is simple to implement, it should be known to each and every frontend developer and we should not expect MVC or * V * type frameworks / libraries to officially support this functionality.
History
From my own experience, I can mention that the lazy loading technique has existed and been applied in frontend development for at least 16 years, in an era dominated by libraries and frameworks such as Dojo, EXTJS, DHTMLX and JQuery and when, an in -depth knowledge of the DOM API and XMLHttp was necessary to build large, performant applications.
Sixteen years ago, the browser language was ES5 and there were no tools such as bundlers, transpilers or frameworks that gave an opinion on the foundation of frontend applications, at most they offered components for them.And if they existed, they were just being born.
The most common foundational problems in developing large applications would be such as:
Scope issues.Pollution and conflict.
Lack of a support system for module distribution and consumption.
Last but not least, internet speed was drastically slower and resources such as RAM and CPU were also drastically inferior to what we have access to today.
It is naive to think that 16 years ago there were no use cases for large or complex frontend applications.
An example is this [https: //dhtmlx.com/blog/customer-spotlight-dhtmlx-cash-flow/](https: //dhtmlx.com/blog/customer-spotlight-dhtmlx-cash-flow/) module, from an ERP initially created in 2009.
https: //dhtmlx.com/blog/customer-spotlight-dhtmlx-cash-flow/
As a developer from that project, I can say that the ERP frontend, which by nature is a large application, consisted of a large number of modules, javascript files loaded on demand.
In 2009 there were already a considerable number of Single Page Applications being offered as SaaS.
We can therefore conclude that, more important than today, Lazy Loading was, and is, an indispensable resource for building large or complex frontend applications and offering better user experiences when it comes to speed and performance rendering.
Understanding how Lazy Loading works.
Requirements of a Lazy Loader
- Ability to asynchronously load Javascript files.
- Ability to asynchronously load CSS files.
- The browser must parse and execute the files.
- The loader must offer an mechanism that notify it callers when the
loaded file
was completely parsed and executed given the global scope;
Now that we have a list of the requirements, let 's see how would be, a common structure of a SPA that contains javascript and css files:
<!DOCTYPE html>
<html lang="en">
<head>
<link href="css/mycss.css" rel="stylesheet">
<title>Page title</title>
<script src="js/app.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
With basic knowledge about the DOM API, we can say that, to parse both files, Javascript and CSS, the HTML file calls the dependencies through the use of two types of HTML Nodes, script
and link
, passing the relative path of the files as parameters for the respective nodes.
Another approach would be to use the xmlHttp built in object.
Now that we know how the browser parses files, without reinventing the wheel, we can think about how to implement a Lazy Loading mechanism using Javascript.
To start a draft, let 's agree that we need to implement a lazyLoad
function that will be called to asynchronously load a list of files.
Since we are talking about different versions of Javascript, I will provide both examples in ES5 and ES6.
ES5
In ES5 we don 't have promises, so, to track the complete loading process of the file, and then notify the loader caller
, we need to rely on callbacks.
The function will receive tow parameters:
url
- relative path of the file.callback
- callback function that will be called when the file is completely loaded.
Code:
function getFileType(url) {
return url.indexOf(".css") > -1 ? "css" : "js";
}
function lazyLoad(url, callback) {
const fileType = getFileType(url);
let node = undefined;
// create the proper HTML NODE type
if (fileType === "js") {
node = document.createElement("script");
node.setAttribute("type", "text/javascript");
node.setAttribute("src", url);
} else {
node = document.createElement("link");
node.setAttribute("rel", "stylesheet");
node.setAttribute("type", "text/css");
node.setAttribute("href", url);
}
// track the loading process of the file
if (node.readyState) {
// IE 6-8 support
node.onreadystatechange = function () {
if (node.readyState == "loaded" || node.readyState == "complete") {
node.onreadystatechange = null;
console.log("node loaded");
callback();
}
};
} else {
// modern browsers
if (fileType === "css") {
console.log("css node loaded");
callback();
} else {
//console.log("no ie");
node.onload = function () {
console.log("js node loaded");
callback();
};
}
}
// append new HTML node onto HTML DOCUMENT HEAD
document.getElementsByTagName("head")[0].appendChild(node);
}
window.onload = () => {
lazyLoad("/MyModuleName1.css", function () {
lazyLoad("/MyModuleName1.js", function () {
console.log("files are ready");
MyModuleName1.render();
});
});
};
ES6
In ES6 we have promises and async /await, and it will simplify the process and avoid the callback hell.
The function will receive one parameter:
-url
- relative path of the file.
Code:
function getFileType(url) {
return url.includes(".css") ? "css" : "js";
}
function lazyLoad(url) {
return new Promise((resolve, reject) => {
const fileType = getFileType(url);
let node = undefined;
// create the proper HTML NODE type
if (fileType === "js") {
node = document.createElement("script");
node.setAttribute("type", "text/javascript");
node.setAttribute("src", url);
} else {
node = document.createElement("link");
node.setAttribute("rel", "stylesheet");
node.setAttribute("type", "text/css");
node.setAttribute("href", url);
}
// track the loading process of the file
if (node.readyState) {
// IE 6-8 support
node.onreadystatechange = function () {
if (
node.readyState == "loaded" ||
node.readyState == "complete"
) {
node.onreadystatechange = null;
console.log("node loaded");
resolve();
}
};
} else {
// modern browsers
if (fileType === "css") {
console.log("css node loaded");
resolve();
} else {
//console.log("no ie");
node.onload = function () {
console.log("js node loaded");
resolve();
};
}
}
// append new HTML node onto HTML DOCUMENT HEAD
document.getElementsByTagName("head")[0].appendChild(node);
});
}
window.onload = async () => {
await lazyLoad("/MyModuleName1.css");
await lazyLoad("/MyModuleName1.js");
console.log("files are ready");
MyModuleName1.render();
};