Use Webpack to optimize React Project.

Wrote a personal blog site while learning React. I used Webpack as a packaging tool. Before learning Webpack, I knew that webpack had plug-ins to compress and extract resources, so as to reduce the volume of resources and facilitate the caching of resources. However, I did not want to immediately use the convenience brought by webpack plug-ins at the beginning of this project. Mainly want to finish the first to optimize, but also for optimization before and after a comparison, for in-depth understanding of the role of the plug-in.

Source address: Gitee, Github

The volume of the main.js file packaged after writing the project is 3.38 MiB. I deployed the server with 1 M bandwidth of Tencent Cloud, and the access speed was very slow.

Take a look at the webpack.config.js configuration at this point:

const path = require('path');
var webpack = require('webpack');
const config = {
  entry: ['babel-polyfill'.'./src/app.js'],
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'main.js'// Package output, single file output publicPath:'/',
  },
  mode: 'production',
  module: {
    rules: [
      {
        test: /\.js$/,
        include: /(src)/,
        use: {
          loader: 'babel-loader',}}, {test: /\.css$/,
        use: [ 'style-loader'.'css-loader'],}, {test: /\.scss$/,
        use: [{
          loader: 'style-loader',
        }, {
          loader: 'css-loader',
        }, {
          loader: 'sass-loader',
        },],
      },
    ],
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
  ],
  devServer: {
    contentBase: path.join(__dirname, "build"),
    compress: true,
    port: 9000,
    open: true,
    inline: true,}}; module.exports = config;Copy the code

NPM run build result:

Asset Size Chunks Names main.js 3.38 MiB main [emitted] mainCopy the code

A 3 Megabyte single file is too large. Appropriately reduce the number of requests, reduce the file size of a single request, which is an important means to do front-end optimization. How to reduce the size of the single-page application, or split resources properly (taking advantage of the browser’s ability to download multiple resources at the same time) to optimize access speed.

Js compressed

Removing comments and reducing whitespace reduces the size of the file occupied by useless characters. The UglifyjsWebpackPlugin is a plugin designed to shrink your javascript files, which in my case are main.js files.

Webpack.config.js is configured as follows:

const path = require('path');
var webpack = require('webpack');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

const config = {
  entry: ['babel-polyfill'.'./src/app.js'],
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'main.js',
    publicPath: '/',
  },
  mode: 'production',
  module: {
    rules: [
      {
        test: /\.js$/,
        include: /(src)/,
        use: {
          loader: 'babel-loader',}}, {test: /\.css$/,
        use: [ 'style-loader'.'css-loader'],}, {test: /\.scss$/,
        use: [{
          loader: 'style-loader',
        }, {
          loader: 'css-loader',
        }, {
          loader: 'sass-loader',
        },],
      },
    ],
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new UglifyJsPlugin({
      uglifyOptions: {
        ie8: false,
        mangle: true,
        output: { comments: false },
        compress: {
          warnings: false,
          drop_console: true,
          drop_debugger: true,
          unused: false,}},sourceMap: true,
      cache: true,
    }),
  ],
  devServer: {
    contentBase: path.join(__dirname, "build"),
    compress: true,
    port: 9000,
    open: true,
    inline: true,}}; module.exports = config;Copy the code

NPM run build

Asset Size Chunks Names main.js 3.1 MiB main [emitted] mainCopy the code

The resource is reduced by 0.28 MIB.

Gzip compression

The compression effect of the UglifyjsWebpackPlugin above may not be enough for our purposes. We are familiar with a package compression method, compress the file into a ZIP package, this kind of compression effect is remarkable, usually can double the file compression, then this kind of compression method can be used here, the answer is yes. The CompressionWebpackPlugin provides this functionality, so let’s take a look.

Webpack.config.js is configured as follows:

const path = require('path');
var webpack = require('webpack');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CompressionPlugin = require("compression-webpack-plugin");

const config = {
  entry: ['babel-polyfill'.'./src/app.js'],
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'main.js',
    publicPath: '/',
  },
  mode: 'production',
  module: {
    rules: [
      {
        test: /\.js$/,
        include: /(src)/,
        use: {
          loader: 'babel-loader',}}, {test: /\.css$/,
        use: [ 'style-loader'.'css-loader'],}, {test: /\.scss$/,
        use: [{
          loader: 'style-loader',
        }, {
          loader: 'css-loader',
        }, {
          loader: 'sass-loader',
        },],
      },
    ],
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new UglifyJsPlugin({
      uglifyOptions: {
        ie8: false,
        mangle: true,
        output: { comments: false },
        compress: {
          warnings: false,
          drop_console: true,
          drop_debugger: true,
          unused: false,}},sourceMap: true,
      cache: true,
    }),
    new CompressionPlugin(),
  ],
  devServer: {
    contentBase: path.join(__dirname, "build"),
    compress: true,
    port: 9000,
    open: true,
    inline: true,}}; module.exports = config;Copy the code

