From browserify, Grunt and gulp to rollup and Webpack, many excellent packaging tools have emerged, and webPack is undoubtedly the most popular one at present. Many popular frameworks and tool libraries choose webPack as a packaging tool. Webpack is therefore a good choice as a packaging tool for development. In the recent project development, I also used Webpack, which also encountered a lot of optimization problems, here summarize some details and methods of webpack optimization.

First of all, vue family bucket was used in this project, and the default configuration generated by VUE-CLI was directly used for webpack configuration. After the project was packaged, we found that the generated vvendor. Js file was extremely large.

Locate large modules

In order to optimize the packaging volume, we first need to find large modules. In this case, we can use the Webpack plugin Webpack-bundle-Analyzer to view the volume structure comparison of the entire project. It is presented in the form of Treemap, which is very intuitive, and there are some specific interaction forms. You can see all the dependencies you are using in your project, as well as visually see the percentage of each module’s volume in the overall project.

The plugin can be installed directly via NPM install webpack-bundle-Analyzer –save-dev and plugins in the webpack configuration information: [new BundleAnalyzerPlugin()] The bundleAnalyzerReport configuration is installed by default but not enabled in the uE-CLI configuration file config/index.js. The default bundleAnalyzerReport configuration is process.env.npm_config_report. It is recommended to add a line of “analyz”: “npM_config_report =true NPM run build” to the scripts of package.json, so that every time you want to enable the plug-in, only NPM run Analyze is required.

Extract common modules

For WebPack, two of its core modular packaging features are support for a wide range of module types, including TypeScript, CoffeeScript, SASS, Stylus, and configuration control of the granularity of the packaged files, as discussed below.

In development, we often extract all the dependency libraries separately, rather than mixing them with our project code. In this case, we use a plugin that comes with WebPack, CommonsChunkPlugin. As the name suggests, it is a plug-in that extracts public modules. It can be seen from its documentation that an object can be passed as a parameter, and the three parameters commonly used in use are:

  • name(names)
  • minChunks
  • chunks

Name is the name of the last packaged file, and if names is used, an array of strings must be passed in. MinChunks, if a number is passed in, means that a module will be packed if it is referred to by other modules more than this number. If a function is passed in, its return value must be a Boolean to indicate whether the module should be packaged into a public module. Chunks specify an array of strings, and if this parameter is set, only the common submodules specified in the chunks will be extracted during packaging.

Here are a few examples of how this plug-in works.

Suppose there are two modules, chunk1.js and chunk2.js, and two project files, A.js and b.js. The structure is roughly as follows:

// a.js
require('./chunk1');
require('./chunk2');
require('jquery');
// b.js
require('./chunk1');
require('./chunk2');
require('vue');
// WebPack is configured as follows
module.exports = {
  entry: {
    main: './main.js'.main1: './main1.js'.jquery: ["jquery"].vue: ["vue"]},output: {      
    path: __dirname + '/dist'.filename: '[name].js' 
  },   
  plugins: [  
    new CommonsChunkPlugin({   
      name: ["common"."jquery"."vue"."load"].minChunks:2}})];Copy the code

The final packing result is: jquery is packed into jquery.js, vue is packed into vue.js, common.js is packed into public modules (chunk1 and chunk2). When using this package, modules that meet minChunks are packed in the first block of the Name array, and then the modules that follow the array are packed in sequence, first from the entry, and then an empty block is generated if there is none. The last block in the Name array packages the WebPack Runtime code, which must be loaded before it is used.

Now take a look at the vuE-CLI configuration file for this plug-in:

new webpack.optimize.CommonsChunkPlugin({
  name: 'vendor'.minChunks: function (module, count) {
    // Extract all the dependent modules in node_modules into the vendor file
    return (
      module.resource &&
      /\.js$/.test(module.resource) &&
      module.resource.indexOf(
        path.join(__dirname, '.. /node_modules'= = =))0)}}),// WebPack generates runtime code when using CommonsChunkPlugin and packages it into vendor.
// So that even without changing the Vendor code, the Runtime changes each time the package is packaged, causing the Vendor hash to change, here
// Remove the separate Runtime code to solve this problem
new webpack.optimize.CommonsChunkPlugin({
  name: 'manifest'.chunks: ['vendor']}),Copy the code

Remove unnecessary files

In the project I managed to locate several large libraries, one of which was the date-processing library moment.js. About why a date processing capabilities, the library will take up so much of the volume, take a closer look at found when refer to the library, all of the locale files have been introduced, and the files even in volume accounts for most of the entire library, therefore when webpack packaged to remove this part will make less packaging file size.

Webpack comes with two libraries to do this:

  • IgnorePlugin
  • ContextReplacementPlugin

IgnorePlugin can be used as follows:

