In normal development, if you do not manually configure or optimize the size of the package, the first screen will load slowly when the user opens the site, and the content will appear several seconds later, greatly increasing the user’s waiting time.

To solve this problem, we need to optimize from packaging. With webpack, a common optimization packaging tool, we started with the popular React and Vue libraries and tried to optimize them.


First, create a React application using the create-react-app scaffolding.

If not, you first need to install the scaffolding from the global as follows:

npm install create-react-app -g
Copy the code

Create a new folder and write the following command on your terminal:

create-react-app webpack-optimiation-react
Copy the code

Once the template is created, its directory structure is as follows:

.├ ─ Package.json ├─ public │ ├─ Favicon. Ico │ ├─ logo73.png │ ├─ Logo53.png │ ├─ Manifest.json │ └ ─ ─ robots. TXT ├ ─ ─ the README. Md ├ ─ ─ the SRC │ ├ ─ ─ App. CSS │ ├ ─ ─ App. Js │ ├ ─ ─ App. Test, js │ ├ ─ ─ index. The CSS │ ├ ─ ─ index. The js │ ├── ├─ ├.txt ├─ ├.txt ├─ ├.txtCopy the code

Once the template files are set up, we add the same code to different libraries and use the Router to make them work.

// src/Home.js
import React from 'react';
export default() = ><h1>Home</>;
Copy the code
// src/About.js
import React from 'react';
export default() = ><h1>About</>;
Copy the code
// src/Concat.js
import React from 'react';
export default() = ><h1>Concat</h1>;
Copy the code

Then, using the NPM package manager, add react-router-dom.

npm install react-router-dom -D
Copy the code

After the above code is written, add the following to SRC /index.js:

import React, { lazy, Suspense } from 'react';
import { Switch, BrowserRouter as Router, Link, Route } from 'react-router-dom';

const Home = lazy((a)= > import('./Home'));
const Concat = lazy((a)= > import('./Concat'));
const About = lazy((a)= > import('./About'));

const NavBar = (a)= > (
  <div>
    <Link to='/'>Home</Link>
    <Link to='/about'>About</Link>
    <Link to='/concat'>Concat</Link>
  </div>
);

function App() {
  return( <Router className='App'> <> <NavBar /> <Suspense fallback={<div>loading... </div>}> <Switch> <Route path='/' exact component={Home} /> <Route path='/about' component={About} /> <Route path='/concat' component={Concat} /> </Switch> </Suspense> </> </Router> ); } export default App;Copy the code

When you’re done, use NPM run start, and the browser will automatically open with:

OK, it is running properly.

Let’s try packing and enter the command at the terminal:

npm run build
Copy the code

A build folder appears, which is the result of packaging and ready for back-end deployment.

The file size after build can be as large as 600K. To see which packages are large, you need to configure WebPack.

Create-react-app, however, does not expose the Webpack configuration file. You need to type a command to see the webapck configuration file:

npm run eject
Copy the code

Its structure directory is as follows:

. ├ ─ ─ the config │ ├ ─ ─ env. Js │ ├ ─ ─ jest │ │ ├ ─ ─ cssTransform. Js │ │ └ ─ ─ fileTransform. Js │ ├ ─ ─ modules. Js │ ├ ─ ─ paths. Js │ ├ ─ ─ pnpTs. Js │ ├ ─ ─ webpack. Config. Js │ └ ─ ─ webpackDevServer. Config. Js ├ ─ ─ package. The json ├ ─ ─ public │ ├ ─ ─ the favicon. Ico │ ├── ├.html │ ├── ├.html │ ├── manifest.json │ ├── ├.txt │ ├── ─ ├.txt │ ├── ─ ├.txt │ ├── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ── ─ Build. Js │ ├ ─ ─ start. Js │ └ ─ ─ test. The js ├ ─ ─ the SRC │ ├ ─ ─ App. CSS │ ├ ─ ─ App. Js │ ├ ─ ─ App. Test, js │ ├ ─ ─ index. The CSS │ ├ ─ ─ ├─ ├─ ├.txt ├─ ├.txt ├─ ├.txt ├─ ├.txtCopy the code

In order to solve the problem of packing 600KB above, we first need to analyze which places are too large after packing. We need the plugin Webpack-bundle-Analyzer to visually see which packages are larger.

Installation method:

npm install webpack-bundle-analyzer -D
Copy the code

Then write the code in config/webpack.config.js:

Import the package first, and then add the plug-in:

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

