background

Because the company’s front-end project takes an absurdly long time in packaging and construction, about 5min for cold startup, 5min for local construction and packaging, and 16~20min for the whole process of online Tencent cloud Docker construction and packaging. Therefore, not only greatly affects the development efficiency, but also greatly delays the time for test delivery.

Optimization idea

  • Understand the project’s current WebPack configuration
  • Build-dependent optimization
  • Optimization of package volume correlation
  • Docker-related optimizations

Quantitative tools

speed-measure-webpack-plugin

Introduction:

speed-measure-webpack-plugin npm

The first step to optimising your webpack build speed, is to know where to focus your attention. This plugin measures your webpack build speed, giving an output like this:

Through the analysis of SMP output, we can clearly understand the time spent by loader and plugin in each stage of WebPack construction.

Usage:

# Yarn
yarn add -D speed-measure-webpack-plugin

const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
module.exports = {
  chainWebpack: config= > {
    config
      .plugin('speed-measure-webpack-plugin')
      .use(SpeedMeasurePlugin)
      .end()
  }
}
Copy the code

Other uses in this project will report error, but the above usage does not seem to distinguish between the use of plugin and Loader, and there is no information about the use of other plugins, confusing.

webpack-bundle-analyzer

Introduction:

Webpack-bundle-analyzer NPM is used to analyze webAPCK build packed files, such as subcontract, occupied volume and other parameters.

Usage:

# Yarn
yarn add -D webpack-bundle-analyzer

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
}
Copy the code

But in vue-CLI you can call the report command directly, and then go to the dist package directory and open report.html.

vue-cli-service build --report
or
vue-cli-service build --report-json
Copy the code

View the current Webpack configuration of VUE-CLI

introduce

Vue-cli scaffolding has a lot of the default behavior of WebPack, so we need to know what webPack is currently configured for vue-CLI based projects so we can analyze and optimize accordingly.

Vue-cli-service exposes the inspect command for reviewing the parsed Webpack configuration. The global vue executable also provides the inspect command, which simply proxies vue-cli-service inspect into your project.

Usage:

Vue inspect --mode production > output.js vue inspect --mode production > output.jsCopy the code

NPM install -g vue-cli NPM install -g vue-cli

Build-dependent optimization

hard-source-webpack-plugin

introduce

hard-source-webpack-plugin npm

HardSourceWebpackPlugin is a plugin for webpack to provide an intermediate caching step for modules. In order to see results, you’ll need to run webpack twice with this plugin: the first build will take the normal amount of time. The second build will be signficantly faster.

Caches are generated for the project when it is started, and if there are no package or other changes to the project, the next time you don’t have to spend time rebuilding, you just reuse the cache.

Usage:

#yarn
yarn add -D hard-source-webpack-plugin

