The preface

A modern front-end architecture requires the support of build tools. The choice, understanding, and application of build tools determines whether or not a smooth and nearly perfect development experience can be created. This article attempts to understand the architectural ideas behind build tools by comparing them horizontally.

What problems does the build tool solve?

First of all, let’s focus on the interpretation of the word “construction” : construction, the original intention is to build, so in terms of engineering process, standardization, ultimately pointing to higher automation, reduce the tedious intermediate links, improve efficiency. The build tool solves the following problems for us:

  • Code compatibility (JS version)
  • Smooth out the differences between frameworks to generate uniform code that can run in browsers (vue, React,ng)
  • CSS prefix completion/preprocessor, JS compression obfuscation, image compression
  • Front-end deployment (e.g. + Hash, static file path issues)

What are the major build tools?

Speaking of build tools, as an experienced front-end developer, I’m sure you can name a few examples from different eras: From Browserify + Gulp to Parcel, Webpack to Rollup, and even nobundle-focused Snowpack and Vite, you’re no stranger. There are many articles on how to use build tools in detail, but I won’t cover them in this article. Here are some common build tools and documentation links:

  • Browserify, Browserify allows you to organize browser-side Javascript code in a way similar to Node’s require(), precompiled so that the front-end Javascript directly uses some of the libraries installed by Node NPM.

  • Grunt is a JavaScript task runner, developed in JavaScript itself, that flexibly manages dependencies between tasks and executes defined tasks.

  • Gulp, Gulp is a stream-based automated build tool that listens to read and write files in addition to managing and executing tasks.

  • Yeoman, Yeoman is a robust combination of tools, libraries, and workflows to help you web developers quickly create beautiful and engaging web applications.

  • YUI Compressor is a JavaScript and CSS Compressor that, in addition to removing comments and whitespace, confuses local variables with the smallest possible variable names.

  • Fis3, integrates common build functions in Web development, such as resource location, file compilation, compression, Sprite diagram, etc.

  • Rollup, which focuses on ES6 modularity, can compile a small piece of code into something larger or more complex, such as a library or application.

  • Webpack, all modules, support module packaging and rich plug-in extension functions.

  • Parcel, fast zero configuration Web application packaging tool, emerging packaging tool.

  • Snowpack, a high-performance packaging tool that leveragesM, represents the Nobundle build tool.

  • Vite is a high performance Nobundle building tool inspired by Snowpack. The website describes it as “the next generation of front-end development tools”.

  • Esbuild is a “JavaScript” Bundler packaging and compression tool that packages and distributes “JavaScript” and “TypeScript” code for running on web pages. Snowpack and Vite also use esbuild to convert JS and TS files.

A graph of popular build tools for 2020 is shared here, and those interested can check out 2020 JavaScript Rising Stars

What analytical tools are available?

Tooling.ReportGitHub is a build tool comparison platform created by the core members of Chrome Core Team and famous developers in the industry.GoogleChromeLabs tooling.report.

Using this platform, we can see how Webpack V5, Rollup V2, Parcel V2, and Browserify behave in different dimensions, as shown below:

It’s clear from the review data that WebPack scored the highest, followed by Rollup, Parcel and Browserify.

Interestingly, here’s a review report from July 2020, when webPack V4 was still in use:

You can see that webpack5 has really improved performance to the top of the list, and those of you who want to see what’s going on here can see what’s new in webpack5

To get back to the point, while the above metrics are useful, the test score you pass is only one part of the story, and it also depends on the design goals of the different build tools.

Webpack, for example, relies heavily on plug-ins and loaders to build, so its capabilities are powerful but the configuration information is cumbersome. Parcel is designed for zero configuration, out of the box, but with relatively limited functionality integration.

From the perspective of horizontal development, the major building tools are also learning from each other. For example, tools led by Webpack have historically been slow to compile builds, and even listening files to start incremental builds does not solve the problem of an initial build time that is too long. Parcel, on the other hand, primarily has multi-core parallel builds built in, leveraging multithreading for compilation capabilities that allow for faster builds during the initial build phase. The Parcel also has a built-in file system cache that holds the compilation results of each file. The new version of Webpack (V5) also follows in this regard.

Therefore, in the horizontal comparison of build tools, whether they are powerful or not is one thing, and build efficiency will also be a core metric for developers to consider.

So what are the “must-haves” for build tools in a modern project?

What are the core indicators that need to be considered?

Let’s start with the scores above and analyze the specific test dimensions.

The scores are based on the following six dimensions:

  • Code Splitting

  • Hashing

  • Importing Modules

  • Non-JavaScript Resources

  • Output Module Formats

  • Transformations

It can be clearly seen from the statistical table of results above:

  • In Code Splitting, Rollup performed best, which is an important embodiment of Rollup modernization, while Browserify performed worst.

  • In terms of Hashing, Importing Modules and Transformation, the performance of major construction tools is relatively similar.

  • In Output Module Formats, with the exception of Browserify, other tools are relatively consistent.

You may be wondering why officials are measuring across these six dimensions. In fact, one of the technical implications of this question is: what aspects should be considered/implemented in a modern build tool or build solution?

Let’s analyze them one by one:

Note: The framework statistics shown below are Browserify, Parcel, Rollup, Webpack, from the right of the main body

