Searching for the correct way to share site materials using Webpack
Finding the best way to organize materials for web projects can be a challenge. There are many different scenarios for users working with projects, many technologies and other factors that need to be taken into account.
The author of the material, the translation of which we are publishing today, says that he wants to tell us about everything you need to know in order to properly prepare web project materials for work. Firstly, it will be how to choose such a strategy for sharing the site files, which is best suited for a particular project and for its users. Secondly, the means of implementing the chosen strategy will be considered.
glossary Webpack , there are two strategies for separating files. This is a bundle splitting and code splitting. These terms may seem interchangeable, but they are not.
Bundle separation is a technique for splitting large bundles into several parts, which are smaller files. Such files, in any case, as well as when working with a single bandle, will be downloaded by all users of the site. The strength of this technique is to improve the use of browser-based caching mechanisms.
Separation of code is an approach that involves dynamic loading of code as the need arises in it. This leads to the fact that the user downloads only the code that he needs to work with some part of the site at a certain point in time.
Separation of code, it seems, looks much more interesting than the separation of a bandle. And, in fact, there is a feeling that in many articles on our topic, the main attention is paid to the separation of code, this technique is considered as the only worthwhile way to optimize the materials of the sites.
However, I would like to say that for many sites the first strategy is much more valuable - the separation of bands. And, perhaps, from its implementation are able to win virtually all web projects.
Let's talk about this in more detail.
Separation of bands
At the heart of the technique for banding is a very simple idea. If you have one huge file and you change one single line of code in it, the regular user will have to download the entire file when the site is visited again. However, if you divide this file into two files, then the same user will need to download only the one to which the changes were made, and the second file will be taken from the browser cache.
It's worth noting that since the optimization of site materials by splitting the bundles is based on caching, users who visit the site for the first time will have to download all the materials in any case, so there is no difference - whether these materials will be presented as one file or in the form of several .
It seems to me that too much talk about the performance of web projects is dedicated to users who visit the site for the first time. Perhaps this is partly because of the importance of the first impression the project will make on the user, and the fact that the amount of data sent to users on the first visit to the site is simple and convenient to measure.
When it comes to regular visitors, it may be difficult to measure the impact on them of the techniques used to optimize materials. But we just have to know about the consequences of such optimization.
To analyze such things, you need something like a spreadsheet. We will also need to create a strict list of conditions in which we can test each of the caching strategies under investigation.
Here is a scenario that is suitable for the general description given in the previous paragraph:
Alisa visits our site once a week for 10 weeks.
We update the site once a week.
We, every week, update the page with the list of goods (product list).
In addition, we have a page with detailed information about the product (product details), but we are not working on it yet.
In the fifth week, we add a new npm package to the project materials.
On the eighth week, we update one of the npm packages already used in the project.
There are people like me who will try to make such a scenario as realistic as possible. But you do not need to do this. The real scenario here has no special significance. Why this is so - we will find out soon.
▍Source conditions
Suppose the total size of our jаvascript package is a considerable 400 Kb and we, in the current conditions, transfer it all to the user in the form of a single file
main.js. We have a Webpack configuration, which, in general terms, is similar to the following (something that does not apply to our conversation, I removed it from there):
const path = require ('path');
module.exports = {
entry: path.resolve (__ dirname, 'src /index.js'),
output: {
path: path.resolve (__ dirname, 'dist'),
filename: '[name].[contenthash].js',
},
};};
Webpack calls the resulting file
main.jsIf there is a single record in the configuration
entry.
If you are not very good at representing work with the cache, consider that every time I write here
main.js, I actually mean something like
main.xMePWxHo.js. An insane sequence of characters is the hash of the contents of the file, what is called
in the configuration. contenthash. Using this approach leads to the fact that when the code changes, the file names change, which forces the browser to load new files.
In accordance with the scenario described above, when we, every week, make some changes to the site code, line
contenthashpackage is changing. As a result, visiting our site on a weekly basis, Alice is forced to download a new 400 KB file.
If we make a pretty tablet (with a useless as yet string of totals) that contains data on the weekly amount of data uploaded to this file, then we get the following.
The amount of data uploaded by the user
As a result, it turns out that the user, in 10 weeks, downloaded ??? MB of code. This indicator can be improved.
▍ Separation of third-party packages from the main code
We divide the big package into two parts. Our own code will be in the file
main.js, and the third-party code in the file
vendor.js. This is not difficult to do, the following Webpack configuration will help us in this:
const path = require ('path');
module.exports = {
entry: path.resolve (__ dirname, 'src /index.js'),
output: {
path: path.resolve (__ dirname, 'dist'),
filename: '[name].[contenthash].js',
},
optimization: {
splitChunks: {
chunks: 'all',
},
},
};};
Webpack 4 tries to make life as easy as possible for the developer, so he does everything he can, and does not require that he be told how to break the bundles into pieces.
This kind of automatic behavior of the program leads to a few delights, like: "Well, what a delight this Webpack", and to a multitude of questions in the spirit: "And what is this done with my bandlah?".
In any case, the addition of
to the configuration. optimization.splitChunks.chunks = 'all'tells Webpack that we need him to take everything from
node_modulesand put it in the
file. vendors ~ main.js.
After we conducted such a basic separation of the bandle, Alice, who regularly visits our site on a weekly basis, will download the file
at each visit. main.jsthe size of 200 KB. And here's the file
vendor.jsit will load only three times. It will happen during visits to the first, fifth and eighth weeks. Here is the corresponding table, in which, by the will of fate, the file sizes are
main.jsand
vendor.jsin the first four weeks coincide and equal 200 Kb.
The amount of data uploaded by the user
As a result, it turns out that the amount of data uploaded by the user in 10 weeks was ??? MB. That is, in comparison with what was before the separation of the bundle, the volume decreased by 36%. Not such a bad result, achieved by adding a few lines to the configuration file. By the way, before you read on, do the same in your project. And if you need to upgrade from Webpack 3 to 4 - do it and do not worry, because the process is fairly simple and still free.
It seems to me that the improvement considered here looks somewhat abstract, since it is stretched for 10 weeks. However, if you consider the amount of data sent to a loyal user, then this is an honest reduction of this volume by 36%. This is a very good result, but it can be improved.
▍Dividing packages into separate files
File
vendor.jssuffers from the same problem as the original
main.js. It consists in the fact that changing any package included in this file leads to the need to repeatedly upload the entire file to the permanent user.
Why do not we create separate files for each npm package? It is not difficult to do this, so let's expand our
react,
lodash,
redux,
moment, and the like, on separate files. The following Webpack configuration will help us with this:
const path = require ('path');
const webpack = require ('webpack');
module.exports = {
entry: path.resolve (__ dirname, 'src /index
],
output: {
path: path.resolve (__ dirname, 'dist'),
filename: '[name].[contenthash].js',
}, ('@', '')}`;
},
},
},
},
},
};};
In documentation you can find an excellent explanation of the constructions used here, but I will still devote a little time to a story about some things, because in order to correctly use them, it took me a long time.
- In Webpack there are quite reasonable standard settings, which, in fact, are not so reasonable. For example, the maximum number of output files is set to ? the minimum file size is 30 KB (that is, smaller files will be merged). I redefined this.
cacheGroupsIs the place where we set the rules for how Webpack should group the data in the output files. I have one group here,
vendor, which will be used for any module downloaded from
node_modules. Usually the name (
name) For the output file is specified as a string. But I asked
nameas a function that will be called for each processed file. Then I take the package name from the path to the module. As a result, we get one file for each package. For example,
npm.react-dom.899sadfhj4.js.
- Package names, in order for them to be published in npm, should be suitable for use in URL , so we do not need to perform the
operation. encodeURIfor the names
packageName. However, I ran into the problem, which is that the .NET server refuses to work with files whose names contain the symbol
@(such names are used for packets with a specified domain name, so-called scoped packages), a poetI, in the corresponding code fragment, get rid of similar symbols.
The above configuration Webpack is good in that it can be configured once, and then forget about it. It does not require access to specific packages by name, so, after its creation, it, even when changing the composition of packages, remains relevant.
Alice, our regular visitor, still re-downloads the 200-kilobyte
every week. main.js, and the first time you visit the site, you have to download 200 Kb of npm-packages, but it does not have to download the same packages twice.
Below is a new version of the table with information about the volumes of weekly data downloads. By a strange coincidence, the size of each file with npm-packages is 20 KB.
The amount of data uploaded by the user
Now the amount of data downloaded for 10 weeks is ??? MB. This means that we have improved the benchmark by 44%. The result is already quite decent, but the question arises whether it is possible to do so in order to achieve a result exceeding 50%. If this happens - it will be just great.
▍Dividing the application code into fragments
Let's return to the file
main.js, which unfortunate Alice has to load constantly.
Above I said that on our website there are two separate sections. The first is a list of products, the second is a page with detailed information about the product. The code size, unique for each of them, is 25 KB (and 150 KB of code is applied both there and there).
The page with the information about the product is not subject to changes, as we have already brought it to perfection. Therefore, if we select its code in a separate file, this file, most of the time with the site, will be loaded into the browser from the cache.
In addition, as it turned out, we have a huge built-in SVG file used for icon rendering, which weighs as much as 25 KB and changes rarely.
With this you need to do something.
We manually created several input points, telling Webpack that it needs to create a separate file for each of these entities.
module.exports = {
entry: {
main: path.resolve (__ dirname, 'src /index.js'),
ProductList: path.resolve (__ dirname, 'src /ProductList /ProductList.js'),
ProductPage: path.resolve (__ dirname, 'src /ProductPage /ProductPage.js'),
Icon: path.resolve (__ dirname, 'src /Icon /Icon.js'),
},
output: {
path: path.resolve (__ dirname, 'dist'),
filename: '[name].[contenthash:8] ('@', '')}`;
},
},
},
},
},
};};
Hardworking Webpack, in addition, will create files for what is common, for example, in
ProductListand
ProductPage, that is, there will not be a duplicate code here.
What we just did will allow Alice to save almost 50 kb of traffic almost every week. Please note that we have edited the file describing the icons in the sixth week. Here is our traditional table.
The amount of data uploaded by the user
Now, in just ten weeks, only ??? MB of data has been downloaded. This means that traffic savings made up an impressive 56%. In accordance with our theoretical scenario, a permanent user will always work with this level of savings.
All this is done at the expense of changes made to the configuration of Webpack. We did not change the code of applications to achieve such results.
Above, I said that the specific scenario for conducting such a test, in fact, does not play a special role. It is said that, regardless of the script used, the conclusion from everything that we talked about will be the same: splitting the application into small files that make sense in applying to its architecture, allows you to reduce the amount of data on the site, downloaded by its regular users.
Very soon we will turn to the discussion of code splitting, but first I would like to answer three questions that you are probably thinking about now.
▍Question No.1. Is the need to perform multiple requests do not harm the speed of the site's download?
This question can be given a simple short answer: "No, it does not harm." This situation poured into the problem in the old days when the HTTP /1.1 protocol was running, and when using HTTP /2 it is no longer relevant.
Although, it should be noted that in this material published in 201? and in this article Khan Academy of 2015 concludes that even with the use of HTTP /? the use of too many files slows down the download. But in both of these materials "too much" means "several hundred". Therefore, it is worth remembering that if you have to work with hundreds of files, the speed of loading them can be affected by restrictions on parallel processing of data.
If interested, support for HTTP /2 is available in IE 11 in Windows 10. In addition, I conducted a comprehensive study among those who use older systems. They unanimously stated that their speed of downloading web sites is especially not concerned.
▍Question No.2. In Webpack-bands there is an auxiliary code. Does it create an additional load on the system?
Yes it is.
▍Question No.3. When working with a lot of small files, their compression level gets worse, does not it?
Yes, that's also true. In fact, I would like to say this:
- More files means more support for the Webpack code.
- More files mean less compression.
Let's figure this out in order to understand how bad it is.
I just ran a test, during which the code from a file of 190 KB was divided into 19 parts. This added about 2% to the amount of data sent to the browser.
As a result, it turns out that when a user visits the site for the first time, the user will download 2% more data, and in the subsequent - 60% less, and this will continue very, very long.
So is it worth worrying about? No, do not.
When I compared a system using 1 file and a system with 19 files, I tested it using various protocols, including HTTP /1.1. The table below strongly supports the idea that more files mean better.
Data on working with 2 versions of the site hosted on a static hosting Firebase, the code of which is 190 KB in size, but, in the first case, it is packed into 1 file, and in the second one - broken down into 19
When working in 3G and 4G-networks to download a version of the site with 19 files, it took 30% less time than downloading a site with one file.
There is a lot of noise in the data presented in the table. For example, one download session for 4G (Run 2 in the table) took 646 ms, another (Run 4) - 1116 ms, which is 73% longer. Therefore, there is a feeling that to say that HTTP /2 is "30% faster" is somewhat unfair.
I created this table in order to see what gives the use of HTTP /2. But, in fact, the only thing that can be said here is that the use of HTTP /2 probably does not have a significant effect on page loading.
The real surprise was the last two lines in this table. Here are the results for not the newest version of Windows with IE11 and HTTP /1.1. I, if I tried to predict the test results in advance, would say that this configuration will load materials much slower than others. True, it used a very fast network connection, and I, for similar tests, probably should use something slower.
And now I will tell you a story. I, to explore my site on a very old system, downloaded the Windows 7 virtual machine from site Microsoft. There was installed IE? which I decided to upgrade to IE9. For this, I went to the Microsoft page for downloading IE 9. But I could not do it.
That's bad luck
By the way, if we talk about HTTP /? I want to note that this protocol is integrated into Node.js. If you want to experiment - you can use the small I wrote. HTTP /2 server with support for response cache, gzip and brotli.
Perhaps, I told everything that I wanted about the technique of bandlocks separation. I think that the only drawback to this approach, when using which users have to download a lot of files, in fact, is not such a "minus".
Now let's talk about code splitting.
Separation of the code
The main idea of the code sharing technique is: "Do not download unnecessary code". I was told that using this approach makes sense only for some sites.
I prefer, when it comes to code sharing, to use the 20/20 rule, which I just formulated. If there is any part of the site that only 20% of users visit, and its functionality provides more than 20% of the site's jаvascript code, then this code needs to be downloaded only on request.
These, of course, are not absolute figures, they can be adjusted to a specific situation, and in reality there are much more complex scenarios than the above. The most important thing here is balance, and it's perfectly normal not to use code sharing at all, if it does not make sense for your site.
▍Identiate or not?
How to find the answer to the question of whether you need a code division or not? Suppose you have an online store and you are thinking about whether you need to separate the code from the rest of the code, which is used to accept payment from customers, since only 30% of visitors have something to buy from you.
What can I say? Firstly - you should work on filling the store and sell something that will be interesting to more visitors to the site. Secondly, you need to understand how much code is completely unique for the site section where payment is accepted. Since before the "code splitting" you should always perform "partitioning the bandel", and you, I hope, do so, then you probably already know about the size of the code we are interested in.
Perhaps this code may be less than you think, therefore, before you rejoice in the new opportunity to optimize the site, it's all worth thinking about. If you, for example, have a React site, then the repository, the rerunsers, the routing system, the actions, will be shared by all parts of the site. Unique code for different parts of the site code will, basically, be represented by components and auxiliary functions for them.
So, you found out that the completely unique code of the section of the site used to pay for purchases, takes 7 KB. The rest of the site code is 300 KB. In a similar situation, I would not be involved in code sharing for several reasons:
- If you download these 7 KB in advance, the site will not slow down. Remember that the files are loaded in parallel and try to measure the difference needed to download 300 KB and 307 KB of code.
- If you download this code later, then the user will have to wait after clicking on the "Pay" button. And this is the very moment when you need everything to go as smoothly as possibleIt is possible.
- Separation of code requires changes to the application. In the code, in those places where everything was done synchronously, asynchronous logic appears. Of course, there are no cosmic complications in such code transformations, but it is still an additional amount of work, which, it seems to me, should be carried out for the sake of a tangible improvement in the user's experience with working with the site.
Strictly speaking, the reasons for which the division of the code may not suit you, we discussed.
And now let's look at a couple of examples of using this technology.
▍Polyfilles
I'm starting with this example, since what we'll look at here is simply implemented and applicable to most sites.
I use on my site many useful pieces in the form of polyphiles. So I have a file in which it all connects. It consists of the following eight lines:
require ('whatwg-fetch');
require ('intl');
require ('url-polyfill');
require ('core-js /web /dom-collections');
require ('core-js /es6 /map');
require ('core-js /es6 /string');
require ('core-js /es6 /array');
require ('core-js /es6 /object');
This file is imported at the very beginning of the
file code. index.js, which is the entry point to the application:
import './polyfills';
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App/App';
import './index.css';
const render = () => {
ReactDOM.render ( dynamic-import . In addition, as stated in the documentation for Webpack, the command import () uses promises, so the polyphyll for this feature should be loaded separately from other polyphiles.
Like I said, it's very simple. Let's consider now an example more difficult.
Let's return to the example with Alice. Suppose now on our site there is a closed section for store administrators, where they can enter in order to manage their goods.
In this section there are many great features, a bunch of graphs and a hefty npm-library for working with them. Since the site already uses the separation of bands, I can understand that all this takes more than 100 KB.
Currently, the site's routing settings are such that when the user views the URL
/admin, it renders
documentation React the code division.
Results of
I guess I told everything I wanted (although, it should be noted, we did not talk about CSS here). Let's sum up the results:
If users of your site visit it more than once - divide the code into many small files.
If your site has large sections that most users do not visit - load their code dynamically.
Dear readers! Do you use bandleading and code separation in your projects?
It may be interesting
17-09-2018, 12:31Publication Date
- Views: 318