NPM run build result:

. Asset Size Chunks Names main.js 3.1 MiB main [dilemma] main main.js.gz 544 KiB [emitted]....Copy the code

As you can see, there is an extra main.js.gz compression package, only 554 KiB, is it a surprise? Nginx’s nginx_http_gzip_STATIC_module module can be configured to request compressed packages. The nginx configuration is as follows:

. server { gzip on; gzip_static on; gzip_min_length 1000; gzip_buffers 4 8k; gzip_types text/plain application/xml text/css text/js text/xml application/x-javascript text/javascript application/json application/xml+rss image/jpeg image/png image/g gzip_vary on; listen 80; location / { ... }}...Copy the code

This reduces the browser download from 3.38MB to 554K.

Main.js is a hodgepodge – CSS extraction

Having compressed the file into a Gzip file, nothing seems to be done to reduce the size of the file. But if you look back at the main.js file, it’s not hard to see that it’s a hodgepodge of BOTH JS and CSS. If you could pull the CSS out of the file, you could further reduce the size of the single file. It is also advantageous from a caching resource perspective. The extract-text-webpack-plugin can be used to extract CSS. Note that the extract-text-webpack-plugin can only be used for webPack 4 or later, and the mini-CSs-extract-plugin is required for Webpack 4 or later

Webpack.config.js is configured as follows:

const path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CompressionPlugin = require("compression-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const config = {
  entry: ['babel-polyfill'.'./src/app.js'],
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: '[name].[hash:8].js',
    publicPath: '/',
  },
  mode: 'production',
  module: {
    rules: [
      {
        test: /\.js$/,
        include: /(src)/,
        use: {
          loader: 'babel-loader',}}, {test: /\.(sa|sc|c)ss$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'.'sass-loader',
        ],
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'index.html',
    }),
    new webpack.HotModuleReplacementPlugin(),
    new UglifyJsPlugin({
      uglifyOptions: {
        ie8: false,
        mangle: true,
        output: { comments: false, },
        compress: {
          warnings: false,
          drop_console: true,
          drop_debugger: true,
          unused: false,}},sourceMap: true,
      cache: true,
    }),
    new CompressionPlugin(),
    new MiniCssExtractPlugin({
      filename: "[name].[hash:8].css",
      chunkFilename: "[id].[hash:8].css",
    }),
  ],
  devServer: {
    contentBase: path.join(__dirname, "build"),
    compress: true,
    port: 9000,
    open: true,
    inline: true,}}; module.exports = config;Copy the code
NPM run the build:Copy the code
Built at: Configuration information configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration configuration MiB main [emitted] main index.html 263 bytes [emitted] index.html.gz 196 bytes [emitted] main.b2c90941.css.gz 33.4 KiB [emitted] main.b2c90941.js.gz 501 KiB [emitted]Copy the code

Visible CSS is taken out, here the extraction effect is not ideal, JS size is not reduced much, the original CSS is not much.

Automatic HTML file generation: The file is automatically imported to the HTML page after being removed

In the previous step, CSS is removed and chunk hash is enabled for packaging. In this way, when JS or CSS files are changed, NPM run build will generate different JS and CSS package file names each time, so there are two problems to be solved: 1. Change the names of CSS and JS files in the index.html page after each build. 2. Daniel has already provided solutions to these two problems.

The html-webpack-plugin allows you to specify which template file to use to generate index.html. The index.html file will be generated automatically after build, and CSS and JS files will be automatically introduced into the HTML file.

The clean-webpack-plugin automatically removes directories or files at the start of a build.

Webpack.config.js is configured as follows:

