“This is the first day of my participation in the First Challenge 2022. For details: First Challenge 2022.”


In this paper, I recorded the construction and optimization process of the project and summarized the optimization methods and configurations for your reference.

Modify the CRA project configuration

Projects created using create-React-app cannot modify their internal Webpack configuration by default, unlike vue-CLI, which can be modified through a configuration file. Although there is an eject command that can expose the configuration completely, this is an irreversible operation and will lose the convenience and subsequent upgrades that CRA provides.

If you want to rewrite the CRA configuration without eject, the following approaches are well developed

  1. Officially supported through CRA--scripts-versionParameter to create your project using your own react-scripts package
  2. Use the react-app-rewired + customize-cra combination to override the configuration
  3. Override the configuration using CrACO

I’m going to go with Craco

The installation

  1. Install dependencies
yarn add @craco/craco
Copy the code
  1. Modify the command in pacage.json
{
  "scripts": {"start": "craco start"."build": "craco build"."test": "craco test"}}Copy the code

Create the craco.config.js configuration file in the root directory

/* craco.config.js */

module.exports = {
  // ...
  webpack: {},
  babel: {},}Copy the code

The basic Configuration is now complete, and the next step is to handle the coverage of various configurations. The complete Configuration File structure of Craco.config. js can be found in the craco official documentation in detail: Configuration File.

Attention! The current latest version of CrACO, V6.4.3, only supports cra4-created projects

Construction volume analysis

We first introduced the webpack-bundle-Analyzer plug-in to analyze the composition of the build artifacts

/* craco.config.js */
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); 
module.exports = {
    webpack: {
        plugins: [
            new BundleAnalyzerPlugin({
                analyzerMode: 'server'.analyzerHost: '127.0.0.1'.analyzerPort: 8888.openAnalyzer: true.// Open the browser
                reportFilename: path.resolve(__dirname, `analyzer/index.html`),}),],}}Copy the code

After packaging using the YARN Build command, you get an analysis diagram that contains the components of each chunk.

You can see that the package size of the project is up to 24M, and there are a lot of duplicate files packed.

Split code to reduce repeated packaging

Due to lazy loading, each page corresponds to a separate chunk file. Some of the more frequently used libraries are repackaged into each chunk, adding a lot of volume. SplitChunksPlugin is used to split these libraries into a single chunk.

In Craco, you can use the configure property to get the webPack configuration object, modify it to configure, and split out the duplicate packages.

After analyzing the figure, it is found that libraries such as JsonEditor, Echarts and ANTV have a great influence on the package volume, so they are split out. In addition to breaking up the repackaged content, we can also extract the basic framework of the project into a single file, base.js, which contains the base runtime environment for all web pages. (For long-term caching of base.js files)

webpack: {
    plugins: [/ * * * /].configure: (webpackConfig, { env: webpackEnv, paths }) = >{ webpackConfig.optimization.splitChunks = { ... webpackConfig.optimization.splitChunks,cacheGroups: {
                    base: {
                        // Basic framework
                        chunks: 'all'.test: /(react|react-dom|react-dom-router)/,
                        name: 'base'.priority: 100,},jsoneditor: {
                        test: /jsoneditor/,
                        name: 'jsoneditor'.priority: 100,},echarts: {
                        test: /(echarts)/,
                        name: 'echarts'.priority: 100,},g2: {
                        test: /@antv/,
                        name: 'g2'.priority: 100,},commons: {
                        chunks: 'all'.// Package modules shared by more than two chunks into the Commons group.
                        minChunks: 2.name: 'commons'.priority: 80,}}};returnwebpackConfig; }},Copy the code

After splitting it out, the package volume was directly reduced to 7.61M, a significant improvement.

Load large libraries on demand

From the optimized diagrams, I found a large library of BizCharts that actually only used a few components in the project.

In this case, you can modify the import mode to import on demand.

import { Chart, Axis, Legend, Tooltip } from 'bizcharts';
// Change to manual import on demand
import Chart from 'bizcharts/lib/components/Chart'
import Axis from 'bizcharts/lib/components/Axis'
import.Copy the code

Changing all the intrusions manually is very cumbersome, and can be done at build time with the Babel plug-in.

The babel-plugin-import plugin was originally introduced for ANTD on demand, but here we can use it for other libraries as well

babel: {
    plugins: [['import', 
        { libraryName: 'bizcharts'.libraryDirectory: 'lib/components'}},]],Copy the code

Optimization of construction speed

The HardSourceWebpackPlugin plugin provides an intermediate cache for modules. The first build time didn’t change much, but on the second start, the build time was reduced by about 80%.

In my project, the initial build speed was 26s, and after configuring the plug-in generation cache, it was 15s, saving more than 60% of the time.

Attach the configuration

// craco.config.js

const path = require('path');
const CracoLessPlugin = require('craco-less');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const WebpackBar = require('webpackbar');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const env = process.env.REACT_APP_ENV;
module.exports = {
    webpack: {
        plugins: [
            new BundleAnalyzerPlugin({
                analyzerMode: env ! = ='development' ? 'server' : 'disabled'.analyzerHost: '127.0.0.1'.analyzerPort: 8888.openAnalyzer: true.reportFilename: path.resolve(__dirname, `analyzer/index.html`),}),new WebpackBar({
                profile: true.color: '#fa8c16',}).new HardSourceWebpackPlugin(),
        ],
        alias: {
            layouts: path.resolve(__dirname, './src/app/layouts'),
            containers: path.resolve(__dirname, './src/app/containers'),
            components: path.resolve(__dirname, './src/app/components'),
            utils: path.resolve(__dirname, './src/utils'),
            routers: path.resolve(__dirname, './src/routers'),},configure: (webpackConfig, { env: webpackEnv, paths }) = > {
            webpackConfig.externals = {
                'ckeditor5-custom-build': 'ClassicEditor'}; webpackConfig.optimization.splitChunks = { ... webpackConfig.optimization.splitChunks,cacheGroups: {
                    base: {
                        // Basic framework
                        chunks: 'all'.test: /(react|react-dom|react-dom-router)/,
                        name: 'base'.priority: 100,},jsoneditor: {
                        test: /jsoneditor/,
                        name: 'jsoneditor'.priority: 100,},echarts: {
                        test: /(echarts)/,
                        name: 'echarts'.priority: 100,},g2: {
                        test: /@antv/,
                        name: 'g2'.priority: 100,},commons: {
                        chunks: 'all'.// Package modules shared by more than two chunks into the Commons group.
                        minChunks: 2.name: 'commons'.priority: 80,}}};returnwebpackConfig; }},babel: {
        plugins: [[// AntD's on-demand loading and automatic import style files
                'import',
                {
                    libraryName: 'antd'.libraryDirectory: 'es'.style: true],},// BizCharts on demand
            ['import', { libraryName: 'bizcharts'.libraryDirectory: 'lib/components' }, 'biz']],},plugins: [{// Modify the antD theme
            plugin: CracoLessPlugin,
            options: {
                lessLoaderOptions: {
                    lessOptions: {
                        math: 'always'.modifyVars: {
                            '@primary-color': '#1890ff'.// Theme color
                        }, 
                        javascriptEnabled: true,},},},},],};Copy the code

conclusion

This optimization is mainly aimed at reducing the volume of the constructed product. The volume is greatly improved from 24M -> 6.8m.

The code splitting method was adopted to reduce the duplication of library packaging, and some large libraries were loaded on demand, while the speed of construction was improved through some cached plug-ins.

Refer to the article

Juejin. Cn/post / 705231… Segmentfault.com/a/119000003… Tsejx. Making. IO/webpack – GUI…