Webpack packaging project

Keywords: Javascript Vue.js

Vue-cli4 project packaging 💕

How to operate webpack

For vli3 and vli4 versions, we need to modify the webpack configuration. We need to create the vue.config.js file in the project root directory

First, configure the cross domain problem of the interface

module.exports = {
	devServer: {
    open: false, // Auto launch browser
    host: '0.0.0.0', // localhost
    port: 6060, // Port number, can not be configured
    hotOnly: false, // Hot update, can not be configured
    overlay: {
      //  Displays a full screen overlay in the browser when a compiler error or warning occurs
      warnings: false,
      errors: true
    },
    proxy: {
      // 2. Configure cross domain
      '/api': {
        target: 'http://120.53.31.103:84/xxx/xxx ', / / domain names that allow cross domain interfaces
        // ws: true, / / whether to enable websockets
        changOrigin: true, // Open the agent and create a virtual server locally
        pathRewrite: {
          '^/api': '/'
        }
      }
    }
  }
}

After configuring the cross domain, we can put the '/ api' in the requested address and use it directly

Configure alias alias

Using Vue cli to develop projects, the biggest feature is componentization. Components frequently refer to other components or plug-ins. We can define some common paths as short names. It is convenient for use in development. It can be configured after the project is created, which is convenient for later use

//Load path module
const path = require('path')
//Define the resolve method to convert a relative path into an absolute path
const resolve = dir => path.join(__dirname, dir)
``module.exports = {
  chainWebpack: config => {
    // add alias
    config.resolve.alias
      .set('@', resolve('src'))  // vue's own alias
      .set('assets', resolve('src/assets'))
      .set('api', resolve('src/api'))
      .set('views', resolve('src/views'))
      .set('components', resolve('src/components'))
    }
 }

After configuration, it will be more convenient for us to use later. The alias can be used directly for the path

Packaging and optimized configuration of webpack after project development

Purpose:

  • Increase packing speed
  • Reduce the project volume and improve the loading speed of the first screen
  • Improve user experience (skeleton screen)

Steps:

First, we need to modify the path of the static resource file

module.exports = {
  publicPath: './', // ###1. Static resource path (default /, which must be changed before packaging, otherwise the screen will be blank after packaging)
  assetsDir: 'assets',
  lintOnSave: true,
}

Here we optimize the packaged project:

  • Remove production environment sourceMap

After the vue project is packaged, some map files will be automatically generated in the js folder, which takes up a considerable part of the space. The sourceMap resource mapping file stores the code locations before and after packaging, which is convenient for development and use. This takes up a considerable part of the space.
The function of the map file is: after the project is packaged, the code is compressed and encrypted. If an error is reported during operation, the output error information cannot accurately know where the code reports an error. With a map, you can accurately output which line and column has an error like unencrypted code.

sourceMap is not required for the production environment. We can configure the following attributes:

module.exports = {
  //Remove the productionSourceMap from the production environment
  productionSourceMap: false,
}

After removing sourceMap, it can reduce about 3-4MB

  • Remove console.log printing and comments
    Download plug-ins:
cnpm install uglifyjs-webpack-plugin --save-dev
// 4. Remove conlose.log
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
 configureWebpack: config => {
    const plugins = [];
    if (isProduction) {
      plugins.push(
        new UglifyJsPlugin({
          uglifyOptions: {
            output: {
              comments: false, // Remove comments
            },
            warnings: false,
            compress: {
              drop_console: true,
              drop_debugger: false,
              pure_funcs: ['console.log']//Remove console
            }
          }
        })
      )
    }
  },

After removing printing and comments, packaging will reduce about 20-30kb, because congsol. Log () and comments will not take up too much volume

  • Accelerate optimization with CDN

cdn optimization refers to the introduction of third-party libraries such as vue, vue router and axios into the project through cdn, which will significantly reduce the number of vendors.js and greatly improve the loading speed of the front page of the project. The following are the specific operations:

const isProduction = process.env.NODE_ENV === 'production';

// externals
const externals = {
  vue: 'Vue',
  'vue-router': 'VueRouter',
  vuex: 'Vuex',
  vant: 'vant',
  axios: 'axios'
}
// The CDN external chain will be inserted into index.html
const cdn = {
  // development environment 
  dev: {
    css: [],
    js: []
  },
 // production environment 
  build: {
    css: ['https://cdn.jsdelivr.net/npm/vant@2.12/lib/index.css'],
    js: [
      'https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js',
      'https://cdn.jsdelivr.net/npm/vue-router@3.1.5/dist/vue-router.min.js',
      'https://cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.min.js',
      'https://cdn.jsdelivr.net/npm/vuex@3.1.2/dist/vuex.min.js',
      'https://cdn.jsdelivr.net/npm/vant@2.12/lib/vant.min.js'
    ]
  }
}
module.exports = {
  configureWebpack: config => {
    // Modify configuration for production environment
    if (isProduction) {
      // externals
      config.externals = externals
    }
  },
  chainWebpack: config => {
    /**
     * Add the CDN parameter to the htmlWebpackPlugin configuration
     */
    config.plugin('html').tap(args => {
      if (isProduction) {
        args[0].cdn = cdn.build
      } else {
        args[0].cdn = cdn.dev
      }
      return args
    })
  }
}

Add in public/index.html:

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
     
          <!-- use CDN of CSS file -->
          <% for (var i in
          htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
          <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style" />
          <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet" />
        <% } %>
         <!-- use CDN Accelerated JS File, configuration in vue.config.js lower -->
        <% for (var i in
          htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
          <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
        <% } %>
    
  </head>



  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>
  • Compress resource files
    Download plug-ins
cnpm i compression-webpack-plugin -D

vue.config.js is configured as follows:

const CompressionWebpackPlugin = require('compression-webpack-plugin')

module.exports = {
  // Change here according to your actual situation
  publicPath,
  assetsDir: 'assets',
  lintOnSave: true,
  configureWebpack: {
    plugins:[
      new CompressionWebpackPlugin({
        filename: '[path].gz[query]',
        algorithm: 'gzip',
        // test: /\.js$|\.html$|\.json$|\.css/,
        test: /\.js$|\.json$|\.css/,
        threshold: 10240, // Only resources with a size greater than this value will be processed
        minRatio: 0.8, // Only resources with a compression ratio less than this value will be processed
        // deleteOriginalAssets: true / / delete the original file
      })
    ],
  },
}

After compression, some space will be saved. The single back end needs to modify nginx to cooperate with the front end

location ~ .*\.(js|json|css)$ {
    gzip on;
    gzip_static on; # gzip_static is the processing module of nginx for static files. This module can read pre compressed gz files, which can reduce the CPU resource consumption of gzip compression each time.
    gzip_min_length 1k;
    gzip_http_version 1.1;
    gzip_comp_level 9;
    gzip_types  text/css application/javascript application/json;
    root /dist;
}
  • Compression of picture resources

Download plug-ins

npm install image-webpack-loader --save-dev

This plug-in is easy to fail to download. If it fails, you can install it several times. If it has been downloaded before, uninstall it first and then download it

//npm installed npm removed npm
npm uninstall image-webpack-loader
//If yarn is installed, remove yarn
yarn remove image-webpack-loader 

Using cnpm means installing cnpm and then setting the global registry to the image of Ali. Domestic Ali is faster

npm install cnpm -g --registry=https://registry.npm.taobao.org

Using cnpm to install image webpack loader, you will find that it will be installed soon

cnpm install --save-dev image-webpack-loader

Configure vue.config.js

module.exports = {
  // Change here according to your actual situation
  publicPath,
  assetsDir: 'assets',
  lintOnSave: true,
  // image compression is defined in chainWebpack
 chainWebpack: config => {
   config.module
      .rule('images')
      .use('image-webpack-loader')
      .loader('image-webpack-loader')
      .options({
        bypassOnDebug: true
      })
      .end()}
}
  • Common code extraction