1.Code Splitting

Code Splitting is Code Splitting. This means being able to export common modules when building packaging, avoiding repeated packaging, and achieving the most rational on-demand loading strategy when page loading runs.

In fact, Code Splitting is a big topic. Such as:

  • Can the code splitting mechanism between different modules support different contexts (special contexts such as Web worker environment)
  • How to implement support for Dynamic Import syntax features
  • Whether the application configuration supports the extraction and packaging of duplicate modules in multi-entry/single-entry mode
  • Whether Living Bindings are supported between code modules (if a value in a dependent module changes, it will be mapped to all modules that depend on that value).

Code Splitting is a standard configuration of modern construction tools, because it directly determines the output of static resources at the front end and affects the performance of project applications. Students who want to know more about Code Splitting can go through the Webpack method.

Framework evaluation data — Code Spltiting

2.Hashing

Hashing, version information mapping of packaged resources. The important technical point behind this topic is to maximize the use of caching mechanisms. We know that an effective caching strategy directly affects page loading performance and determines the user experience. Therefore, for the building tool, in order to achieve a more reasonable hash mechanism, the building tool needs to analyze various packaging resources, export dependencies between modules, and determine the hash value of the output package according to the dependency context. Since changes in a resource will cause changes in the associated resources downstream of its dependencies, the build tool is packaged based on the analysis of module dependencies and the ability for developers to define their own hashing strategies based on the dependencies (for example, the differences between different types of hashes provided by Webpack: Hash/chunkhash/contenthash).

This involves a knowledge: how to distinguish the hash in Webpack/chunkhash/contenthash?

  • The hash reflects the build version of the project, so the hash generated during the same build is the same. In other words, if a module in the project changes, triggering a rebuild of the project, the hash value of the file will change accordingly. If you use a hash strategy, there is a problem: even if the contents of a module have not changed at all, rebuilding it will result in a new hash value, resulting in a lower cache hit ratio.

  • Different from Contenthash and Chunkhash, chunkhash performs dependency resolution based on Entry files.

  • Contenthash generates a hash value based on the content of the file.

Let’s make a concrete analysis. Assume that our application project packages the public library and the business project entry file separately and adopts the Chunkhash policy. If the business project entry file is changed, the hash value of the public library will not change. The following examples correspond to:

entry:{
    main: path.join(__dirname,'./main.js'),
    vendor: ['react']},output: {path:path.join(__dirname,'./build'),
    publicPath: '/build/'.filname: 'bundle.[chunkhash].js'
}
Copy the code

Let’s look at another example where a reference to index.css appears in index.js:

// index.js
require('./index.css')
Copy the code

At this point, because index.js and index.css have dependencies, they share the same chunkhash value. If the contents of index.js are changed, even if the index.css is not changed, the hash value of the separately split index.css changes when chunkhash is used. If you want index.css to determine the hash value entirely based on the contents of the file, you can use the Contenthash policy.

In fact, Webpack’s hash strategy has become more complete and mature. Hash vs Chunkhash vs ContentHash for specific design ideas, see The differences between Hash, Chunkhash and ContentHash in Webpack for examples

Figure note: Framework evaluation data — Hashing

3.Importing Modules

Importing Modules is the dependency mechanism. Of course it is very important for a build process or tool because, for historical and design reasons, front-end developers are often faced with different modular solutions including ESM, CommonJS, etc. The design of a build tool will, of course, incorporate different types of Modules Importing schemes. In addition, due to the design of node.js’ NPM mechanism, the build tool also supports importing public packages from node_modules.

Note: Framework Profiling data – Importing Modules

4.Non-JavaScript Resources

Non-javascript Resources are the ability to import Resources that are not JavaScript types. Non-javascript Resources here can be HTML documents, CSS style Resources, JSON Resources, rich media Resources, and so on. These resources are also key components of an application, and of course the build process/tools need to be understood and supported.

Figure note: Framework metrics data — non-javascript Resourcess

5.Output Module Formats

Output Module Formats corresponds to the Importing Modules topic above. The modular approach to building output also needs to be more flexible, such as developer configurable build content exports for ESM, CommonJS, and other specifications.

Framework profiling data — Output Module Formats

6.Transformations

  1. TransformationsModern front-end development is inseparable from the compile/escape process. For example, compression of JavaScript code, removal of useless code (DCE), etc. It is important to note that jSX-like compilation and.vue file compilation are not built into the build tool when we design the build tool. Instead, community capabilities such as Babel are “seamlessly integrated” into the build process. The build tool does only what the build does, and the rest of the extension capability is done through a plug-in mechanism, which is clearly a reasonable and necessary design.

Graph note: Framework evaluation data — doubling

conclusion

In this paper, mainstream packaging tools were introduced and compared in six dimensions with Tooling.Report. The comparison is only one thing, but more importantly, we need to compare the results to understand what each build tool does. What should be considered in infrastructure and engineering? Understanding this information, we can stand in a higher perspective, technology selection, engineering and infrastructure.

Tooling.Report Was written in the following form: Esbuild, Vite, Snowpack, and WMR.

  • Use cases

  • Set up the

  • Development server

  • The production building

  • Other features

For the whole article, see Comparing the New Generation of Build Tools. For those who feel English is difficult, please refer to this Chinese translation.