preface

A simple WebPack configuration has been implemented for local development and production of front-end resource packs. However, this is far from meeting the requirements of our release and production line, and there is still a lot of room for optimization. Therefore, this article mainly explains the optimization of packaging configuration.

1. Optimization of packaging speed

1.1. Analysis of packaging speed

Before we optimize WebPack, we need to know the status and results of our WebPack work. Firstly, the speed of packaging is analyzed. The speed-measure-webpack-plugin plug-in can be used to obtain the time consuming of each plug-in and loader plug-in.

  • Installing a plug-in
npm install speed-measure-webpack-plugin -D
Copy the code
  • rightwebpack.prod.jsModify the configuration:
// webpack.prod.js
const { merge } = require('webpack-merge')
const common = require('./webpack.common.js')
// Speed analysis plugin
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin")
const smp = new SpeedMeasurePlugin()

const config = merge(common, {
  mode: 'production'
})

module.exports = (env, argv) = > {
  return process.env.IS_SPEED === 'TRUE' ? smp.wrap(config) : config
}
Copy the code
  • Add anpm scriptCommand, passcross-envAdd aIS_SPEEDEnvironment variable, whenIS_SPEED=TRUEWhen executing the speed analysis plug-in:
{
  "scripts": {
    "speed": "cross-env IS_SPEED=TRUE npm run build"}},Copy the code
  • performnpm run speed, the console will print out the total time it took to pack and eachThe plugin, loader,Is the running time.

1.2. Configure cache

The use of cache is one of the important means to optimize the speed of webpack hot update and packaging. Before WebAPck5, we mostly used cache-loader and some caching functions of Loader. The webpack5 update brought two major changes in the handling of caching: long-term caching and persistent caching.

1.3. Long-term cache

The Chunk, module ID, and export name are determined. The algorithm of long-term cache is added. These algorithms are enabled by default in production mode. When using [contenthash], Webpack 5 will use a true file contenthash. Before, it only used the hash value of the internal structure. This can have a positive impact on long-term caching when only comments are modified or variables are renamed. These changes are not visible after compression.

1.4. Persistent caching

Webpackage 5 has a file system cache. It is optional and can be configured to be enabled with the cache option:

  • Add configuration to webpack.common.js
// webpack.common.js
module.exports = {
  cache: {
    // Set the cache type to file system
    type: 'filesystem'.buildDependencies: {
      / / recommended in webpack configuration Settings cache. BuildDependencies. Config: [__filename] to get the latest configuration and all dependencies
      config: [__filename]
    }
  }
}
Copy the code
  • The cache will be stored by default innode_modules/.cache/webpack, we will executenpm run dev, andnpm run buildOperation, at this timenode_modules/.cache/webpackFolder under the development environment and packaging environment under the cache files.

1.5. Loader Cache

  • babel-loaderOpen the cache