Starting from webpack4, the commonchunk plug-in was officially removed and the optimization attribute was used for more flexible configuration, which should also be the most complex part of the code modification process of upgrading from V3 to V4

splitChunks: {
    chunks: "async",//The default value is all/initial/async/function(chunk). When the value is function, the first parameter is the chunk module when traversing all entry chunks, chunk_ Modules are all dependent modules of chunk. They can be freely configured through the name of chunk and the resource s of all dependent modules. All public modules that meet the conditions of chunk and all dependent modules of the module, including css, will be extracted
    minSize: 30000,  //Indicates the minimum module size before compression. The default value is 30kb
    minChunks: 1,  // Indicates the number of references. The default value is 1;
    maxAsyncRequests: 5,  //All asynchronous requests must not exceed 5
    maxInitialRequests: 3,  //The number of initial parallel requests shall not exceed 3
   automaticNameDelimiter:'~',//Name separator, default is~
    name: true,  //The packed names are chunk s by default, and the names are separated by separators (the default is ~)
    cacheGroups: { //Set the cache group to extract chunk s that meet different rules. Take generating common as an example
       common: {
         name: 'common',  //The name of the extracted chunk
         chunks(chunk) { //It is the same as the parameter configuration of the outer layer, covering the chunks of the outer layer, and extracting with the chunk as the dimension
         },
         test(module, chunks) {  //You can extract strings, regular expressions, and functions in the dimension of module. Any module that meets the conditions will be extracted into the chunk of the common. When it is a function, the first parameter is each module traversed, and the second parameter is each chunk array referenced to the module. In the process of trying, I found that css could not be extracted, which needs further verification.
         },
        priority: 10,  //Priority. A chunk is likely to satisfy multiple cache groups and will be extracted into the cache group with high priority
       minChunks: 2,  //Referenced by at least a few chunk s
       reuseExistingChunk: true,//  If the extracted chunk is referenced in the chunk, the chunk will be referenced directly and the packaged code will not be repeated
       enforce: true  // If minSize is not set in the cacheGroup, judge whether to use the upper minSize. true: use 0, false: use the upper minSize
       }
    }
}
  • Third party module extraction
// Common code extraction
configureWebpack: config => {
//....
//Optimize Item Configuration
config.optimization = {
    splitChunks: { // Split code block
        cacheGroups: {
            vendor: {//Third party library withdrawal
                chunks: 'all',
                test: /node_modules/,
                name: 'vendor',
                minChunks: 1,//The minimum number of times this code block should be referenced before splitting
                maxInitialRequests: 5,
                minSize: 0,//Greater than 0 bytes
                priority: 100//weight
            },
            common: {  //Common module extraction
                chunks: 'all',
                test: /[\\/]src[\\/]js[\\/]/,
                name: 'common',
                minChunks: 2,The minimum number of times this code block should be referenced before splitting
                maxInitialRequests: 5,
                minSize: 0,//Greater than 0 bytes
                priority: 60
            },
            styles: { //Style extraction
                name: 'styles',
                test: /\.(sa|sc|c)ss$/,
                chunks: 'all',
                enforce: true
            },
            runtimeChunk: {
                name: 'manifest'
            }
        }
    }
  }
}

After configuring these attributes, they can be compressed to 1MB. If you want to optimize them, you can continue to configure them:
Complete packaging

Complete packaging

//Load path module
const path = require('path')
//Define the resolve method to convert a relative path into an absolute path
const resolve = dir => path.join(__dirname, dir)

