background

As the development cycle of a project gets longer and larger, the packaging speed of a webPack-based project gets longer and longer. If ts + TSX is used, the slow compilation speed in the development environment will be very obvious. The packaging speed of the current project is as follows

Packaging for the first time

2min30s

Single-edit package

15s

There is an urgent need to improve build efficiency

Optimization idea

Refactoring packaged content based on Vite

Advantages:

  1. Start the development server directly, compile the request module in real time, and package it at the millisecond level
  2. HMR is naturally supported and the browser only needs to request changes to the module

Disadvantages:

  1. The current code packaging approach is more intrusive
  2. React support for the Vite tool may be flawed

Esbuild-loader + Webpack is used

Advantages:

  1. Can quickly improve the packaging speed
  2. It is less intrusive to existing packaging projects and can be injected only in the form of plug-ins

Disadvantages:

  1. The optimization points are not thorough enough, and there is still much room for improvement
  2. The capability provided by esbuild-Loader itself is weak and may need to be fixed

Optimization scheme

Single compilation speed optimization

Currently, the esbuild-Loader solution is used for optimization

A single optimization is only for the single type with a large proportion of TS TSX

Replace ts-loader babel-loader with esbuild-Loader by introducing the webpackEsBuildPlugin into the WebPack life cycle

Record file change points in advance for the convenience of thermal update verification and optimization later

First time compilation speed optimization

Enable multi-threaded packaging for babel-loader which takes a lot of time for the first time

Use HappyPack to package and split

The esbuild-loader is faulty

Decorators are not supported

Esbuild itself does not support TS decorators

The solution

OneOf uses a single loader for a single file. When returen is false, babel-loader + TS-loader is used to package files.

    oneOf: [
      {
        test: filePath => {
          if (!filePath) {
            return false;
          }
          try {
            const fileContent = fs.readFileSync(filePath).toString();
            return !hasDecorator(fileContent);
          } catch (e) {
            return false;
          }
        },
        use: [
          {
            loader: 'esbuild-loader',
            options: {
              loader: 'tsx',
              target: 'es2018',
            },
          },
        ],
      },
      {
        use: [
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true,
            },
          },
          {
            loader: 'ts-loader',
            options: {
              transpileOnly: true,
            },
          },
        ],
      },
    ],
Copy the code

Determines whether the input file has a decorator

/ function hasDecorator(fileContent, offset = 0) {const atPosition = fileconten.indexof ('@', offset); / function hasDecorator(fileContent, offset = 0) {const atPosition = fileconten.indexof ('@', offset); if (atPosition === -1) { return false; } if (atPosition === 1) { return true; } if (["'", '"'].includes(fileContent.substr(atPosition - 1, 1))) { return hasDecorator(fileContent, atPosition + 1); } return true; }Copy the code

Circular dependencies are not supported

Separate dependencies and resolve circular dependencies

Loading on demand is not supported

Because you are using ESbuild in the development environment, the development environment can do full import.

In antD, for example, on-demand import must be enabled in the production environment

The development environment can only take full import of style missing parts caused by on-demand import

The development environment needs to be packaged as ES5

The development environment needs to be compatible with low-end browsers, and for TSconfig, target needs to remain ES5

Es5 code packaged by TS-Loader may conflict with ES6 code packaged by ESBuild

The development environment needs to specify the configuration to set ts-Loader to ES6 code

Write the config file while keeping the development environment unchanged as far as possible

{ "extends": ".. /.. Json ", // inherit development environment configuration "compilerOptions": {"target": "es6", // override Settings "baseUrl": ".. /.. / SRC ", / / pay attention to the root directory to modify}, "include" : [".. /.. / SRC "], "exclude" : [".. /.. / node_modules ", ".. /.. / build ", ".. /.. / dist "]}Copy the code

Check the documentation for the latest TS-Loader setup available

The configFile field specifies the corresponding JSON file

If you want to make a global substitution

You can use

const TsconfigPathsPlugin = require(‘tsconfig-paths-webpack-plugin’);

Be replaced

If you want to replace the config specified in esbuild, you can go through

MinifyPlugin

To replace tsconfigRaw

Later iteration update plan

  1. Refactoring packaging with Vite (seriously)
  2. Change more loaders in development environment to ESbuild package
  3. Use SplitCode to separate libraries for packaging (dllpugin is not considered due to library update issues)

The optimization effect

Single compilation speed

10s-15s => 1-3s

First compilation speed

2min30s => 20s