Implementation of Webpack3+React16 code splitting

Implementation of Webpack3+React16 code splitting

Project Background

Recently, there is a project with an older webpack version. Since the upgrade and framework change are not accepted by the leader layer for the time being o(╥﹏╥)o, it can only be optimized under the existing conditions.

webpack3 + react16

webpack v3 configuration check

It is obvious that the project configuration is inherited from v1. The upgrade from v1 to v3 is relatively simple. You can refer to the official website https://webpack.js.org/migrate/3/.

Loaders become rules
Chained loaders are no longer supported, and json-loader does not need to be configured
The UglifyJsPlugin plugin needs to enable minimize

Analyze existing package problems

After building the package using webpack-bundle-analyzer, as shown in the figure

The problem is pretty obvious:

Except for the larger package zxcvbn, the code is simply packaged into vender and app, and the file is very large.

Dynamic import split vendor

Analyze the vendor code. Some large packages, such as libphonenumber.js, are not used frequently. Take them out and request them when the relevant features are used.

Refer to the official react code splitting guide, https://react.docschina.org/docs/code-splitting.html#import

import { PhoneNumberUtil } from 'google-libphonenumber'
function usePhoneNumberUtil(){
  // Using PhoneNumberUtil
}

Modify to dynamic import() method, then and async/await are both supported to obtain asynchronous data

const LibphonenumberModule = () => import('google-libphonenumber')
function usePhoneNumberUtil(){
  LibphonenumberModule().then({PhoneNumberUtil} => {
    // Using PhoneNumberUtil
  })
}

When Webpack parses this syntax, it will automatically perform code splitting.

The modified effect:

libphonenumber.js (1.chunk.js) is separated from vender, and in the actual operation of the project, the libphonenumber.js file is requested from the server only when entering the usePhoneNumberUtil process.

Route-based code splitting

React.lazy

Refer to the React official code splitting guide - route-based code splitting, https://react.docschina.org/docs/code-splitting.html#route-based-code-splitting.

Example before splitting:

import React from 'react';
import { Route, Switch } from 'react-router-dom';
const Home = import('./routes/Home');
const About = import('./routes/About');

const App = () => (
<Router>
 <Suspense fallback={<div>Loading...</div>}>
  <Switch>
   <Route exact path="/" component={Home}/>
   <Route path="/about" component={About}/>
  </Switch>
 </Suspense>
</Router>
);

Example after splitting:

import React, { lazy } from 'react';
import { Route, Switch } from 'react-router-dom';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));