const path = require('path');
var webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CompressionPlugin = require("compression-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const config = {
  entry: ['babel-polyfill'.'./src/app.js'],
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: '[name].[hash:8].js',
    publicPath: '/',
  },
  mode: 'production',
  module: {
    rules: [
      {
        test: /\.js$/,
        include: /(src)/,
        use: {
          loader: 'babel-loader',}}, {test: /\.(sa|sc|c)ss$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'.'sass-loader',],},],}, plugins: [// Build a file that needs to be removed at the start of the new CleanWebpackPlugin(['build/*'], { 
      watch: trueNew HtmlWebpackPlugin({template:'index.html',
    }),
    new webpack.HotModuleReplacementPlugin(),
    new UglifyJsPlugin({
      uglifyOptions: {
        ie8: false,
        mangle: true,
        output: { comments: false },
        compress: {
          warnings: false,
          drop_console: true,
          drop_debugger: true,
          unused: false,}},sourceMap: true,
      cache: true,
    }),
    new CompressionPlugin(),
    new MiniCssExtractPlugin({
      filename: "[name].[hash:8].css",
      chunkFilename: "[id].[hash:8].css",
    }),
  ],
  devServer: {
    contentBase: path.join(__dirname, "build"),
    compress: true,
    port: 9000,
    open: true,
    inline: true,}}; module.exports = config;Copy the code

NPM run the build:

. / / delete the specified file clean - webpack - plugin: / Users/wewin/reactLearn/story - blog/build / * has had been removed. The... Asset Size Chunks main.ad06a35d. CSS 317 KiB main [emitted] main main.ad06a35d.js 2.69 MiB main [emitted] Emitted emitted by main index.html 265 bytes [emitted] index.html.gz 196 bytes [emitted] main.ad06a35d.css.gz 33.4 KiB main.ad06a35d.js.gz 501 KiB [emitted] ...Copy the code

Automatically generated index.html file:

<! DOCTYPE html> <html> <head> <meta charset="UTF-8">
    <title>redux blog</title>
    <link href="/main.ad06a35d.css" rel="stylesheet">
  </head>
  <body>
    <div id="root"></div>
    <script type="text/javascript" src="/main.ad06a35d.js"></script>
  </body>
</html>
Copy the code

Js and CSS files are automatically imported into the HTML page, and files generated in the last build are automatically deleted.

What exactly is in the packaged files – the BundleAnalyzerPlugin

The compression of the file and the extraction of the CSS play a role in reducing js submissions, but after the above two steps, the final package of main.js is still 501 KiB. To further reduce the size of the file, we need to know what is in the main.js file and what causes the file to be so large. The BundleAnalyzerPlugin helps us analyze the composition of a file, which can be presented to us as a file or web page. Configuration and usage are not specified here.

Public JavaScript modules are removed

Removing common JavaScript modules to avoid repeated introductions can effectively reduce the size of JS files. Webpack 4 can use SplitChunksPlugin to extract common JS, and later webPack 4 can use CommonsChunkPlugin.

webpackge.config.js

const path = require('path');
var webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
var HtmlWebpackPlugin = require('html-webpack-plugin');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CompressionPlugin = require("compression-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const config = {
  entry: ['babel-polyfill'.'./src/app.js'],
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: '[name].[hash:8].js',
    publicPath: '/',
  },
  mode: 'production',
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',}}, {test: /\.(sa|sc|c)ss$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'.'sass-loader',
        ],
      },
    ],
  },
  plugins: [
    new CleanWebpackPlugin(['dist'.'build/*'], {
      watch: true,
    }),
    new HtmlWebpackPlugin({
      template: 'index.html',
    }),
    new webpack.HotModuleReplacementPlugin(),
    new UglifyJsPlugin({
      uglifyOptions: {
        ie8: false,
        mangle: true,
        output: { comments: false },
        compress: {
          warnings: false,
          drop_console: true,
          drop_debugger: true,
          unused: false,}},sourceMap: true,
      cache: true,
    }),
    new CompressionPlugin(),
    new MiniCssExtractPlugin({
      filename: "[name].[hash:8].css",
      chunkFilename: "[id].[hash:8].css",
    }),
  ],
  optimization: {
    splitChunks: {
      chunks: 'initial',
      minSize: 30000,
      minChunks: 1,
      maxAsyncRequests: 2,
      maxInitialRequests: 2,
      automaticNameDelimiter: '~',
      name: true,
      cacheGroups: {
        vendors: {
          test: /\/node_modules\//,
          priority: -10,
        },
        'react-vendor': {
          test: (module, chunks) => /react/.test(module.context),
          priority: 1,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        }
      }
    }
  },
  devServer: {
    contentBase: path.join(__dirname, "build"),
    compress: true,
    port: 9000,
    open: true,
    inline: true,}}; module.exports = config;Copy the code

NPM run the build:

Asset Size Chunk Names main.3413403b.js 8.33 emitted KiB main [emitted] main react-vendor~main.3413403b.css 318 KiB React-vendor ~main [emitted] react-vendor~main react-vendor~main.3413403b.js 2.68 MiB react-vendor~main [emitted] Act-vendor ~main index.html 355 bytes [emitted] main.3413403b.js.gz 3.2 KiB [emitted] index.html.gz 214 bytes [emitted] Act-vendor ~main.3413403b.css.gz 33.5 KiB [emitted] Act-vendor ~main.3413403b.js.gz 498 KiB [emitted]Copy the code

The configuration of splitChunks is basically the default configuration. You can refer to the official website for the usage of splitChunks. Extract js files to reduce file size, and extract base dependencies like React React- DOM to facilitate caching.

Here is mainly to record their own use of webpack plug-ins to reduce the size of the packaged file, I hope to have the same needs of friends to help.

Welcome correction!