plugins:[
    ...
     isEnvProduction && new  BundleAnalyzerPlugin(),
     // the isEnvProduction variable refers to whether it is in production. ]Copy the code

The results of visual analysis are as follows:

As you can see from the figure above, the largest package packaged is the React-dom.

So let’s go ahead and optimize it for two reasons:

  1. Ways to reduce stress on the server side

  2. Ways to reduce size

Ways to reduce server stress include:

  • Using AggressiveSplittingPlugin plug-in

  • Use the prefetch&preload methods

  • Use the gzip method

Ways to reduce size are:

  • Using ModuleConcatenationPlugin plug-in

  • Use the UglifyJS plug-in

  • Use the exterals option


AggressiveSplittingPlugin

Now try AggressiveSplittingPlugin optimization package code.

Add code directly to plugins in config/webpack.config.js:

plugins: [
  new webpack.optimize.AggressiveSplittingPlugin({
    minSize: 3000.maxSize: 5000.chunkOverhead: 0.entryChunkMultiplicator: 1})];Copy the code

There are a lot of small files in the build folder. This plugin will split files over a certain size. Helps to reduce the request load on the server. AggressiveSplittingPlugin bundle can be split into smaller chunk, until all the chunk size reach maxSize option Settings. It organizes modules together through a directory structure.

It Records the separation point in Webpack Records and attempts to restore the separation the way it started. This ensures that the old separation points (and chunks) are reusable after changes to the application, since they may already be in the client’s cache. Therefore, using Records is highly recommended.


preload&prefetch

The second is the ability to pre-load, and the ability to pre-request. Let’s see what happens with Preload.

myhtml-webpack-pluginVersion is4.0.0 - beta. 11In order to install the Preload plug-in correctly, its version must be set to3.0.0 - beta. 3Otherwise, it will report the following error:

Plugin could not be registered at 'html-webpack-plugin-before-html-processing'. Hook was not found.
Copy the code

Install this version using the NPM manager:

NPM install [email protected]Copy the code

Next add to webpack.config.js:

const PreloadWebpackPlugin = require('preload-webpack-plugin');
// ...
plugins: [
  new HtmlWebpackPlugin(
    Object.assign(
      {},
      {
        inject: true.template: paths.appHtml
      },
      isEnvProduction
        ? {
            minify: {
              removeComments: true.collapseWhitespace: true.removeRedundantAttributes: true.useShortDoctype: true.removeEmptyAttributes: true.removeStyleLinkTypeAttributes: true.keepClosingSlash: true.minifyJS: true.minifyCSS: true.minifyURLs: true}} :undefined)),// ...
  new PreloadWebpackPlugin()
];
// ...
Copy the code

Open the debug console, if your node appears like this, then you have succeeded.

And then we have ref=preload which is preloading.

preloadWhat can you do?

  • You can preload files or resources.
  • Does not block page loading.

For example, if many font files are used in a web page, the user will wait for the font to load while browsing, resulting in a blank screen.

With Preload, the browser doesn’t request a page at a time and reload it in the specified page. Instead, it preloads the font file and doesn’t reload it in the specified page.


Now, let’s try prefetch and see what happens.

Modify in config/webpack.config.js:

// ...
plugins: [
  // ...
  new PreloadWebpackPlugin({
    rel: 'prefetch'
  })
  // ...
];
// ...
Copy the code

At this point, preload becomes prefetch as shown below:

If you see ref=prefetch, the prefetch is successful.

What do they have in common?

  1. Load resources asynchronously without blocking web page rendering
  2. Downloading does not execute the file
  3. Ability to request files in advance
  4. There are no same-domain restrictions

What is the difference between prefetch and preload?

  1. preloadWill load first, and will occupyHTTPConcurrency, which is the number of requests made as soon as the page is entered. whileprefetchThe browser will be out of the idle period, then request the file.
  2. preloadCan be requested across domains,prefetchDon’t.

Gzip

Gzip allows us to reduce storage space, as well as transfer time.

We need to install the plugin:

npm install -D compression-webpack-plugin
Copy the code

In config/webpack.config.js add:

/ /...
plugins: [
  / /...
  new CompressionWebpackPlugin({
    filename: '[path].gz[query]'.algorithm: 'gzip'.test: /\.(js|css)/.threshold: 1024.minRatio: 0.8
  })
  / /...
];
/ /...
Copy the code

Once the build is complete, open the browser. If you see a header like this, gzip has been successfully enabled.


Now, let’s start reducing the size optimization.

Using ModuleConcatenationPlugin can improve the speed of execution of code and precompiled:

In config/webpack.config.js add:

// ...
plugins: [
  // ...
  new webpack.optimize.ModuleConcatenationPlugin()
];
// ...
Copy the code

After build, the size is reduced by nearly 10KB compared to the previous set, and the page opening speed is much faster.

It is 50ms faster than before. Plus lazy loading, you don’t need to load all the pages in advance, so the first screen rendering is much faster.


Uglify

Next, our last killer, the UglifyJS plug-in, compresses and uglifies code to the maximum extent possible.

The command line to install it:

npm install -D uglifyjs-webpack-plugin
Copy the code

Import into config/webpack.config.js and use:

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

 // ...
optimization: {
  minimizer: {new UglifyJsPlugin(),
  }
}
// ...
Copy the code

You can see that the compressed code is 180kb larger.

Externals

The externals webpack option was removed during the packaging process. This effectively reduces dependencies, and the use of third-party CDNS reduces HTTP stress and request stress.

To use externals, add the following code to webpack.config.js:

// ...
externals:{
      'react': 'React'.'react-dom': 'ReactDOM'.'react-router-dom': 'ReactRouterDOM'
}
// ...
Copy the code

Then add the CDN to index.html.

<script src="https://cdn.bootcss.com/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdn.bootcss.com/react-router-dom/5.1.2/react-router-dom.min.js"></script>
<script src="https://cdn.bootcss.com/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
Copy the code