const App = () => (
// The routing configuration remains unchanged)

Effect after splitting:

app.js is automatically split into different files by webpack according to the route. When the route is switched, the target route code file will be pulled.

Named Export

This paragraph is quoted from https://react.docschina.org/docs/code-splitting.html#named-exports.

React.lazy currently only supports default exports. If you want the imported module to use named exports, you can create an intermediate module that re-exports as the default module. This ensures that tree shaking doesn't fail and that you don't include unnecessary components.

// ManyComponents.js
export const MyComponent = /* ... */;
export const MyUnusedComponent = /* ... */;
// MyComponent.js
export { MyComponent as default } from "./ManyComponents.js";
// MyApp.js
import React, { lazy } from 'react';
const MyComponent = lazy(() => import("./MyComponent.js"));

Implementing AsyncComponent yourself

The lazy loading routing component wrapped by React.lazy must add Suspense. If you don't want to force it, or need to freely extend the lazy implementation, you can define and implement AsyncComponent, and use it in the same way as lazy.

import AsyncComponent from './components/asyncComponent.js'
const Home = AsyncComponent(() => import('./routes/Home'));
const About = AsyncComponent(() => import('./routes/About'));
// async load component
function AsyncComponent(getComponent) {
 return class AsyncComponent extends React.Component {
  static Component = null
  state = { Component: AsyncComponent.Component }

  componentDidMount() {
   if (!this.state.Component) {
    getComponent().then(({ default: Component }) => {
     AsyncComponent.Component = Component
     this.setState({ Component })
    })
   }
  }
  // component will be unmount
  componentWillUnmount() {
   // rewrite setState function, return nothing
   this.setState = () => {
    return
   }
  }
  render() {
   const { Component } = this.state
   if (Component) {
    return <Component {...this.props} />
   }
   return null
  }
 }
}

common business code splitting

After completing the route-based code splitting, I looked carefully at the package size and found that the total package size had increased from 2.5M to 3.5M.

From the webpack analysis tool, we can see that the culprit is that each separate routing code is packaged with a separate public file such as components, utils, and locales.

Use the webapck configuration to package the common part separately.

Components file merge and export

The example exports all the files under components together. The same is true for other files.

function readFileList(dir, filesList = []) {
 const files = fs.readdirSync(dir)
 files.forEach((item) => {
  let fullPath = path.join(dir, item)
  const stat = fs.statSync(fullPath)
  if (stat.isDirectory()) {
   // Recursively read all files readFileList(path.join(dir, item), filesList)
  } else {
   /\.js$/.test(fullPath) && filesList.push(fullPath)
  }
 })
 return filesList
}
exports.commonPaths = readFileList(path.join(__dirname, '../src/components'), [])

Webpack configuration extracted from common

import conf from '**';
module.exports = {
 entry: {
  common: conf.commonPaths,
  index: ['babel-polyfill', `./${conf.index}`],
 },
 ... //Other configuration plugins:[
  new webpack.optimize.CommonsChunkPlugin('common'),
  ... // other plugins
 ]
}

CommonsChunkPlugin is used in webpack3 to extract third-party libraries and common modules. The parameter common passed in is the existing chunk of entrty, then the common module code will be merged into this chunk.

Extract the code from common

After extracting the duplicate codes of each route, the total size of the package becomes 2.5M again. There is an additional common bundle file. (Common is too large, it can actually be further split)

Summarize

There are still many areas that can be optimized in webpack packaging. In addition, there are some differences between different webpack versions. The idea of ​​unpacking is to extract the common ones and load them on demand according to the usage scenario.

This is the end of this article about the implementation of Webpack3+React16 code splitting. For more relevant Webpack3+React16 code splitting content, please search for previous articles on 123WORDPRESS.COM or continue to browse the following related articles. I hope you will support 123WORDPRESS.COM in the future!

You may also be interested in:
  • React implements page code splitting and on-demand loading
  • 4 ways to implement code splitting in React

<<:  Steps for using the non-installed version of MySQL and solutions for forgetting the password

>>:  How to lock a virtual console session on Linux

Recommend

Solution to the problem of English letters not wrapping in Firefox

The layout of text has some formatting requiremen...

How to install MySQL Community Server 5.6.39

This article records the detailed tutorial of MyS...

Input file custom button beautification (demo)

I have written such an article before, but I used...

React native realizes the monitoring gesture up and down pull effect

React native implements the monitoring gesture to...

How to limit the number of concurrent connection requests in nginx

Introduction The module that limits the number of...

Native JS to achieve sliding button effect

The specific code of the sliding button made with...

Example explanation of MySQL foreign key constraints

MySQL's foreign key constraint is used to est...

Solve nginx "504 Gateway Time-out" error

Students who make websites often find that some n...

Detailed explanation of common methods of JavaScript Array

Table of contents Methods that do not change the ...

HTML table cross-row and cross-column operations (rowspan, colspan)

Generally, the colspan attribute of the <td>...

Linux RabbitMQ cluster construction process diagram

1. Overall steps At the beginning, we introduced ...

CUDA8.0 and CUDA9.0 coexist under Ubuntu16.04

Preface Some of the earlier codes on Github may r...

Detailed installation and use of SSH in Ubuntu environment

SSH stands for Secure Shell, which is a secure tr...