⬆️ ⬇️

Frontend performance. Part 1 - concatenation, compression, caching (4th of 12 articles about Mozilla’s Node.js)

From the translator: This is the fourth article from the Node.js series from the Mozilla Identity team that deals with the Persona project. This article is about how to increase the speed of loading site resources and the connect-cachify module created in Mozilla for this purpose.










In this article, we’ll talk about front-end performance and present the tools that Mozilla created to make the Persona site as fast as possible. We describe the work with connect-cachify, a module that automates some of the most important aspects that ensure front-end performance.

')

But first, let's take a look at the most common ways to improve performance. If you already have experience in optimizing the frontend, you can skip these sections and go to the end of the article, which describes how to use connect-cachify to automatically do what you used to do manually.



Three 'K' client performance



The network is full of information on how to achieve high performance. There are dozens of tricks that can save a millisecond or two here or there, but three techniques — concatenation, compression, and caching — always form the basis for optimization.



Concatenation


The purpose of concatenation is to minimize the number of requests to the server. Requests are expensive. The time to open an HTTP connection is often longer than the time it takes to transfer data. Every extra request slows down the site, especially on mobile devices, where network delays can be very large. Have you ever had to go to any online store from a mobile phone on EDGE and painfully wait for the alternate loading of multiple images?



The new protocol SPDY, which has already become the basis for the specification of the new version of the HTTP 2.0 protocol, is able to combine requests to several resources into one HTTP connection. Unfortunately, so far his support is only in the latest versions of Chrome, Firefox and Opera.



Combining several resources into one in manual mode, in the old-fashioned way, works anywhere and will work even after the widespread distribution of SPDY. There are many tools for combining the main types of resources - JavaScrit, CSS and images.



Javascript and css


If the site uses more than one external JavaScript file, then you should think about merging all these files into one. Browsers suspend page rendering while scripts are loading. Since each request requires time overhead, the example below works slower than it could:



<script src="jquery.min.js"></script> <script src="main.js"></script> <script src="image-carousel.js"></script> <script src="widget.js"></script> 


By combining several requests into one, you can significantly reduce load times:



  <script src="main.production.js"></script> 


Working with the combined file while writing code and debugging is very inconvenient, therefore, usually concatenation is done only for the finished site. CSS files are merged in the same way as JavaScript.



Images


The two main techniques for reducing the number of requests for loading images are embedding data in URLs and sprites.



data: URI


data: URI is a special form of URL that allows you to embed small images as a URL directly into the text of an HTML or CSS document. The data URI can be used both in the src attribute of the img, tag and in the url of the background image in CSS. Since embedded images are encoded in base64, they take up more space, but require one request less than normal images. If the picture is small enough, the gain due to the decrease in the number of requests is greater than the loss in size. IE6 and IE7 do not support data: URI, keep this in mind.



Sprites


Sprites are a great alternative to data: URI. Sprite is a collection of images combined into one big picture. Using CSS you can cut out small images from it. The lack of sprites - inconvenience in maintenance and modification. Adding or modifying images in the sprite may require corresponding changes in the CSS.



There are many tools for creating sprites from a set of individual images. One of them is the Sprite Cow site, which generates CSS code for sprites based on the loaded image.



Remove extra bytes - minification, optimization and compression



Consolidating several resources into one to minimize the number of HTTP requests is a big step towards speeding up the site, but more can be achieved. After concatenation, you need to reduce the total number of bytes sent over the network. This is usually done in the form of minification, optimization and compression.



Javascript and css


JavaScript and CSS are textual resources that are effectively minified. Minification is the removal of all characters that the browser ignores. Conversion of both JavaScript and CSS begins with the removal of extra spaces, line breaks and comments. In the case of JavaScript, you can go much further. Some minifiers replace multi-character variable names with single-character ones, remove unnecessary language constructs and even replace them with equivalent shorter ones.



Among the popular tools for JavaScript minification are UglifyJS , YUICompressor and Google Colsure Compiler . For CSS, YUICompressor and UglifyCSS are often used.



Images


Images often contain redundant data that can be removed without degrading visual quality. This is not difficult, but requires the use of special tools. Francois Marie from our team wrote in more detail on this topic in articles devoted to working with PNG and GIF .



Yahoo! offers the online tool Smush.it . ImageOptim is a similar offline program for OSX. It can work in fully automatic mode - just drag the necessary files onto it, and they can lose weight noticeably.



If some deterioration in visual quality is permissible, you can additionally squeeze the image with a greater degree of compression.



The server can also help



Even after concatenation and minification there is something to save. Almost all servers and browsers support HTTP traffic compression. The two most popular compression schemes are deflate and gzip. They both use very efficient compression algorithms to reduce the size of the data before they leave the server.



Caching


