Principle of webPack asynchronous loading
This article is featured on Github github.com/Michael-lzg…
Webpack Ensure, known to some as asynchronous loading and others as code cutting, simply exports a js module to a.js file, and when using that module, creates a script object and adds it to the Document. head object. The browser will automatically make a request to the JS file for us, and then write a callback function to let the requested JS file do some business operations.
For example
Requirements: Main. js relies on two JS files: a. js is executed after clicking the aBtn button, and B. JS is executed after clicking the bBtn button.
Webpack.config.js, let’s first write the code for the WebPack-packaged configuration
const path = require('path') // Path processing module
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin') // Introduce the CleanWebpackPlugin plugin
module.exports = {
entry: {
index: path.join(__dirname, '/src/main.js'),},output: {
path: path.join(__dirname, '/dist'),
filename: 'index.js',},plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, '/index.html'),}),new CleanWebpackPlugin(), // The name of the folder to clean],}Copy the code
The index. HTML code is as follows
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="Width = device - width, initial - scale = 1.0" />
<title>webpack</title>
</head>
<body>
<div id="app">
<button id="aBtn">Button A</button>
<button id="bBtn">Button B</button>
</div>
</body>
</html>
Copy the code
The entry file main.js is as follows
import A from './A'
import B from './B'
document.getElementById('aBtn').onclick = function () {
alert(A)
}
document.getElementById('bBtn').onclick = function () {
alert(B)
}
Copy the code
The codes of A.js and B. JS are as follows
// A.js
const A = 'hello A'
module.exports = A
// B.js
const B = 'hello B'
module.exports = B
Copy the code
At this point, we NPM run build the project and only two files are packaged
- index.html
- index.js
As you can see, webPack has packaged the two files that main.js depends on into the same JS file and introduced them in index.html. However, both A.JS and B.JS are executed by clicking the corresponding button. If the user does not click the corresponding button, and the two files are relatively large, will the js file loaded by default on the home page be too large, resulting in a slow rendering of the home page? Is it possible to load the dependent file when the user clicks the button?
Webpack.ensure solves this problem.
Require. Ensure Asynchronous loading
Let’s change main.js to be loaded asynchronously
document.getElementById('aBtn').onclick = function () {
// Load A asynchronously
require.ensure([], function () {
let A = require('./A.js')
alert(A)
})
}
document.getElementById('bBtn').onclick = function () {
// Load b asynchronously
require.ensure([], function () {
let B = require('./B.js')
alert(B)
})
}
Copy the code
At this point, we packaging again, and found that 1.index.js and 2.index.js are two more files. When we open the page, we only introduce the index.js file. When we click button A, we introduce the 1.index.js file, and when we click button B, we introduce the 2.index.js file. This satisfies our need to load on demand.
Ensure this function is a code separation line, indicating that the require in the callback is the one we want to separate out, i.e. Require (‘./ a.js ‘), which separates the a.js into a separate WebPack JS file. The syntax is as follows
require.ensure(dependencies: String[].callback: function(require), chunkName: String)
Copy the code
We open the 1.index.js file and find its code as follows
; (window.webpackJsonp = window.webpackJsonp || []).push([
[1], [,function (o, n) {
o.exports = 'hello A'},]])Copy the code
As you can see from the above code:
- Asynchronously loaded code is stored in a global file
webpackJsonp
In the. webpackJsonp.push
The two parameters are respectively the id of the module to be installed in the file to be loaded asynchronously and the list of modules to be installed in the file to be loaded asynchronously.- When certain conditions are met, the code in the specific module is executed.
Import () is loaded on demand
Webpack4 official documents provide modules to be cut and loaded on demand. Combined with es6’s import() method of loading on demand, the volume of home page packages can be reduced and the request speed of home page can be accelerated. Only other modules can load corresponding JS when needed.
The syntax for import() is simple. This function takes only one argument, the address of the referenced package, and uses a Promise-style callback to get the package loaded. All modules import() in the code are typed into a single package and placed in the chunk storage directory. When the browser runs to this line of code, the resource is automatically requested and loaded asynchronously.
Let’s change the above code to import().
document.getElementById('aBtn').onclick = function () {
// Load A asynchronously
import('./A').then((data) = > {
alert(data.A)
})
}
document.getElementById('bBtn').onclick = function () {
// Load b asynchronously
import('./B').then((data) = > {
alert(data.B)
})
}
Copy the code
The file that comes out of the package is the same as the webpack.ensure method.
The route was loaded lazily. Procedure
Why do you need lazy loading?
For a single-page application like Vue, if there is no lazy route loading, the file packaged with WebPack will be very large, resulting in too much content to be loaded when entering the home page, resulting in a long period of white screen. If lazy route loading is used, the page can be divided and the page can be loaded only when needed. It can effectively share the loading pressure of the home page and reduce the loading time of the home page.
There are three methods for lazy vUE route loading
- Vue Asynchronous component
- The ES6
import()
- The webpack
require.ensure()
Vue Asynchronous component
This approach uses the resolve asynchronous mechanism and uses require instead of import for on-demand loading
export default new Router({
routes: [{path: '/home'.', component: (resolve) => require(['@/components/home'], resolve), }, { path: '/about', '.component: (resolve) = > require(['@/components/about'], resolve),
},
],
})
Copy the code
require.ensure
This pattern can be used to unpack js with webpackChunkName in the parameter.
export default new Router({
routes: [{path: '/home'.component: (resolve) = > require.ensure([], () = > resolve(require('@/components/home')), 'home'),}, {path: '/about'.component: (resolve) = > require.ensure([], () = > resolve(require('@/components/about')), 'about'),},],})Copy the code
ES6 import ()
Vue-router provides a method on the website, which is also understood to work through the Promise’s resolve mechanism. Because the Promise function returns a Promise for the resolve component itself, we can use import to import the component.
export default new Router({
routes: [{path: '/home'.component: () = > import('@/components/home'),}, {path: '/about'.component: () = > import('@/components/home'),},],})Copy the code
Webpack subcontracting strategy
In the process of webPack packaging, it is often the case that single vvendor. Js and app.js files are large, which is the first file loaded by the web page. This will make the loading time too long, which will make the white screen time too long, and affect the user experience. So we need to have a reasonable subcontracting strategy.
CommonsChunkPlugin
Before the Webapck4.x release, we used CommonsChunkPlugin for separation
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor'.minChunks: function (module, count) {
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(path.join(__dirname, './node_modules'= = =))0)}}),new webpack.optimize.CommonsChunkPlugin({
name: 'common'.chunks: 'initial'.minChunks: 2,}),]Copy the code
Let’s separate the following documents and pack them separately
node_modules
Module in the folder- There are 3 entrances
chunk
Shared modules
optimization.splitChunks
The biggest change in WebPack 4 is the abolition of CommonsChunkPlugin and the introduction of optimisation. splitChunks. If your mode is production, then webpack4 will automatically open Code Splitting.
Its built-in code splitting strategy looks like this:
- Whether the new chunk is shared or from
node_modules
The module - Whether the new chunk size is greater than 30kb before compression
- The number of concurrent requests to load the chunk on demand is less than or equal to five
- The number of concurrent requests on the initial page load is less than or equal to three
Although Code Splitting is automatically opened in webpack4, with the project getting bigger and bigger, it often doesn’t meet our needs and we need personalized optimization.
Examples of application
Let’s first find a project with a large optimization space to operate. This is a background management system project, most of the content is developed by 3-4 front-end, usually the development cycle is short, and most people do not have the awareness of optimization, just write good business code to complete the requirements, a long day, resulting in a large package of files, greatly affecting the performance.
We first use Webpack-bundle-Analyzer to analyze the bundled module dependencies and file size to determine the direction of optimization.
Then let’s take a look at the js file that is packaged
When I looked at these two images, I was devastated. The slots are as follows
- After packaging, generate multiple js files of nearly 1M, many of them
vendor.js
The home page must load a large file xlsx.js
There is no need to use such a plugin. A better way to export Excel would be to return the file stream format to the front end for processingechart
和iview
The file is too large, the method introduced by CDN should be used
After the banter, we’ll get down to business. Because there are so many slots, we are better able to verify the feasibility of our optimization method.
Pull away from Echart and iview
Since the echart and iView files are too large, we use WebPack4’s optimization. SplitChunks to separate them into separate files. (In order to better display the optimization, we will remove xlsx.js first)
Vue.config. js is modified as follows:
chainWebpack: config= > {
config.optimization.splitChunks({
chunks: 'all'.cacheGroups: {
vendors: {
name: 'chunk-vendors'.test: /[\\/]node_modules[\\/]/,
priority: 10.chunks: 'initial'
},
iview: {
name: 'chunk-iview'.priority: 20.test: /[\\/]node_modules[\\/]_? iview(.*)/
},
echarts: {
name: 'chunk-echarts'.priority: 20.test: /[\\/]node_modules[\\/]_? echarts(.*)/
},
commons: {
name: 'chunk-commons'.minChunks: 2.priority: 5.chunks: 'initial'.reuseExistingChunk: true}}})},Copy the code
At this point, we use Webpack-bundle-Analyzer to analyze
The packaged JS file
As you can see here, we have successfully isolated echart and iView, and vvendor. Js has been reduced in size accordingly. In addition, we can continue to pull out more third-party modules.
CDN way
Although the third-party module is isolated, it still needs to load such a file of several hundred KB when loading the home page or corresponding route, which is not good for performance optimization. In this case, we can use CDN to introduce such a plug-in or UI component library.
- in
index.html
Introduce the corresponding CDN link
<head>
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/iview/3.5.4/styles/iview.css" />
</head>
<body>
<div id="app"></div>
<script src="https://cdn.bootcss.com/vue/2.6.8/vue.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/iview/3.5.4/iview.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/xlsx/0.16.8/xlsx.mini.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/xlsx/0.16.8/cpexcel.min.js"></script>
</body>
Copy the code
vue.config.js
configurationexternals
configureWebpack: (config) = > {
config.externals = {
vue: 'Vue'.xlsx: 'XLSX'.iview: 'iView'.iView: 'ViewUI',}}Copy the code
- Delete the previous import mode and uninstall the corresponding NPM dependency packages
npm uninstall vue iview echarts xlsx --save
Copy the code
At this point we’re going to look at what happens when you pack
The packaged JS file
well done ! At this time, there is basically no large file packaged out, and the vvendor. Js required for home page loading is only dozens of KB. Moreover, we can further optimize by introducing some modules of vUE family bucket through CDN, such as Vue-router, VUex, axios, etc. The performance of the page, especially the home page loading, is greatly optimized.