Using craco to optimize the construction of cra project

Posted by ajcrm125 on Wed, 09 Feb 2022 16:49:55 +0100

Modify the configuration of CRA project

A project created with create react app cannot modify its internal webpack configuration by default. Unlike Vue cli, it can be modified through a configuration file. Although an eject command can completely expose the configuration, it is an irreversible operation and will lose the convenience brought by CRA and subsequent upgrades.

If you want to rewrite CRA configuration without eject, the following methods are mature at present

  1. Use the rewritten react scripts package when creating a project through the -- scripts version parameter officially supported by CRA
  2. Use react-app-rewired + customize-cra Combined overlay configuration
  3. Use craco Overlay configuration

I choose craco here

install

  1. Installation dependency
yarn add @craco/craco
Copy code
  1. Modify pacage Commands in JSON
{
  "scripts":{
    "start": "craco start",
    "build": "craco build",
    "test": "craco test"
  }
} 
Copy code

Create craco.com in the root directory config. JS configuration file

/* craco.config.js */

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

This completes the basic configuration. The next step is to handle the coverage of various configurations, the complete craco config. JS configuration file structure, which can be queried in detail in the official document of craco: Configuration File .

be careful! The latest version of craco is V6 4.3 only projects created by cra4 are supported

Build volume analysis

Firstly, the plug-in webpack bundle analyzer is introduced to analyze the composition of the build product

/* 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 browser after construction
                reportFilename: path.resolve(__dirname, `analyzer/index.html`), 
            }),
        ],
    }
}
Copy code

After packaging with the yarn build command, you can get an analysis diagram containing the components of each chunk.

You can see that the package volume of the project here is as high as 24M, and a lot of duplicate files are packaged.

Code splitting to reduce repeated packaging

Due to lazy loading, each page corresponds to an independent chunk file. Some frequently used libraries will be repeatedly packaged into each chunk, which increases a lot of volume. Use here SplitChunksPlugin To break these libraries into a single chunk.

In craco, you can get the configuration object of webpack through the configure attribute, modify it to configure, and split the duplicate packages.

Through the analysis of the graph, it is found that jsoneeditor, echarts, antv and other libraries have a great impact on the package volume, so they are separated. In addition to splitting the repeatedly packaged content, we can also extract the basic framework of the project into a separate file base JS, this file contains the basic running environment of all web pages. (for long-term cache of base.js file)

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',
                        // Pack the modules shared by more than two chunk s into the commons group.
                        minChunks: 2,
                        name: 'commons',
                        priority: 80,
                    },
                },
            };
            return webpackConfig;
        },
}
Copy code

After it is separated, the package volume is directly reduced to 7.61M, which is significantly improved.

Load large volume libraries on demand

From the optimized analysis diagram, I found a large library BizCharts, which actually uses only a few components in the project

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

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

It is very troublesome to modify all the introductions manually. At this time, we can use the babel plug-in to help us modify them during construction.

Babel plugin import plug-in was originally used to introduce antd on demand, but here we can also use it for other libraries

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

Copy code

Construction speed optimization

The HardSourceWebpackPlugin plug-in can provide intermediate caching for modules. The first build time has not changed much, but the second start can save about 80% of the build time.

In my project, the initial construction speed is 26s and 15s after configuring the plug-in to generate cache, saving more than 60% of the time.

Attach 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',
                        // Pack the modules shared by more than two chunk s into the commons group.
                        minChunks: 2,
                        name: 'commons',
                        priority: 80,
                    },
                },
            };
            return webpackConfig;
        },
    },
   
    babel: {
        plugins: [
            [   // antd's on-demand loading and automatic introduction of style files
                'import',
                {
                    libraryName: 'antd',
                    libraryDirectory: 'es',
                    style: true,
                },
            ],
            // On demand loading of bizcharts
            ['import', { libraryName: 'bizcharts', libraryDirectory: 'lib/components' }, 'biz'],
        ],
    },
    plugins: [
        {   // Modify antd theme
            plugin: CracoLessPlugin,
            options: {
                lessLoaderOptions: {
                    lessOptions: {
                        math: 'always',
                        modifyVars: {
                            '@primary-color': '#1890ff ', / / theme color
                        }, 
                        javascriptEnabled: true,
                    },
                },
            },
        },
    ],
};
Copy code

summary

This optimization is mainly aimed at reducing the volume of the construction product. The volume is about 24m - > 6.8m, and the improvement is still very large.

Through code segmentation, reduce the repeated packaging of libraries, load some large libraries on demand, and improve the construction speed through some cached plug-ins.

last

If you think this article is a little helpful to you, give it a compliment. Or you can join my development exchange group: 1025263163 learn from each other, and we will have professional technical Q & A to solve doubts

If you think this article is useful to you, please click star: http://github.crmeb.net/u/defu Thank you very much!

PHP learning manual: https://doc.crmeb.com
Technical exchange forum: https://q.crmeb.com

Topics: Javascript React Webpack crmeb