Concatenation and compression help those who first visited our site. The third "K" - caching, helps regular visitors. A user who has already been on the site should not reload all resources. HTTP offers two mechanisms to achieve this - caching headers and ETags, or entity tags.



There are two kinds of HTTP headers related to caching static resources that never or almost never change. These are the two HTTP response header fields — Expires and Cache-Control: max-age . The Expires field stores the date after which the resource will need to be updated. max-age - resource lifetime in seconds. If these fields in the header are set, the browser will request an existing resource only after the specified time has elapsed.



The ETag is the resource version label, which serves to ensure that the local version of the resource matches the actual version on the server. Etag is suitable for dynamic content that can change at any time. When an ETag is installed on a resource, it tells the browser to check the version, and if it has not changed, the resource can be not downloaded. Since the ETag resource still requires a small amount of information to be transferred from the server each time, it is not as effective as the resource with the lifetime indication.



Forced cache cleaning


The advantage of using Expires and max-age fields over Etag is that the browser requests resources only if they are expired. This is also the main drawback. What if the resource suddenly changes? Sometimes it is necessary to forcibly clear the cache.



This is usually done by adding the version number of the resource to the URL. Any change to the URL causes the new version of the resource to be loaded.



For example, if the lifetime example.com/logo.png example.com/logo.png is one year, but the logo is changing, everyone who has already downloaded the old version will see the new one only in a year. You can deal with this by adding a version identifier to the URL:



 http://example.com/v8125/logo.png 


or



 http://example.com/logo.png?v8125 


When the logo changes, the URL will also change, which means all users will download the new version.



Connect-cachify: the Node.js library for concatenating and caching resources



Connect-cachify is a middleware for Node.js, developed by Mozilla to facilitate the concatenation and caching of resources.



In production mode, connect-cachify gives combined and minified resources and sets their lifetime to one year by default. In development mode, instead, resources are given one by one, which makes debugging easier. Connect-cachify does not deal with concatenation and minification, leaving it on the conscience of the build script.



connect-cachify is configured by calling the setup() function. It takes two arguments, assets and options . assets is a dictionary that establishes the correspondence between resources in production and development modes. Each combined and minified resource corresponds to several separate development mode.



options are optional parameters, among which are:





Connect-cachify usage example



Suppose we have simple HTML that references three CSS files and three separate scripts:



 ... <head> <title>Dashboard: Hamsters of North America</title> <link rel="stylesheet" type="text/css" href="/css/reset.css" /> <link rel="stylesheet" type="text/css" href="/css/common.css" /> <link rel="stylesheet" type="text/css" href="/css/dashboard.css" /> </head> <body> ... <script type="text/javascript" src="/js/lib/jquery.js"></script> <script type="text/javascript" src="/js/magick.js"></script> <script type="text/javascript" src="/js/laughter.js"></script> </body> ... 


We connect connect-cachify to our server, set the correspondence between production and development-resources and configure the module:



 // Include connect-cachify const cachify = require('connect-cachify'); // Create a map of production to development resources var assets = { "/js/main.min.js": [ '/js/lib/jquery.js', '/js/magick.js', '/js/laughter.js' ], "/css/dashboard.min.css": [ '/css/reset.css', '/css/common.css', '/css/dashboard.css' ] }; // Hook up the connect-cachify middleware app.use(cachify.setup(assets, { root: __dirname, production: your_config['use_minified_assets'], })); ... 


In order not to violate the DRY principle, the resource map can be stored in a separate file used for both connect-cachify and the build script.



Then we will update our templates, indicating the place of inclusion of CSS and JavaScript. CSS is connected using the cachify_css helper, JavaScript, respectively, cachify_js .



 ... <head> <title>Dashboard: Hamsters of North America</title> <%- cachify_css('/css/dashboard.min.css') %> </head> <body> ... <%- cachify_js('/js/main.min.js') %> </body> ... 




Connect-cachify output



If the production flag is set to false , connect-cachify will generate three CSS links and three JS links, exactly as in the original HTML above. But if it is set to true , only one tag will be created. The URL in each tag will include the MD5 hash of the resource. This is done to force a refresh in the event of a resource change. That's all.



 ... <head> <title>Dashboard: Hamsters of North America</title> <link rel="stylesheet" type="text/css" href="/v/2abdd020a6/css/dashboard.min.css" /> </head> <body> ... <script type="text/javascript" src="/v/acdd2ab372/js/main.min.js"></script> </body> ... 




Conclusion



Many things that can be done to speed up a site are very simple. Even the most basic things, our three "K" - concatenation, compression and caching - can significantly reduce page load time and improve the user experience. Connect-cachify helps automate the concatenation and caching of applications, but there are many other improvements ahead. In the next article on accelerating the frontend, we will explain how to cache dynamic content using ETag.






Source: https://habr.com/ru/post/196358/



All Articles