const HardSourceWebpackPlugin = require('hard-source-webpack-plugin')
module.exports = {
	configureWebpack: config= > {
  	config.plugin.push(
    	// Provide an intermediate cache for the module. The cache path is node_modules/. Cache /hard-source
      // solve Configuration changes are not being detected
      new HardSourceWebpackPlugin({
        root: process.cwd(),
        directories: [].environmentHash: {
          root: process.cwd(),
          directories: [].files: ['package.json'.'yarn.lock'.'vue.config.js']}})// The main reason for configuring files is to solve the problem that the cache does not take effect due to configuration updates. Plugin will rebuild part of the cache after configuration changes)}}Copy the code

Note:

Could not freeze : Cannot read property ‘hash’ of undefined after node_modules/. Cache is deleted and the project is restarted.

Narrow the file retrieval parsing scope

To avoid useless retrieval and recursive traversal, you can use alias to specify the module to reference, and noParse does not parse third-party dependencies that do not depend on native code.

// Define the getAliasPath method to convert relative paths to absolute paths
const getAliasPath = dir= > join(__dirname, dir)
module.exports = {
	configureWebpack: config= > {
    config.module.noParse = /^(vu|vue-router|vuex|vuex-router-sync|lodash|echarts|axios|element-ui)$/
  }
  chainWebpack: config= > {
    // Add an alias
    config.resolve.alias
      .set(The '@', getAliasPath('src'))
      .set('assets', getAliasPath('src/assets'))
      .set('utils', getAliasPath('src/utils'))
      .set('views', getAliasPath('src/views'))
      .set('components', getAliasPath('src/components'))}// EsLint is disabled in production
  lintOnSave:! process.env.NODE_ENV ! = ='production',}Copy the code

Reduce packing volume

Image-webpack-plugin Image compression

The image pixel requirements are not very extreme, this compression can still be used, the compression rate looks like the naked eye is not much difference. Notice here that I did not compress SVG because when compressed SVG is packaged as Base64 at build time, the resulting Base64 will have problems and be inaccessible.

module.exports = {
  chainWebpack: config= > {
   // Compress the image
    config.module
      .rule('images')
      .test(/\.(png|jpe? g|gif)(\? . *)? $/)
      .use('image-webpack-loader')
      .loader('image-webpack-loader')
      .options({ bypassOnDebug: true })
      .end()
	}
}
Copy the code

UglifyJsPlugin Deletes the console comment

UglifyJsPlugin is used to compress JS files and reduce the size of JS files. It will slow down the compilation of WebPack. It is recommended that you turn it off in development and turn it on in production. It is recommended to standardize the code of team members to solve.

#yarn
yarn add -D uglifyjs-webpack-plugin

const UglifyJsPlugin = require('uglifyjs-webpack-plugin')

module.exports = {
	configureWebpack: config= > {
  	config.plugin.push(
    	new UglifyJsPlugin({
        uglifyOptions: {
          // Delete comments
          output: {
            comments: false
          },
          // Delete console debugger delete warning
          compress: {
            warnings: false.drop_console: true.//console
            drop_debugger: false.pure_funcs: ['console.log'] / / remove the console}},sourceMap: false.parallel: true // Use multiple processes running in parallel to speed up builds. Default number of concurrent runs: os.cpus().length-1.}}}))Copy the code

DLL dynamic link library

This plugin creates a DLL -only bundle(DLL-only -bundle) in an additional separate Webpack setup. The plugin generates a file named manifest.json, which is used to map the DLLReferencePlugin to related dependencies.

This can be simply interpreted as separating dependencies from the project’s bundle and loading them with requests through mapping relationships. I think once it’s broken down it’s not parsed in the project, so it helps build, volume.

Configuring the DllPlugin can be divided into the following steps:

  1. Create a new webpack.dll.config.js file (any other name can be used) and configure the plug-in to be split.
  2. Create a new command in the package.json file to package specifically,"build:dll":"webpack --config webpack.dll.config.js"; Run the command.
  3. Configure it in the vue.config.js fileDllReferencePlugin, mainly reference DLLS to dependencies that need to be precompiled;
  4. Manually introduce split bundles in index. HTML (preferably in CDN)

Installation:

#yarn 
yarn add webpack-cli@^3.23. add-asset-html-webpack-plugin@^3.13. clean-webpack-plugin@^1.01. --dev
Copy the code
// webpack.dll.config.js
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require('path')
const webpack = require('webpack')
const CleanWebpackPlugin = require('clean-webpack-plugin')
// Directory for storing DLL files
const dllPath = 'public/vendor'

module.exports = {
  entry: {
    // The library file to extract
    vendor: ['vue'.'vue-router'.'vuex'].utils: ['axios'.'lodash']},output: {
    path: path.join(__dirname, dllPath),
    filename: '[name].dll.js'.// Global variable name exposed in vendor.dll.js
    // Keep the same name as webpack.dllPlugin
    library: '[name]_[hash]'
  },
  plugins: [
    // Clear the previous DLL file
    new CleanWebpackPlugin(['*. *'] and {root: path.join(__dirname, dllPath)
    }),
    // manifest.json describes what the dynamic link library contains
    new webpack.DllPlugin({
      path: path.join(__dirname, dllPath, '[name]-manifest.json'),
      // keep the same name as output.library
      name: '[name]_[hash]'.context: process.cwd()
    })
  ]
12

Copy the code

Used in the vue.config.js plugin

config.plugin.push(
  new DllReferencePlugin({
    context: process.cwd(),
    manifest: require('./public/vendor/vendor-manifest.json')}),new DllReferencePlugin({
    context: process.cwd(),
    manifest: require('./public/vendor/utils-manifest.json')}),// Inject the DLL into the generated HTML template
    new AddAssetHtmlPlugin({
    // DLL file location
    filepath: getPath('./public/vendor/*.js'),
    // DLL reference path
    publicPath: './vendor'.// DLL final output directory
    outputPath: './vendor'}))Copy the code

SplitChunks split code

split-chunks-plugin webpack

  • Chunks: indicates which code needs to be optimized. There are three optional values: Initial (initial chunks), async(chunks loaded on demand), and all(chunks loaded on demand). The default is Async
  • MinSize: indicates the minimum module size before compression. The default value is 30000
  • MinChunks: indicates the number of times to be referenced. The default value is 1
  • MaxAsyncRequests: Maximum number of parallel requests that can be loaded on demand (default: 5)
  • MaxInitialRequests: The maximum number of parallel requests for an entry. Default is 3
  • AutomaticNameDelimiter: Named connector
  • Name: indicates the name of the split block. By default, the block name and hash value are automatically generated
  • CacheGroups: cacheGroups. The attributes of the cache group include test, Priority, and reuseExistingChunk
    • Test: Used to control which modules are matched by this cache group
    • Priority: priority of cache group packaging
    • ReuseExistingChunk: No new block is created if the current block contains a module that already exists
config.optimization = {
  runtimeChunk: 'single'.splitChunks: {
    chunks: 'all'.// Indicates which code needs to be optimized. There are three optional values: initial(initial block), async(load on demand block), and all(all block). The default is async
    maxInitialRequests: Infinity.// Maximum number of parallel requests when loading on demand. Default is 5
    minSize: 30000.// Dependency packages exceeding 300000bits will be packaged separately
    / / cache group
    // priority: priority of cache group packaging
    // minChunks: indicates the number of times to be referenced. The default value is 1
    cacheGroups: {
      // Public module
      commons: {
        name: 'chunk-commons'.test: resolve('src'), // can customize your rules
        minSize: 100.// The size exceeds 100 bytes
        minChunks: 3.// minimum common number
        priority: 5.reuseExistingChunk: true
      },
      // Third-party libraries
      libs: {
        name: 'chunk-libs'.test: /[\\/]node_modules[\\/]/,
        priority: 10.chunks: 'initial'.// only package third parties that are initially dependent
        reuseExistingChunk: true.enforce: true
      },
      echarts: {
        name: 'chunk-echarts'.test: /[\\/]node_modules[\\/]echarts[\\/]/,
        chunks: 'all'.priority: 12.reuseExistingChunk: true.enforce: true}}}}Copy the code

conclusion

The above are some small optimizations I have made in the company’s project, which may not be applicable to other projects or even not optimal in use, but they are still helpful to this project to a large extent. For more webpack optimization ideas, I attached a mind map. (For learning only, record 📝)

other

Vue CLI

Vue-configurewebpack Settings in the CLI

How do I deploy a front-end project using Docker