// Plug-in configuration
plugins: [
  // Ignore all locale files in moment.js
  new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
],
// The usage mode
const moment = require('moment');
// Import the zh-cn locale file
require('moment/locale/zh-cn');
moment.locale('zh-cn');
Copy the code

The ContextReplacementPlugin can be used as follows:

// Plug-in configuration
plugins: [
  // Only locale zh-cn files are loaded
  new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /zh-cn/),
],
// The usage mode
const moment = require('moment');
moment.locale('zh-cn');
Copy the code

In both ways, moment.js can be reduced roughly to a quarter of its original size.

Modular introduction

I used loDash, a popular tool library, for my project, but found that it took up a lot of volume when I was trying to locate the code. If you think about it, we tend to use only a few of these libraries, but we introduce the whole library. So this can be further optimized to only reference what is needed.

import {chain, cloneDeep} from 'lodash';
// Can be written as
import chain from 'lodash/chain';
import cloneDeep from 'lodash/cloneDeep';
Copy the code

This allows us to pack only part of the functionality we need.

Reference by CDN

For libraries that are necessary but cannot be better volumetric optimized, you can try to reduce the size of the packaged files by introducing them externally. Using this method, you only need to find external links to the libraries you want to reference at the CDN site and simply configure webPack.

// Add a script reference to the HTML<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
Copy the code
// Externals' key is the package name that needs to be required, and value is the globally registered variable name of the library introduced via script
externals: {
  jquery: 'jQuery'
}
// How to use it
require('jquery')
Copy the code

throughDLLPluginDLLReferencePluginSplit the file

If the project is too large, the packaging time will be quite long, and if frequent updates come online, the code will be constantly compiled and packaged, wasting a lot of time. In this case, we can compile and package the infrequently updated frameworks and libraries (such as vue.js, etc.) separately, so that we only need to compile and package our development files every time development goes live, which greatly saves unnecessary packaging time. This method requires the cooperation of DLLPlugin and DLLReferencePlugin.

DLLPlugin

To use this plug-in, we need to create a separate configuration file, named webpack.dll.config.js, with the following configuration:

module.exports = {
  entry: {
    lib: ['vue'.'vuex'.'vue-resource'.'vue-router']},output: {
    path: path.resolve(__dirname, '.. /dist'.'dll'),
    filename: '[name].js'.publicPath: process.env.NODE_ENV === 'production'
      ? config.build.assetsPublicPath
      : config.dev.assetsPublicPath,
    library: '[name]_library'
  },
  plugins: [
    new webpack.DefinePlugin({
      'process.env': '"production"'
    }),
    /** * path: manifest.json output file path * name: DLL object name, same as output.library */ 
    new webpack.DllPlugin({
      context: __dirname,
      path: path.resolve(__dirname, '.. /dist/dll'.'lib.manifest.json'),
      name: '[name]_library'}})]Copy the code

A few points to note here:

  1. entrySpecify all modules to be packaged separately
  2. outputthelibraryProperty to expose DLL packages
  3. DLLPluginIn the configuration ofpathIndicate themanifest.jsonFile generation path,nameExposes the function name of the DLL

Running this configuration file generates the package file and manifest.json file.

DLLReferencePlugin

For the configuration of this plug-in, there is no need to write a separate configuration file as above, just add the following code to the production configuration file:

new webpack.DllReferencePlugin({       
  context: __dirname,                  // The path must be the same as that configured for the DLL
  manifest: require('.. /dist/dll/lib.manifest.json') // The manifest location
}),
Copy the code

Then I run WebPack and find that the packaging speed is greatly improved without having to compile and package the dependent libraries every time the code is updated.

other

For the packaging optimization of Webpack, I have summarized some of the above methods. In order to make the page load faster and have a better user experience, we can not only optimize from the packaging, but also other aspects of optimization. Here I also briefly mention the methods I used.

Enable Gzip compression

Enabling GZIP compression can reduce the amount and time of HTTP data transmission, thus reducing the response time of client requests. Because of the reduced request time, the page loading speed is improved, and the rendering speed is faster, which greatly improves user experience. Since almost all major browsers support Gzip compression, you only need to configure the server. We will not specify how to configure the server here.

Compressing obfuscated code

You can use tools such as UglifyJS to compress js code, remove unnecessary Spaces, comments, console information, etc., and effectively reduce the size of the code.

conclusion

This paper ends here, mainly on the packaging optimization part of Webpack to do some explanation, of course, the ability and time is limited, only part of the study method, there may be other more optimization methods, whether from the compilation package volume or speed can have a better optimization. After contact with Webpack for a period of time, I found that it was too complicated as a packaging tool. No matter from the official documents at the beginning or to the new advanced features, it was difficult to fully master them, and I still needed to practice and study them in depth.