// 4. Remove conlose.log
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const isProduction = process.env.NODE_ENV === 'production';
//  5. CDN accelerated optimization
// externals exclusion
const externals = {
  vue: 'Vue',
  'vue-router': 'VueRouter',
  vuex: 'Vuex',
  vant: 'vant',
  axios: 'axios'
}
// The CDN external chain will be inserted into index.html
const cdn = {
  // development environment 
  dev: {
    css: [],
    js: []
  },
  // production environment 
  build: {
    css: ['https://cdn.jsdelivr.net/npm/vant@2.12/lib/index.css'],
    js: [
      'https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js',
      'https://cdn.jsdelivr.net/npm/vue-router@3.1.5/dist/vue-router.min.js',
      'https://cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.min.js',
      'https://cdn.jsdelivr.net/npm/vuex@3.1.2/dist/vuex.min.js',
      'https://cdn.jsdelivr.net/npm/vant@2.12/lib/vant.min.js'
    ]
  }
}

module.exports = {
  publicPath: './', // ###1. Static resource path (default /, white screen after packaging)
  assetsDir: 'assets',
  lintOnSave: true,

  productionSourceMap: false,//Remove production environment sourceMap
  devServer: {
    open: false, // Auto launch browser
    host: '0.0.0.0', // localhost
    port: 6060, // Port number
    hotOnly: false, // Hot renewal
    overlay: {
      //  Displays a full screen overlay in the browser when a compiler error or warning occurs
      warnings: false,
      errors: true
    },
    proxy: {
      // 2. Configure cross domain
      '/api': {
        target: 'http://120.53.31.103:84/api/app ', / / domain name of the interface
        // ws: true, / / whether to enable websockets
        changOrigin: true, // Open the agent and create a virtual server locally
        pathRewrite: {
          '^/api': '/'
        }
      }
    }

  },
  chainWebpack: config => {
    // 3 add alias
    config.resolve.alias
      .set('@', resolve('src'))
      .set('assets', resolve('src/assets'))
      .set('api', resolve('src/api'))
      .set('v', resolve('src/views'))
      .set('components', resolve('src/components'))

    // 4. Add the CDN parameter to the htmlWebpackPlugin configuration
    config.plugin('html').tap(args => {
      if (isProduction) {
        args[0].cdn = cdn.build
      } else {
        args[0].cdn = cdn.dev
      }
      return args
    })

    // 5. Picture compression
    config.module
      .rule('images')
      .use('image-webpack-loader')
      .loader('image-webpack-loader')
      .options({
        bypassOnDebug: true
      })
      .end()

  },
  configureWebpack: config => {// 4. Remove conlose.log
    const plugins = [];
    if (isProduction) {
      // 4. External exclusions
      config.externals = externals
      plugins.push(
        new UglifyJsPlugin({
          uglifyOptions: {
            output: {
              comments: false, // Remove comments
            },
            warnings: false,
            compress: {
              drop_console: true,
              drop_debugger: false,
              pure_funcs: ['console.log']//Remove console
            }
          }
        })
      )
    }
    // 6. Optimize Item Configuration
    config.optimization = {
      splitChunks: { // Split code block
        cacheGroups: {
          vendor: {//Third party library withdrawal
            chunks: 'all',
            test: /node_modules/,
            name: 'vendor',
            minChunks: 1,//The minimum number of times this code block should be referenced before splitting
            maxInitialRequests: 5,
            minSize: 0,//Greater than 0 bytes
            priority: 100//weight
          },
          common: {  //Common module extraction
            chunks: 'all',
            test: /[\\/]src[\\/]js[\\/]/,
            name: 'common',
            minChunks: 2,//The minimum number of times this code block should be referenced before splitting
            maxInitialRequests: 5,
            minSize: 0,//Greater than 0 bytes
            priority: 60
          },
          styles: { //Style extraction
            name: 'styles',
            test: /\.(sa|sc|c)ss$/,
            chunks: 'all',
            enforce: true
          },
          runtimeChunk: {
            name: 'manifest'
          }
        }
      }
    }
  },
}

Posted by Catharsis on Tue, 14 Sep 2021 17:13:19 -0700