Aamir
1. A slow site is very uncomfortable, especially on mobile-phone
2. A slow site directly affects your product.
Now the obvious question is, what makes the website fast:-
A site is fast when:-
---- It loads quickly,
---- And, being loaded, it works quickly (meaning animations don’t skip frames, scrolling is smooth, etc.)
The site loads quickly when:-
---- The server promptly responds to requests
---- The app renders quickly
In this article, we will mostly talk about how to loads and renders the page quickly
Minification :- During minification, the code loses all unnecessary characters, receives shorter variable names, and so on. In the end, it becomes smaller but keeps working as intended. Minification helps to reduce the code size by 30–40%.
Minification is supported in every major app builder:
— mode: production in webpack,
— babel-preset-minify in Babel,
— gulp-uglify in Gulp
Async and Defer:- Next. So, you wrote a script, minified it, and now want to load it on a page. How would you connect it to the page?
The simplest approach is just to write the <script> tag and specify the path to your file. It’s a fine approach, and it works. But do you know what’s the issue with this approach?
The issue is that scripts block parsing. What does this mean?
When your browser loads the page, it starts parsing the HTML document into tags and building a DOM tree. Later, it uses this DOM tree to render the page.
The problem is, a script can alter the way the DOM tree is built. For example, a script can call document.write() and write an opening comment tag into the document. This will ruin the whole DOM tree (and the whole page!) that comes after the script.
That’s why browsers stop parsing when they encounter a script – to keep the document from jumping and to avoid doing additional work.
From the browser’s standpoint, here’s how this looks:_
— The browser starts scanning the document and parsing it
— At some moment, the browser encounters the <script> tag. It pauses the HTML parsing and starts downloading and executing the script
— Once the script is executed, the browser continues with parsing
So, what to do? Use async and defer script attributes.
These attributes tell the browser that a script should be downloaded in the background, without interrupting document parsing. Here’s how they work:
— The async attribute asks the browser to download a script asynchronously (in the background) and to execute it as soon as it is loaded. Document parsing will be continued while the script is being downloaded. (If the script downloads before the parsing is complete, parsing will be paused while the script executes, but because downloading a script usually takes more time than parsing a document, this rarely happens.)
— The defer attribute tells the browser to download the script asynchronously and execute it only after the document is parsed.
here’s a significant difference between async and defer attributes:
— async scripts are executed as soon as the download occurs, without maintaining the script order. This means that if you have an async React bundle and an async app bundle, and the React bundle is larger, the app will be downloaded and executed earlier than React – potentially breaking the site
— defer scripts, unlike async, would execute in the right order only after the document is fully downloaded. Because of this, defer might be safer than async when optimizing a large complex app.
And, as a result, from my experience, these optimizations can strip 200–500 ms (or even more!) off the loading time, depending on your code size and connection. Half a second is actually a lot.
Code Splitting:- Applications are often built this way: you compile an app and end up with a big bundle that you send to the client with each request. The problem is that apps often have screens that users would encounter at very rare times –
" e.g., modal windows that open once a month or routes that nobody ever uses. Even though the code from these routes or popups is almost useless, it still takes up space in the bundle and increases loading time."
This is usually solved by code splitting – dividing big bundles into smaller ones.
With code splitting, we move different parts of app functionality to different files and fetch them only when necessary. Thanks to this, if a user doesn’t need to open the “Change avatar” modal, they won’t download it at all.
How to implement code splitting?
You’ll need a bundler like webpack, Parcel, or Rollup. If you use React, you can use React.lazy method as of React 18 version.
Well, and data savings from code splitting? Huge.
If done properly, code splitting is the most meaningful optimization in terms of data saving. So:
If you only do a single optimization in your app, make it code splitting
Optimize App dependencies :- Like to remove unused code from dependencies. We will discuss this in a separate article
Minify CSS just like your JS code. Delete unnecessary spaces and characters to make the code smaller.
Here are the tools that would help you with this:
— webpack’s postcss-loader with cssnano
— PostCSS’s cssnano
— Gulp’s gulp-clean-css
Styles block rendering :- Just like JavaScript blocks parsing – styles block page rendering. That’s because sites without styles look weird. If a browser rendered pages before styles were loaded, the user would first see this...
...and then the page would blink, changing to this. Hardly a pleasant user experience.
That’s why the page remains white while styles are still loading.
Now, there’s a clever optimization trick here. It’s perfectly reasonable that browsers keep the page empty while styles haven’t loaded, and we don’t want to get rid of this. However, we can still make pages render faster – by loading the page with just a part of styles needed for initial rendering and fetching the remaining styles afterward. Those styles needed for initial rendering are called “Critical CSS”.
Let’s see how Critical CSS works.
With Critical CSS, the page loading process looks like this:
You split your styles into critical and non-critical CSS.
You inline the critical CSS right into the HTML response. This helps to serve it as soon as possible.
Now, as soon as you serve HTML with the critical CSS inlined, the page could already be rendered. However, you still need to fetch and apply the non-critical CSS.
There are several ways to load remaining CSS, and all of them are hacky, unfortunately. Here’s what I prefer to do:
You add a <link rel="stylesheet"> to start fetching the non-critical CSS file – but as a media="print" stylesheet.
And, as soon as the file is fetched and cached, you change the media attribute from print to all. This makes the browser take the cached CSS file and apply it to the document.
Fortunately, there are automated tools that can do it for you:-
— styled-components. It’s a CSS-in-JS library that extracts and returns critical styles during server-side rendering. It works only if you write styles using styled-components, but if you do, it works really well.
— critical. It’s a utility that takes an HTML page, renders it in a headless browser and extracts styles for above-the-fold content. Because critical runs only over a single page, it might not work well for complex single-page apps.
— penthouse. It’s similar to critical but operates on URLs instead of HTML pages.
What about performance wins? They are huge. From my experience, extracting critical CSS will shave 200–500 ms off Time to First Paint – or even more!
Minification:- The first approach to transferring less data over the network is, again, minification. Minify HTML documents you’re sending to the client (along with CSS and JS, as we discussed earlier).
Brotli:- The second approach to transferring less data is to compress everything you send to the client using Brotli.
Brotli is an algorithm that compresses data you send to the client using a sophisticated archiving algorithm. After compression, your documents will look like an unreadable binary soup, but their volume will be reduced by 60–80%. And when a browser receives the data, it will decompress it back.
Preloading:- There’re 5 different preload methods, and all are tailored for different purposes.
— <link rel="dns-prefetch"> instructs the browser to make a DNS request for a server’s IP address in advance. This is useful for CDNs, Google Fonts, and for other cases when you know you’ll need a resource in a short time, know the domain it’s hosted at, but don’t know its exact path. In this case, resolving the server’s IP address in advance would save you from 50 to 300 ms.
— <link rel="preconnect"> instructs the browser to perform the connection to a server in advance. It’s useful in the same cases when dns-prefetch is useful, but sets up a full connection and saves more time. The drawback here is that opening a new connection is pretty resource-intensive, so you don’t want to overuse this optimization.
— <link rel="prefetch"> preloads and caches a resource in background with a low priority. This is useful e.g. to preload a JS bundle for the next page of an app.
— <link rel="preload"> preloads a resource in background with a high priority. This is useful to preload a resource you’ll need in several seconds – e.g., a non-critical CSS file.
— <link rel="prerender"> preloads the specified page in the background and renders it in an invisible tab. Later, when a visitor clicks to a link leading to the prerendered page, the page displays immediately. This is what Google uses to preload its first search result.
Choose a better format:-
svg is best for vector images such as icons or logos.
jpg is best for photos because it compresses images with a slight quality loss not visible by the human eye.
png i s best for raster graphics that you want to display without any quality losses – e.g., raster icons or pixel art.
webp works great for both photos and raster graphics because it supports both lossy and lossless compression. It also compresses at least 20-30% better than jpg and png
And, finally, gifs.
Don’t use gifs at all. gifs are massive – too often, they take megabytes or tens of megabytes of data. Instead, use video files (with the tag) which compress the content more effectively.
More importantly, try to minimize the image as much as possible without much compromising the quality
Add font fallback:- A fallback font is a font that the browser uses if the primary font can’t be downloaded or if it takes a while to load. It’s specified in the font or font-family CSS declarations after the custom font name.
A fallback font might be a popular built-in font (like Georgia); it might be a generic font family (like serif or sans-serif); or it might be both. Generally, even if you specify a popular built-in font, you should still add a generic font family – because even that font might be absent on some devices.
Without the fallback font, if a custom font isn’t available, the browser will render everything in the default serif font. And this might not look good.
With the fallback font, however, you have a chance to specify the font that resembles the custom one.
Font-display:- Use the font-display CSS property for your custom fonts.
The font-display property adjusts the way a custom font is applied. By default, it’s set to auto, and in all major browsers, this means the browser will wait 3 seconds for the custom font to download. This means that if a network is slow, the visitor will have to wait for the text for another 3 seconds.
This is bad. To optimize this, specify a different font-display value.
There’re two font-display values that I believe are suitable in most cases.
The first one is font-display: fallback. With it, the browser will render the text immediately with the available font: either the custom font if it’s cached, or the fallback font. Then, if the custom font isn’t cached, the browser will download it. And, if the custom font downloads quickly enough (usually in 3 seconds), the browser will swap the fallback font with the custom one.
With this behavior, sometimes, the user will see the page in the fallback font, start reading, but then the browser will flash the text by substituting the custom font (see also: the flash of unstyled text). This is slightly worse for the user experience, but it’s still better than not showing any text at all.
The second suitable font-display value is optional. With it, the browser will also render the text immediately with the available font: either the custom font if it’s cached, or the fallback font. But then, even if the custom font downloads, the browser won’t substitute it until the page is refreshed.
This behavior means that the user will only see the page in the custom font or in the fallback one, but will never experience the flash of unstyled text.
How to choose between font-display: fallback and font-display: optional?
I believe it’s a matter of taste. I, personally, prefer to keep my text rendered with the custom font, so I choose the font-display: fallback value. If you’re OK that first-time visitors will see your page in a fallback font, font-display: optional would work great for you.
Wrap Up. Share this article and kindly bookmark this website to get the latest blog on full-stack. Till then Good bye, happy reading.
Thank you
A handful guide to make the developerslife easy.