// webpack.common.js
module.exports = {
  module: {
    rules: [{test: /\.m? js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader? cacheDirectory=true'}}]}}Copy the code
  • vue-loaderusecache-loader
npm install cache-loader -D
Copy the code
// webpack.common.js
module.exports = {
  module: {
    rules: [{test: /\.vue$/,
        loader: 'vue-loader'.options: {
          // Specify the cache file storage address
          cacheDirectory: pathResolve('node_modules/.cache/vue-loader'),
          / / use the cache - loader
          cacheIdentifier: 'cache-loader:{version} {process.env.NODE_ENV}'}}]}}Copy the code

1.6. Narrow the construction target range

In the working process of loader, we can make it accurately find the files and directories to be processed, avoiding the identification and processing of irrelevant files and wasting time. Exclude and include can be configured in the loader rule

  • Exclude: excludes all matching modules.
  • Include: Introduces modules that meet any of the following criteria.

Example: Apply to vue-loader

// webpack.common.js
module.exports = {
  module: {
    rules: [{test: /\.vue$/,
        loader: 'vue-loader'.include: [resolve('src')].exclude: /node_modules/,
        options: {
          // Specify the cache file storage address
          cacheDirectory: pathResolve('node_modules/.cache/vue-loader'),
          / / use the cache - loader
          cacheIdentifier: 'cache-loader:{version} {process.env.NODE_ENV}'}}]}}Copy the code

1.7 oneOf optional method

  • We can useoneOfTo improve the matching efficiency of the Loader. After a loader is matched, subsequent matches will not continue. We have reformed rules

  • If you only want to use it in certain Vue componentsCSS Modules, you can useoneOfThe rules and theresourceQueryCheck module string in string

1.8, multi-process packaging

It is recommended that we use Thread-loader for time-consuming loader operations. To use this loader, place it before other loaders. Loaders placed after this loader will run in a separate worker pool.

  • Install the loader
npm install thread-loader -D
Copy the code
  • We have tobabel-loaderandts-loaderConfigured to use
// webpack.common.js
module.exports = {
  module: {
    rules: [
      // Escape the js file
      {
        test: /\.m? js$/,
        include: [resolve('src')].exclude: /node_modules/,
        use: [
          {
            loader: 'thread-loader'.options: {
              // The number of workers generated, default is (CPU cores -1)
              workers: 3}},'babel-loader? cacheDirectory=true']},// Escape TSX files
      {
        test: /\.tsx? $/,
        include: [resolve('src')].exclude: /node_modules/,
        use: [
          {
            loader: 'thread-loader'.options: {
              workers: 3}}, {loader: 'ts-loader'.options: {
              transpileOnly: true.happyPackMode: true.appendTsSuffixTo: [/\.vue$/]}}]}}Copy the code

1.9, runtimeChunk

Its purpose is to extract the chunks mapping list separately from main.js. Since the ID of each chunk is basically hash based on the content, every change you make will affect it. If you don’t extract it, Is equal to main.js changes every time. The cache is invalidated. After extracting runtimeChunk separately, each package generates a runtime~main.xxx.js.

  • Our component lazy load package is the Runtime code, we create a layout component in the project, and use the route lazy load. No configurationruntimeChunkAfter packaging and modifying the layout, the hash of layout.js and main.js files has changed.

  • To configureruntimeChunk, you only need tooptimization.runtimeChunkTrue will do
// webpack.prod.js
const config = merge(common, {
  mode: 'production'.optimization: {
    runtimeChunk: true.// Option splits the Runtime code into a separate chunk}})Copy the code
  • If you repeat the above operation, you will now have a runtime~main.xxx.js file, but the hash value of the main.js file will not change.

We found that the runtime.js generated by packaging is very small, usually only a few KB after Gzip, but the file changes frequently and we need to request it again each time. Its HTTP time is much longer than its execution time, so it is not recommended to unpack it separately. Instead, we inline it into our index.html (which would have changed every time we packed it).

The script-ext-html-webpack-plugin was chosen mainly because it also supports preload and prefetch, so we don’t want to reference another plugin when we need it.

const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin");

HtmlWebpackPlugin must be referenced after HtmlWebpackPlugin
// Inline name is the same as your runtimeChunk name
new ScriptExtHtmlWebpackPlugin({
  inline: /runtime~main\.. *\.js$/
})
Copy the code
  • At this point, our runtime~main.xxx.js code is inlined into index.html

2. Packaging volume optimization

2.1. Packaging volume analysis

After analyzing and optimizing the packaging speed, we will look at the volume of the products we package. Use webpack-bundle-Analyzer to view the details of our packaged files:

  • Install the plugin
npm install webpack-bundle-analyzer -D
Copy the code
  • Modify the webpack.prod.js configuration
// webpack.prod.js
const { merge } = require('webpack-merge')
const common = require('./webpack.common.js')
// Speed analysis plugin
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin")
const smp = new SpeedMeasurePlugin()
// Volume analysis plug-in
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

const config = merge(common, {
  mode: 'production'
})

// Determine whether to enable the packaged volume analysis plug-in based on environment variables
if (process.env.IS_ANALYZER === 'TRUE') {
  config.plugins.push(new BundleAnalyzerPlugin())
}

module.exports = (env, argv) = > {
  return process.env.IS_SPEED === 'TRUE' ? smp.wrap(config) : config
}
Copy the code
  • Add anpm scriptCommand, passcross-envAdd aIS_ANALYZEREnvironment variable, whenIS_ANALYZER=TRUEExecute the speed analysis plug-in
{
  "scripts": {
    "analyzer": "cross-env IS_ANALYZER=TRUE npm run build"}},Copy the code
  • performnpm run analyzer, the browser automatically opens a window to show the information about the packaged file

2.2. Code compression

2.2.1. Js compression

Webpack V5 comes out of the box with the latest version of the Terser-Webpack-plugin. If you are using WebPack V5 or later and want to customize the configuration, you still need to install the Terser-webpack-plugin

  • Install the plugin
npm install terser-webpack-plugin -D
Copy the code
  • In the configuration file configuration, we custom remove the codeconsole.log()
// webpack.prod.js
const config = merge(common, {
  mode: 'production'.optimization: {
    runtimeChunk: true.moduleIds: 'deterministic'.usedExports: true.minimizer: [
      new TerserPlugin({
        parallel: true.terserOptions: {
          compress: {
            drop_console: true / / remove the console log}})]}})Copy the code

2.2.2 CSS Compression

Before WebPack 5 we used the extract-text-webpack-plugin to compress and extract our CSS. But new features in WebPack V5 bring us the MiniCssExtractPlugin, which extracts CSS into a separate file, creates a CSS file for each JS file containing CSS, and supports on-demand loading of CSS and SourceMaps.

  • Install the plugin
npm install mini-css-extract-plugin -D
Copy the code
  • Configure the loader

We distinguish between environment, use MiniCssExtractPlugin for a production environment, in the style file is processed before using CSS – loader MiniCssExtractPlugin. Loader

// webpack.common.js
const isDev = process.env.NODE_ENV === 'development'

MiniCssExtractPlugin is enabled in the production environment
const styleLoader = isDev
  ? {
      loader: 'vue-style-loader'.options: {
        sourceMap: false.shadowMode: false
      }
    }
  : MiniCssExtractPlugin.loader

module.exports = {
  module: {
    rules: [{oneOf: [
          // Process the.css file
          {
            test: /\.css$/,
            include: [
              resolve('src')].oneOf: [
              // This matches' 
              {
                resourceQuery: /module/,
                use: [
                  styleLoader,
                  {
                    loader: 'css-loader'.options: {
                      modules: true.// Enable the CSS module
                      localIdentName: '[local]_[hash:base64:5]'}}, {loader: 'postcss-loader'.options: {
                      sourceMap: false // Disable SourceMap generation}}},// This matches normal '
              {
                use: [
                  styleLoader,
                  {
                    loader: 'css-loader'.options: {
                      sourceMap: false.importLoaders: 1 // Set the number of Loaders to be applied before CSS-Loader}}, {loader: 'postcss-loader'.options: {
                      sourceMap: false}}]}]},// Process.scss files
          {
            test: /\.(sa|sc)ss$/,
            include: [resolve('src')].exclude: /node_modules/,
            use: [
              styleLoader,
              {
                loader: 'css-loader'.options: {
                  sourceMap: false.importLoaders: 2}}, {loader: 'postcss-loader'.options: {
                  sourceMap: false}}, {loader: 'sass-loader'.options: {
                  sourceMap: false}}]}]}}Copy the code
  • Configure the plugin
// webpack.prod.js

/ / CSS
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

const config = merge(common, {
  mode: 'production'.plugins: [
    new MiniCssExtractPlugin({
      filename: 'static/css/[name].[contenthash:8].css'.// Start the long-term cache. Add [name] as needed
      chunkFilename: 'static/css/[id].[contenthash:8].css'})]})Copy the code
  • performnpm run buildAfter successfully packing, indist/staticFound an extra contain in directorymain.*.cssCSS folder for files.

  • Use the CSS MinimizerPlugin to compress CSS files
npm install css-minimizer-webpack-plugin -D
Copy the code
/ / compress CSS
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin")

const config = merge(common, {
  mode: 'production'.optimization: {
    minimizer: [
      new CssMinimizerPlugin({
        minimizerOptions: {
          preset: [
            'default',
            {
              discardComments: { removeAll: true } // Remove all comments (including those starting with /*! Opening note)}]})]}})Copy the code
  • Execute at this pointnpm run buildAnd the originalmain.*.cssThe code in the file is compressed

2.3 SplitChunks

SplitChunks can be used to extract common dependencies into existing chunks or into a newly generated chunk, avoiding the repeated packaging of several chunks into multiple files. SplitChunks are enabled by default in the production environment, and we use the default configuration for package analysis and NPM Run Analyzer.

For the sake of effect, here are some of the following demo scenarios using actual line project code!!

  • We then modify its configuration according to the actual situation of our own projects
// webpack.prod.js
const config = merge(common, {
  mode: 'production'.optimization: {
    runtimeChunk: true.// Option splits the Runtime code into a separate chunk
    moduleIds: 'deterministic'.// Whether to add any new local dependencies, vendor Hash should be the same for both builds
    usedExports: true.// Extract third-party libraries (such as Lodash or React) into a separate Vendor chunk file
    splitChunks: {
      chunks: 'all'.// This indicates which chunks will be selected for optimization. When supplied with a string, valid values are all, async, and initial
      minSize: 20000.// Generates the minimum chunk size
      maxAsyncRequests: 6.// Maximum number of parallel requests when loading on demand
      maxInitialRequests: 6.// The maximum number of parallel requests at the entry point
      cacheGroups: {
        vendor: {
          name: 'chunk-vendors'.minChunks: 2.// The minimum number of chunks that must be shared before splitting
          test: /[\\/]node_modules[\\/]/,
          priority: 10.reuseExistingChunk: true
        },
        fantUI: {
          name: 'chunk-fantUI'.test: /[\\/]node_modules[\\/]_? fant-ui(.*)/,
          priority: 15.reuseExistingChunk: true
        },
        common: {
          name: 'chunk-common'.minChunks: 2.priority: 5.reuseExistingChunk: true}}}}})Copy the code
  • To performnpm run analyzer, we based onpriorityPrioritize the UI component libraryfant-uiSeparate them out,echartsBecause the volume is too large is also extracted, the restnode_modulesRely on being hit to onebundleInside, some project common modules were hitchunk-common bundleInside the file; Entry files for such projectsmain.jsThe volume of the project is greatly reduced, making the loading speed of the first screen of the project increased.

2.4. CDN introduces third-party modules

For some third-party modules in the project, we do not want to follow the project to package deployment, so as to reduce the volume after packaging and reduce the pressure on the deployment server. We can deal with it by introducing CDN. For example, after the SplitChunks are subcontracted above, we can see that the volume of echarts and Aliyun-OSS-SDK, two third-party packages, are relatively large, so we can use CDN to deal with them.

  • The externals configuration option of Webpack provides a method to “exclude dependencies from the output bundle”, and the CDN can use jsdelivr here.

  • Modify webpack.com mon. Js

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      title: 'retail-admin'.filename: 'index.html'.template: 'index.html'.inject: true.scriptLoading: 'defer'.cdn: [
        'https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js'.// Jsdelivr CDN introduces echarts
        'https://cdn.jsdelivr.net/npm/[email protected]/dist/aliyun-oss-sdk.min.js' // Jsdelivr CDN introduces aliyun-osS-sdk]})],externals: {
    echarts: 'echarts'.'ali-oss': 'OSS'}}Copy the code
  • Modified index. HTML
<! DOCTYPEhtml>
<html lang="zh">
  <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">
    <title><%= htmlWebpackPlugin.options.title %></title>
    <! CdnJS -->
    <% for(var js of htmlWebpackPlugin.options.cdn) { %>
      <script defer src="<%=js%>"></script>The < %} % ></script>
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>
Copy the code
  • To performnpm run analyzer, you can seeechartsandaliyun-oss-sdkIt’s all out of our project package.

2.4.1 CDN is down

Suddenly one day the test feedback page could not be opened, and then I searched for the reason and found that the file introduced by CDN could not be loaded!!

The echarts website fell for the trick, opening only HTML structure displayed. When I opened the console, I found a bunch of folder loading errors, and the third party packages on the site were mostly links to jsdelivr references

Because the company does not have its own CDN, in order to prevent production accidents caused by this problem in the future, it cancelled the use of external CDN to introduce…

2.5. Image compression

If many image files are used in the project, image compression can greatly reduce the volume of the project package. For Webpack, image-webpack-loader can be used for image compression.

  • The installationimage-webpack-loaderIs used herecnpmGo ahead and install it otherwise it will run with an error (pit)
npm install cnpm -g --registry=https://registry.npm.taobao.org
cnpm install image-webpack-loader -D
Copy the code
  • Modify the configuration of image file processing
Webpackage 5.0 adds an asset Module to handle static resources
{
  test: /\.(png|jpe? g|gif|webp)(\? . *)? $/,
  include: [resolve('src')].exclude: /node_modules/.// Image compression
  use: [
    // image-webpack-loader must be installed using CNPM. Otherwise, errors may be reported
    {
      loader: 'image-webpack-loader'.options: {
        mozjpeg: {
          progressive: true
        },
        // optipng.enabled: false will disable optipng
        optipng: {
          enabled: false
        },
        pngquant: {
          quality: [0.65.0.9].speed: 4
        },
        gifsicle: {
          interlaced: false
        },
        // the webp option will enable WEBP
        webp: {
          quality: 75}}}].type: 'asset'.generator: {
    // Outputs the file location and file name
    filename: 'images/[name].[hash:8].[ext]'
  },
  parser: {
    dataUrlCondition: {
      maxSize: 10 * 1024 If the value exceeds 10kb, base64 will not be transferred}}}Copy the code

2.5.1 Panda press drawing

Since image-webpack-loader is installer unfriendly and error-prone, only NPM can be used to install dependencies when packing in a CI/CD environment. I still recommend using Tinypng, an online map crushing site, to manually compress project images.

  • The images in the project were not compressed, and the package, images folder, had a lot of images

  • Use images from the projecttinypngAfter compression, you can see that the volume of the compressed picture can be reduced by more than 50%

  • When the project is packaged, the images folder is now left with only a few images, because images are configured not to exceed 10KB to base64; The size of the project package has also been greatly reduced.

2.6. Enable Gzip

Can reduce the file size, faster transfer speed. Gzip is an effective way to save bandwidth and speed up your website.

  • Install compression will – webpack – the plugin
npm install compression-webpack-plugin -D
Copy the code
  • Add anpm scriptCommand, passcross-envAdd aIS_GZIPEnvironment variable, whenIS_GZIP=TRUEWhen performingcompression-webpack-pluginPlug-in:
{
  "scripts": {
    "build:gzip": "cross-env IS_GZIP=TRUE npm run build"}}Copy the code
  • Add webPack configuration
// webpack.prod.js
const { merge } = require('webpack-merge')
const common = require('./webpack.common.js')
// Speed analysis plugin
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin")
const smp = new SpeedMeasurePlugin()
// Volume analysis plug-in
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
/ / open Gzip
const CompressionWebpackPlugin = require('compression-webpack-plugin')

const config = merge(common, {
  // ...
})

// Use the environment variable IS_ANALYZER to determine whether to enable the packaged volume analysis plug-in
if (process.env.IS_ANALYZER === 'TRUE') {
  config.plugins.push(new BundleAnalyzerPlugin())
}

// Use the environment variable IS_GZIP to determine whether to enable Gzip
if (process.env.IS_GZIP === 'TRUE') {
  config.plugins.push(new CompressionWebpackPlugin({
    filename: '[path][base].gz'.// Prints the destination file name
    algorithm: 'gzip'.// Compressed format
    test: new RegExp('\\.(js|css)$'), // The file to be processed is regular
    threshold: 0.// Process only files larger than this size (in bytes)
    minRatio: 0.8.// Process only files that are better compressed than this ratio (minRatio = compressed size/original size)
    deleteOriginalAssets: false // Whether to delete the original file}}))module.exports = (env, argv) = > {
  return process.env.IS_SPEED === 'TRUE' ? smp.wrap(config) : config
}
Copy the code
  • To perform,npm run build:gzipAt this time the dozen out of the bag.gzFormat file, and the size is much smaller than the source file.

  • Enable web server Gzip support…

  • Content-encoding :gzip Response headres is displayed in the control panel

  • We compared the loading size of files in Network with the size of files packaged locally, and found that the overall volume was greatly compressed (PS: ignoring the different hash values of files, one is packaged locally and the other is packaged online).

3. Packaging experience optimization

3.1. Handle port occupation in the development environment

When we are developing multiple front-end projects, local web services may be enabled, and the port may be occupied. Run NPM run dev to start the hot update service of the project, and set the port to 9000. Then open a terminal window and run NPM run dev. The console reported an error that the port address has been used.

  • At this point we have to manually modify the configuration before running, which is not very friendly. We can use the middleware Portfinder to set the default port and get the unused port and then set the Webpack

  • Install portfinder

npm install portfinder -D
Copy the code
  • Modify the WebPack configuration
// webpack.dev.js
const { merge } = require('webpack-merge')
const common = require('./webpack.common.js')
const { resolve } = require('path')
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')
const utils = require('./utils')

function pathResolve(dir) {
  return resolve(process.cwd(), '. ', dir)
}

const devWebpackConfig  = merge(common, {
  mode: 'development'.devtool: 'eval-cheap-module-source-map'.devServer: {
    // ...}})// The processing port is occupied
module.exports = new Promise((resolve, reject) = > {
  // Set the default port to 8080
  portfinder.basePort = process.env.PORT || 8080
  // Get an unoccupied port
  portfinder.getPort((err, port) = > {
    if (err) {
      reject(err)
    } else {
      process.env.PORT = port
      // Set the port for starting the hot update service
      devWebpackConfig.devServer.port = port

      / / start according to the actual port after reoccupy FriendlyErrorsWebpackPlugin plug-in in the terminal display of information
      devWebpackConfig.plugins.push(
        new FriendlyErrorsWebpackPlugin({
          compilationSuccessInfo: {
            messages: [
              `Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`]},// Handle exceptions using Node-notifier for native notification
          onErrors: utils.createNotifierCallback()
        })
      )

      resolve(devWebpackConfig)
    }
  })
})
Copy the code
  • Install Node-notifier and use Node.js to send cross-platform native notifications.
npm install node-notifier -D
Copy the code
  • Create a new utils.js file
'use strict'
const path = require('path')
const packageConfig = require('.. /package.json')

exports.createNotifierCallback = () = > {
  const notifier = require('node-notifier')

  return (severity, errors) = > {
    if(severity ! = ='error') return

    const error = errors[0]
    const filename = error.file && error.file.split('! ').pop()

    // Cross-platform native message notification
    notifier.notify({
      title: packageConfig.name,
      message: severity + ':' + error.name,
      subtitle: filename || ' '.// Set the notification display icon
      icon: path.join(__dirname, '.. /public/favicon.ico')}}}Copy the code
  • At this point, after compiling errors in the development environment, an error message pop-up box will appear on the computer, and the message notification bar also has pop-up information.

3.2 optimize the production packaging terminal prompt

The ProgressPlugin plug-in was used in the previous configuration to show the progress of the WebPack run, but production packaging is rarely required for daily development, and the official documentation states that ProgressPlugin may not provide much value for quick builds. We make changes to the configuration only when progress is indicated in the development environment, and production packaging is mostly used for CI/CD, so we only need to care if the packaging is successful.

  • Install ORA, elegant terminal spinner plug-in
NPM install [email protected] - DCopy the code
  • The installationrimrafUsed to replace the previousoutput.cleanAbility to clear the dist directory
npm install rimraf -D
Copy the code
  • By installing Chalk, console output is no longer monotonous, and text background can be added, font color can be changed, etc
NPM install [email protected] - DCopy the code
  • Create a new build.js file and change itnpm scriptBuild command.
{
  "scripts": {
    "build": "cross-env NODE_ENV=production node ./webpack/build.js"}}Copy the code
// build.js
'use strict'

process.env.NODE_ENV = 'production'

const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
const webpack = require('webpack')
const webpackConfig = require('./webpack.prod') ()// Open the elegant terminal spinner ORA
const spinner = ora('building for production... ')
spinner.start()

// Rimraf empties the specified folder and the callback executes Webpack for packing
rm(path.join(path.resolve(__dirname, '.. /dist'), 'static'), err= > {
  if (err) throw err
  // Perform webpack packaging
  webpack(webpackConfig, (err, stats) = > {
    spinner.stop()
    if (err) throw err
    // The console prints webpack information output (console.log() is a call to process.stdout.write with formatted output)
    process.stdout.write(
      // When using the Node.js API, you need to pass stats configuration items to stats.tostring ()
      stats.toString({
        assets: false.// Tell stats whether to display resource information
        colors: true.// Tell stats whether to output different colors
        modules: false.// Tell stats whether to add information about the building module
        children: false.// Tell stats whether to add information about submodules
        entrypoints: false.// Tell stats whether to display the entry file and its corresponding file bundles
        chunks: false.// Tell stats whether to add information about chunk
        chunkModules: false // Tell STATS whether to add information about built modules and chunks
      }) + '\n\n'
    )

    // Package abnormally exits the process
    if (stats.hasErrors()) {
      console.log(chalk.red(' Build failed with errors.\n'))
      process.exit(1)}// Pack the printed information normally
    console.log(chalk.cyan(' Build complete.\n'))
    console.log(
      chalk.yellow(
        ' Tip: built files are meant to be served over an HTTP server.\n' +
          " Opening index.html over file:// won't work.\n"))})})Copy the code
  • To performnpm run buildCommand, the following figure shows the console output from the packaged and packaged console respectively

3.3. Check the package environment node version

Webpack runs in Node.js, and the lowest version of Node.js running WebPack 5 is 10.13.0 (LTS). Webpack in the earlier version of Node.js environment, due to some node.js API incompatible packaging exceptions, it is necessary to check the version before packaging! Prevent the package from being unusable in production, resulting in production accidents.

  • Package. The json configurationengines
{
  "engines": {
    "node": "> = 10.13.0"."npm": "> = 3.0.0"}}Copy the code
  • Install Semver, an implementation of the Semantic Versioning specification
npm install semver -D
Copy the code
  • Install shelljs, Node.js script language parser
npm install shelljs -D
Copy the code
  • newcheck-versions.jsfile
'use strict'
const chalk = require('chalk')
const semver = require('semver')
const packageConfig = require('.. /package.json')
const shell = require('shelljs')

// To create the Node synchronization process, run CMD
function exec(cmd) {
  return require('child_process').execSync(cmd).toString().trim()
}

const versionRequirements = [
  {
    name: 'node'.Semver.clean (' =v1.2.3 ') // '1.2.3'
    currentVersion: semver.clean(process.version),
    versionRequirement: packageConfig.engines.node
  }
]

// Check whether the console can run commands starting with NPM
if (shell.which('npm')) {
  versionRequirements.push({
    name: 'npm'.// To create a Node synchronization process, run NPM --version
    currentVersion: exec('npm --version'),
    versionRequirement: packageConfig.engines.npm
  })
}

module.exports = function () {
  const warnings = []

  for (let i = 0; i < versionRequirements.length; i++) {
    const mod = versionRequirements[i]

    / / semver. Satisfies (' 1.2.3 ', '1 x | | > = 2.5.0 | | 5.0.0-7.2.3') / / true
    if(! semver.satisfies(mod.currentVersion, mod.versionRequirement)) { warnings.push( mod.name +':' +
          chalk.red(mod.currentVersion) +
          ' should be ' +
          chalk.green(mod.versionRequirement)
      )
    }
  }

  // The console displays abnormal information
  if (warnings.length) {
    console.log(' ')
    console.log(chalk.yellow('To use this template, you must update following to modules:'))
    console.log()

    for (let i = 0; i < warnings.length; i++) {
      const warning = warnings[i]
      console.log(' ' + warning)
    }

    console.log()
    // End the Webpack process
    process.exit(1)}}Copy the code
  • Introduce a call to check-versions in build.js
// build.js
'use strict'

require('./check-versions') ()Copy the code
  • usenvmModify the node version so that it does not meet the requirementsenginesThe requirements of

  • To performnpm run build, the console will print the publishing exception and terminate the WebPack running process.

4, and end

The code address

References:

Webpack website

vuejs-templates/webpack

Using webpack4 with proper posture

Previous articles:

Webpack5 actual combat full analysis – basic chapter

NVM is used to manage node versions and solve front-end development environment configuration