background

The other night, on my way home from work, I walked past the project team next door and listened to them talk about building projects:

Now online packaging time is too long, one minute to fix a bug, half an hour to release, the thief uncomfortable.

Their projects are relatively large, and the online build time is extremely long, generally more than 15 minutes.

I talked to them briefly, went back and took a look at the build times of my projects:

In fact, it is quite long, so I took time to optimize it, and the effect is relatively obvious:

In the main body, I will share the following:

  • Some configurations to improve WebPack packaging performance
  • Some thoughts on optimizing the build time of large projects

I hope it inspires you.

The body of the

Our project is not very big, it is a medium-sized international project, about a hundred pages.

After configuring Vite, the local build time has been reduced to around 20s. If you are interested in this article, you can see the following:

After a look, the online build time was five or six minutes, not painful, but there should be room for optimization, so I am going to optimize it.

1. Identify problems

When it comes to optimizing build times, the first step is, of course, to identify the problems, identify the time consuming phases, and then optimize them.

Here I use the SMP plugin.

The SMP plug-in is very simple to use, and I’ll just mention it here:

// webpack.config.js
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
const smp = new SpeedMeasurePlugin();

module.exports = smp.wrap({
  // ...
});
Copy the code

Use the SMP plug-in to get the packaging time of each stage:

Two obvious problems were found:

  1. IgnorePluginIt takes nearly 20 seconds.
  2. less-loaderPart was executed twice, wasting more than a minute.
  3. ts-loaderIt took a minute and a half, which is a long time.

2. Solve the problem

1. IgnorePlugin

Look at the configuration and find that IgnorePlugin in the configuration is not working as expected. Delete it.

2. less-loader

After viewing the configuration, it is found that the less part is processed again.

Less file processing, you can directly see the official document, document address:

Webpack.docschina.org/loaders/les…

My configuration:

{
  test: /\.less$/,
  use: [
    'style-loader'.'css-loader',
    {
      loader: 'less-loader'.options: {
        javascriptEnabled: true.sourceMap: true.modifyVars: {
          // inject our own global vars
          // https://github.com/ant-design/ant-design/issues/16464#issuecomment-491656849
          hack: `true; @import 'The ${require.resolve('./src/vars.less')}'; `. themeVariables, },limit: 10000.name: '[name].[hash:7].[ext]'.outputPath: 'styles/',},},],}, {test: /\.css$/,
  use: ['style-loader'.'css-loader'],},Copy the code

3. ts-loader

For the optimization of ts-Loader, please refer to:

Webpack.js.org/guides/buil…

It is also clearly described in the document:

The documentation suggests that we turn on the transpileOnly option and turn off type checking.

If you want to type checking, can use ForkTsCheckerWebpackPlugin, this plugin will make relevant inspection in another process.

This plugin, which we also explored in optimizing for memory overruns at build time, is of interest to my article:

Now let’s turn this option on as well.

When the local build is enabled, the local sends a warning:

This is a very familiar error from the import type problem we discussed earlier. Let’s fix it:

Problem solved.

Rebuild, and get the following results:

After optimization, we found that:

  • IgnorePlugin, HtmlWebpackPluginTime is dramatically shortened.
  • less-loaderWhen it was back to normal, it was only executed once.
  • ts-loaderThe time is dramatically shortened from 1 minute 30 seconds to 40 seconds.

Local effect is obvious, need to go to the online build verification.

3. Confirm validity

When executed online, the result is as follows:

Then went to check the page, also is normal.

Perfect!

Looking back, it is not difficult to find that, in fact, did not change many things, the harvest of good results.

For small and medium-sized projects, changing the configuration can often meet our requirements, but for large projects?

Like projects with dozens of modules and hundreds of pages.

Back to the original problem: fix a bug for 1 minute, release it for half an hour.

Even simple configuration changes can’t bring the time down, so what do you do?

Some thoughts on optimizing the build time of large projects

Molecular disassembly application

Let’s say we have a project with nearly 30 large modules:

There are dozens of pages in each large module. This system takes a long time to build and needs to be optimized.

And later in the project, the problems will become more obvious, such as:

  • The code is getting bloated
  • The service module itself is not associated
  • Builds are getting slower and slower
  • Unable to deploy independently

In the face of this situation, one possible approach is: molecular application.

The split architecture:

Each subproject has its own entry point and is a standalone project that can be deployed.

Subitems into separate UMD packages:

When the main project starts, we load these subprojects:

After loading, we need to handle the route and store.

// base
export const bootstrap = () = > {
  // ...
  ReactDOM.render((
    <Provider store={store}>
      <Router history={history}>
        <App defaultInitialData={_initialData} />
      </Router>
    </Provider>
  ), document.getElementById('root'));
  return Promise.resolve();
};

// main
const loadSubApp = (htmlEntry: string) = > {
  return importHTML(`${htmlEntry}?The ${Date.now()}`)
    .then((res: any) = > res.execScripts())
    .then((exportedValues: any) = > {
      console.log(`importHTML: ${htmlEntry} loaded, exports:`, exportedValues);
      const { store, router } = exportedValues || {} as any;
      router && addCustomRouter(router);
      store && addCustomStore(store);
    })
    .catch(e= > {
      console.error('importHTML: ${htmlEntry} load error:', e);
    });
};

const load = () = > {
  if(__ENV__ ! = ='dev') {
    const paths: string[] = [];
    subAppConfig.subApps.forEach(item= > {
      if (item.project === localStorage.getItem('ops_project')) {
        paths.push(...item.paths);
      }
    });
    Promise.all(paths.map(path= > loadSubApp(path)))
      .catch(e= > console.log(e))
      .finally(setAllLoaded);
  } else{ setAllLoaded(); }};const init = () = > {
  console.log('init: Start to bootstrap the main APP');
  addCustomStore(rootStore);
  bootstrap().then(() = > {
    load();
  });
};

init();
Copy the code

Code sharing

  • Common package

    • component
    • utils
    • typings
    • .
  • externals

    • The react buckets
    • moment
    • antd
    • .

Style isolation

Add a namespace named subproject to the style:

Development and debugging

Take the OPS project as an example.

Make it easy to develop and debug the OPs-Common package as if it were a local file:

  1. Let the project compile the Common package

  1. wepback alias

  1. TS alias

Independent deployment

Apply for separate modules for each subproject on the same project

Advantages and disadvantages of molecular disassembly applications

Advantages:

  • Any subapplication will doIndependent publishing, submodule and main moduledecouple.
  • Subprojects are fineSeparate compilationThe main project only needs to do the introduction to thisReduce the build time of the main module.

Disadvantages:

  • Additional complexity and maintenance costs

conclusion

Generally speaking, for small and medium-sized projects, do a good job of packaging configuration optimization, can solve part of the problem.

To optimize the build time of large projects, we can consider the model of dismantling the molecule application.

However, this mode needs to consider some maintenance issues, such as how to maintain the version tag, how to roll back quickly, and so on.

These decisions need to be made in light of the actual situation of your project.

So much for today’s content, I hope to inspire you.

The last

I wish you a happy May Day ~ ~