As we develop Web Apps we tend to hit performance bottlenecks along the way and while sometimes they are not as terrible they can slow down our Web App considerably and can consume a lot of data which isn’t great. There are some key areas we can focus on to increase drastically the performance of our Web Apps:
Unsplash Image Credits: Emil Jarfelt
- Google Maps bottleneck
- Loading Images Lazy
- Minify CSS and Javascript
- Serve assets using Service Worker
- IndexedDB is not that terrible
Google Maps bottleneck
A lot of websites these days use google maps to display their store location or to indicate where something is happening but few users know just how much google maps decreases the performance of a Website. It seems rather simple to copy and paste a pre-generated map into our HTML and 1,2,3 we have a map but Google map is resource hungry. The way google maps loads is that it loads several smaller images (tiles) to generate the whole map you requested and it also loads a few scripts. Unfortunately those images can take some time to load especially in HTTP 1.0.
The solution to this problem is rather simple:
- place it at the bottom where it gets rendered later
- use a toggle button to load the map only when the user actually needs it
Personally I prefer option 2 as it actually stops the map from loading the
individual tiles and gives the user the option to chose. The way you archive
this effect is simple. Place your google HTML snippet inside a div
and give
that div
a style of display:none
. This means that the user won’t use the
map but it will be loading the Javascript in the back just not the images.
<div id="map-container" style="display:none">
<!-- the map goes here -->
</div>
<div onclick="showMap(this)">
<img src="palceholder-image.png">
</div>
I usually use a dummy image of a map which is scaled up a lot which gives it a blurred effect. The replacement image itself is just 15kb large and when the user clicks the image, I change the styling to display:block for the map and display:none for the image.
function showMap(placeholder) {
let mapContainer = document.getElementById('map-container');
mapContainer.style.display = 'block';
placeholder.style.display = 'none';
}
Loading Images Lazy
Browsers load all images by default. This behaviour makes it easy to build websites but at the same time it makes sites less responsive. We cannot just ditch image loading all together but it would be wise to prioritize the images that are above the fold and lazy load the rest of the images (when the user scrolls). This sounds great but how do we do this ?
This is where the Intersection Observer API
comes into play. This API enables
us to tell when an HTML element enters the view and leaves it. We do this by
adding an element to our observer API.
When we create our images we cannot add the src
attribute just yet or else
the Browser will fetch the image immediately which we don’t want. Instead we
add a data-src
attribute which will store the image link for later:
<img class='image-class-name' data-src='ourImage.png'>
Now we can write the javascipt needed to notify us when the images enter the view:
const images = document.querySelectorAll('.image-class-name');
let observer = new IntersectionObserver(onIntersection, config);
images.forEach(image => {
observer.observe(image);
});
function onIntersection(element) {
const images = document.querySelectorAll(".your-classname");
element.forEach(elem => {
if (elem.intersectionRatio > 0) {
observer.unobserve(elem.target);
preloadImage(elem.target);
}
};
};
A quick walkthrough, first we are gathering all our images with the class
.image-class-name
. Next we create our IntersectionObserver
and finally we
loop through our images and add tell our observer to notify us when these images
enter the view. The onIntersection
function gets called when the image or
images [it can be an array] enter the view. The image is past into our function
as the first parameter. This element is an array so we loop through it
(it can be 1 as well as 100 images). We make sure that the element is without
our desired view and then we tell our observer to unobserve our image so that
we don’t fetch the same image more than once. Finally we load our image.
With a few lines of javascript we were able to lazy load our images. Now as the user scrolls our site, our images will load dynamically which means less Data consumption for our mobile users and a faster page speed.
Minify CSS and Javascript
Assets such as CSS and Javascript have to be minified or uglyfied. Personally I like injecting my minified CSS into the head with a style tag if my CSS is small to reduce unnecesary requests and to speed up the paint of the site. Javascript should be minified and added inside the body tag using an async or defer attribute to improve the way it is loaded. There are several tools available for this such Webpack and Parcel or the old school Gulp and Grunt. Especially CSS increases the pages “Firs meaningful paint” time which will reduce your performance quite a bit.
Serve assets using Service Worker
The service worker is a middle man that can effectively intercept all requests your website does. Why is this useful ? The big idea here is that you can cache resources on the user’s machine and then you can serve them locally instead of fetching them from the web. This doesn’t just reduce the amount of server calls it also lays out the way to an offline experience. In an offline/low signal scenario you can actually serve the website 100% from cache showing the user the content they want without accessing the internet which is not just faster but safes also data from your plan. Furthermore you can combine the service worker with IndexedDB to store large images and even videos locally which enhances even more the offline experience and responsiveness of your website.
IndexedDB is not that terrible
As we saw previously, IndexedDB allows us to store almost everything on the user’s machine. This is a great oportunity to build a web app around but also to improve the performance of our websites. IndexedDB works with callbacks.