当前位置:网站首页>Esbuild & SWC: a new generation of construction tools

Esbuild & SWC: a new generation of construction tools

2022-07-06 03:19:00 phial03

First , ESBuild & swc What is it? ?

  • ESBuild Is based on Go Language development JavaScript Bundler, from Figma front CTO Evan Wallace Development , And was also Vite Used for dependency resolution and analysis of development environment Transform.

  • SWC Is based on Rust Of JavaScript Compiler( Its ecology also includes packaging tools spack), At present for Next.JS/Parcel/Deno And other well-known projects in the front-end circle .

Why focus on these two tools ?img

  • You may have encountered in your daily work , The construction time of the project increases gradually with the volume and complexity of the project , Sometimes it takes a few minutes to edit a project locally ( here @Webpack)
    img

  • This is ESBuild The official website packages it 10 Share three.js Velocity contrast
    img

  • SWC It claims to be better than Babel fast 20 times ( In the case of quad core, it can be fast 70 times )

img

  • that ESBuild & SWC It's really so fast ? Or the developer's own words ? Let's test it by experiment , First look at ESBuild

    • Let's start with a very simple piece of code

      import * as React from 'react'
      import * as ReactServer from 'react-dom/server'
      
      const Greet = () => <h1>Hello, world!</h1>
      console.log(ReactServer.renderToString(<Greet />))
      
    • Then let's go through Webpack & ESBuild Build it

      • use ESBuild Pack it up

        #  compile 
        > build-esb
        > esbuild ./src/app.jsx --bundle --outfile=out_esb.js --minify
        
        #  Build product size and build time 
        out_esb.js  27.4kb
         Done in 13ms
        
        #  Run the product 
        node out_esb.js 
        <h1 data-reactroot="">Hello, world!</h1>
        
      • use Webpack Pack it up

        #  compile 
        > build-wp
        > webpack --mode=production
        
        #  Build products 
        asset out_webpack.js 25.9 KiB [compared for emit] [minimized] (name: main) 1 related asset
        modules by path ./node_modules/react/ 8.5 KiB
          ./node_modules/react/index.js 189 bytes [built] [code generated]
          ./node_modules/react/cjs/react.production.min.js 8.32 KiB [built] [code generated]
        modules by path ./node_modules/react-dom/ 28.2 KiB
          ./node_modules/react-dom/server.browser.js 227 bytes [built] [code generated]
          ./node_modules/react-dom/cjs/react-dom-server.browser.production.min.js 28 KiB [built] [code generated]
        ./src/app.jsx 254 bytes [built] [code generated]
        ./node_modules/object-assign/index.js 2.17 KiB [built] [code generated]
        
        #  Build time 
        webpack 5.72.0 compiled successfully in 1680 ms
        
        npm run build-wp  2.79s user 0.61s system 84% cpu 4.033 total
        
        #  function 
        node out_webpack.js  
        <h1 data-reactroot="">Hello, world!</h1>
        
  • Look again. swc Compilation efficiency

    • It's another simple ES6 Code

      //  Some variables declare 
      const PI = 3.1415;
      let x = 1;
      
      // spread
      let [foo, [[bar], baz]] = [1, [[2], 3]];
      const node = {
              
        loc: {
              
          start: {
              
            line: 1,
            column: 5
          }
        }
      };
      let {
               loc, loc: {
               start }, loc: {
               start: {
               line }} } = node;
      
      // arrow function
      var sum = (num1, num2) => {
               return num1 + num2; }
      
      // set
      const s = new Set();
      [2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
      
      // class
      class Point {
              
        constructor(x, y) {
              
          this.x = x;
          this.y = y;
        }
      
        toString() {
              
          return '(' + this.x + ', ' + this.y + ')';
        }
      }
      
    • First use Babel Translate

      yarn compile-babel
      yarn run v1.16.0
      warning package.json: No license field
      $ babel src/es6.js -o es6_babel.js
        Done in 2.38s.
      
    • Reuse swc Translate

      yarn compile-swc  
      yarn run v1.16.0
      warning package.json: No license field
      $ swc src/es6.js -o es6_swc.js
      Successfully compiled 1 file with swc.
        Done in 0.63s.
      
    • The product comparison of the two

      // es6_babel
      "use strict";
      
      function _classCallCheck(instance, Constructor) {
               if (!(instance instanceof Constructor)) {
               throw new TypeError("Cannot call a class as a function"); } }
      
      function _defineProperties(target, props) {
               for (var i = 0; i < props.length; i++) {
               var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
      
      function _createClass(Constructor, protoProps, staticProps) {
               if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", {
               writable: false }); return Constructor; }
      
      var PI = 3.1415;
      var x = 1;
      var foo = 1,
          bar = 2,
          baz = 3;
      var node = {
              
        loc: {
              
          start: {
              
            line: 1,
            column: 5
          }
        }
      };
      var loc = node.loc,
          start = node.loc.start,
          line = node.loc.start.line;
      
      var sum = function sum(num1, num2) {
              
        return num1 + num2;
      };
      
      var s = new Set();
      [2, 3, 5, 4, 5, 2, 2].forEach(function (x) {
              
        return s.add(x);
      });
      
      var Point = /*#__PURE__*/function () {
              
        function Point(x, y) {
              
          _classCallCheck(this, Point);
      
          this.x = x;
          this.y = y;
        }
      
        _createClass(Point, [{
              
          key: "toString",
          value: function toString() {
              
            return '(' + this.x + ', ' + this.y + ')';
          }
        }]);
      
        return Point;
      }();
      
// es6 swc
function _classCallCheck(instance, Constructor) {
    
    if (!(instance instanceof Constructor)) {
    
        throw new TypeError("Cannot call a class as a function");
    }
}
function _defineProperties(target, props) {
    
    for(var i = 0; i < props.length; i++){
    
        var descriptor = props[i];
        descriptor.enumerable = descriptor.enumerable || false;
        descriptor.configurable = true;
        if ("value" in descriptor) descriptor.writable = true;
        Object.defineProperty(target, descriptor.key, descriptor);
    }
}
function _createClass(Constructor, protoProps, staticProps) {
    
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    if (staticProps) _defineProperties(Constructor, staticProps);
    return Constructor;
}
var PI = 3.1415;
var x = 1;
var foo = 1, bar = 2, baz = 3;
var node = {
    
    loc: {
    
        start: {
    
            line: 1,
            column: 5
        }
    }
};
var loc = node.loc, start = node.loc.start, _loc = node.loc, line = _loc.start.line;
var sum = function(num1, num2) {
    
    return num1 + num2;
};

var s = new Set();

[2,3,5,4, 5,2, 2].forEach(function(x1) {
    
    return s.add(x1);
});

var Point = /*#__PURE__*/ function() {
    
    "use strict";
    function Point(x2, y) {
    
        _classCallCheck(this, Point);
        this.x = x2;
        this.y = y;
    }
    _createClass(Point, [
        {
    
            key: "toString",
            value: function toString() {
    
                return "(" + this.x + ", " + this.y + ")";
            }
        }
    ]);
    return Point;
}();


//# sourceMappingURL=es6_swc.js.map
  • It can be seen from the above data that

    • In the comparison of packaged code , ESBuild The speed of (20ms) Far faster than Webpack(1680ms)
    • In the comparison of compiled code , swc Also on the babel It has obvious performance advantages (0.63s vs 2.38s).
    • What needs to be added is , The code used as an example is very simple , And in the comparison, all construction optimization strategies of various construction tools are not fully used , Just compare the speed of several tools under the most basic configuration , This and the list of tools benchmark The data will be different , And the construction speed is also related to the hardware performance / Runtime state .
  • ESBuild/swc So fast ? That's OK Webpack/Babel Throw it away ? Don't worry , current ESBuild and Swc It may not completely replace Webpack. But through this sharing, we may have a more comprehensive understanding of them , You can also explore future opportunities to use these new generation front-end tools at work

ESBuild/swc Positioning in front-end Ecology

  • In today's front-end world , New tools emerge in endlessly , Sometimes there are so many different tools that I can't tell what their functions are for a while , So let's study it first ESBuild/swc Role in today's front-end engineering system .

img

  • From the screenshot above, select several front-end engineering tools that we contact most frequently in our daily life :
    • Task Runner Task runner : Developers set up scripts for the build tool to complete the development 、 structure 、 A series of tasks in deployment , What we usually use is npm/yarn The script function of ; Earlier , More popular Gulp/Grunt Such a tool
    • Package Manager Package manager : This is no stranger to everyone , npm/Yarn/pnmp Help developers download and manage dependencies , It is essential for current front-end development .
    • Compiler/Transpiler compiler : In the market, many browsers only support ES5 In grammar , Babel In this way Comipler It is essential in front-end development ; If you use TypeScript Words , It also needs to pass tsc perhaps ts-loader Compile .
    • Bundler Packaging tools : Starting from the entrance set by the developer , Analyze module dependencies , Load and package various resources into 1 Tools for one or more files .
      • Loader: Because the front-end project contains various file types and data , It needs to be transformed into JS Modules can be used and built for packaging tools . JS Of Compiler And other types of files Loader May be collectively referred to as Transfomer.
      • Plugin: You can further customize the construction process , Transform the module ( Like compression JS Of Terser)
      • There are also some front-end building tools that are encapsulated or add additional functions based on general building tools , such as CRA/Jupiter/Vite/Umi

image.png

  • ESBuild The location is Bundler, But it's also Compiler( Yes Transform The power of code )

img

  • swc Call itself Compiler + Bundler, But at the moment spack It's not very easy to use

img

ESBuild/SWC Why so fast ?

  • Think about it , Go & Rust These two languages and JavaScript What's the difference between ?

ESBuild The implementation of the ( Reference resources ESBuild FAQ)

  • from Go Implement and compile into local code : Most of the Bundler It's all by JavaScript Realized , however CLI Apply to JIT Compiling languages are the worst performing . Each run Bundler When , JS Virtual machines are parsed from the perspective of running code for the first time Bundler( such as Webpack) Code for , No optimization information . When ESBuild In parsing JavaScript When , Node Still parsing Bundler Of JS Code

  • Heavy use of parallel computing : Go The design of the language itself attaches great importance to parallel computing , therefore ESBuild This will be exploited . There are three main links in the construction : analysis (Parsing), link (Linking) And code generation (Code generation), In the process of parsing and code generation, multi-core parallel computing will be used as much as possible

  • ESBuild Everything in the code is implemented from zero : Avoid performance problems caused by third-party libraries by implementing all logic by yourself , Unified data structure can reduce the cost of data conversion , And you can change the architecture as needed , Of course, the biggest disadvantage is that the workload is doubled .

    • It's amazing SpaceX This company , A large number of parts are produced in-house , Effectively reduce production costs img
  • Efficient use of memory : ESBuild In the implementation, try to reduce the transmission and conversion of data , ESBuild Minimize the impact on the whole AST The transfer , And reuse as much as possible AST data , Other Bundler Data formats may be converted back and forth at different stages of compilation (string -> TS -> JS -> older JS -> string…). In terms of memory storage efficiency Go Is better than JavaScript More efficient .

swc The implementation of the

  • swc Our official documents and websites are not correct swc A more specific explanation of the internal implementation , According to its Blog Some analysis in , babel The main reason for the slowness is its single threaded nature

A summary

  • from ESBuild and swc In the official resources of , The common point is to make good use of Parallel computing .JS Because the goal at the beginning of the design is to serve the browser scene well , So single thread & Event driven is not suitable for CPU Intensive Computing , and ESBuild/Rust It is also on this point that we are based on Node Our build tools have a systematic speed advantage .

How to use ESBuild/swc Efficiency improvement ?

  • Now we know that ESBuild/Rust What does it do , And what are the characteristics , How can we use... In our work ESBuild/swc To improve our development experience ?

Use ESBuild

  • ESBuild stay API Very concise on the level , The main API Only two. : Transform and Build, these two items. API Can pass CLI, JavaScript, Go Method call

    • Transform Mainly used for the conversion of source code , The input accepted is a string , The output is the converted code

      #  use CLI Way to call ,  take ts Code to js Code 
      echo 'let x: number = 1' | esbuild --loader=ts => let x = 1;
      
    • Build Mainly for building , The input accepted is one or more files

      //  use JS Mode call build Method 
      require('esbuild').buildSync({
              
        entryPoints: ['in.js'],
        bundle: true,
        outfile: 'out.js',
      })
      
  • ESBuild The type of content (Content Type) It includes ES File types that can be resolved during packaging , This and Webpack Of loader Similar concepts , The following example is used when packaging JSX Loader analysis JS file .

require('esbuild').buildSync({
    
  entryPoints: ['app.js'],
  bundle: true,
  loader: {
     '.js': 'jsx' },
  outfile: 'out.js',
})
  • ESBuild It also includes plug-in system , During the construction process (Transform API The plug-in cannot be used ) Change your build process through plug-ins
//  Plug in demonstration from the official website 
let envPlugin = {
    
  name: 'env',
  setup(build) {
    
    // Intercept import paths called "env" so esbuild doesn't attempt
    // to map them to a file system location. Tag them with the "env-ns"
    // namespace to reserve them for this plugin.
    build.onResolve({
     filter: /^env$/ }, args => ({
    
      path: args.path,
      namespace: 'env-ns',
    }))

    // Load paths tagged with the "env-ns" namespace and behave as if
    // they point to a JSON file containing the environment variables.
    build.onLoad({
     filter: /.*/, namespace: 'env-ns' }, () => ({
    
      contents: JSON.stringify(process.env),
      loader: 'json',
    }))
  },
}

//  The use of plug-in 
require('esbuild').build({
    
  entryPoints: ['app.js'],
  bundle: true,
  outfile: 'out.js',
  plugins: [envPlugin],
}).catch(() => process.exit(1))
  • Use in other tools ESBuild

    • If you think it's completely used now ESBuild Not yet mature , It can also be in Webpack Used in the system ESBuild Of loader To replace babel For code conversion , besides ,

      esbuild-loader

      It can also be used for JS & CSS Minimize your code .

      const {
               ESBuildMinifyPlugin } = require('esbuild-loader')
      
      module.exports = {
              
          rules: [
            {
              
              test: /.js$/,
              //  Use esbuild As js/ts/jsx/tsx loader
              loader: 'esbuild-loader',
              options: {
              
                loader: 'jsx',  
                target: 'es2015'
              }
            },
          ],
          //  Or use esbuild-loader As JS Compression tool 
          optimization: {
              
            minimizer: [
              new ESBuildMinifyPlugin({
              
                target: 'es2015'
              })
            ]
          }
      }
      
  • Be careful

Use Vite

  • Want to say 2021 New tools with high attention in the front-end circle in , Vite It can be said to be among the best , that Vite and ESBuild/swc What does it matter ?

img

  • Vite The core idea of is to use ESM+ Compile language tools (ESBuild) Speed up local operations

img

  • Vite In the development environment ESBuild Pre build , In the production environment Rollup pack , It is also possible to use ESBuild Build the production environment .

img

Use swc

  • Comilation

    • have access to swc Command line tools (swc/cli) coordination The configuration file Compiling Files

      # Transpile one file and emit to stdout
      npx swc ./file.js
      
      # Transpile one file and emit to `output.js`
      npx swc ./file.js -o output.js
      
      # Transpile and write to /output dir
      npx swc ./my-dir -d output
      
    • swc The core of swc/core There are three main types API

      • Transform: Code conversion API, Enter the source code => Output the converted code
      • Parse: Analyze the source code , Output AST
      • Minify: Minimize code
    • swc Also launched swc/wasm modular , Allows users to use... In a browser environment wasm Code conversion

    • If you want to Webpack Use under the system swc( replace babel), You can also use swc-loader

      img

  • Bundle

    • ️swc It also supports packaging , But at present, the function is not very complete , And there are many in use Bug. The author is currently trying to use spack Pack a simple React The application is not yet successful , You can't use it out of the box
    • img
    • at present swc Of Bundle The tool is called spack, Later, it will be renamed swcpack.
    • Packaging can be done through spack.config.js File

A little summary and thinking

Full text summary

  • ESBuild/swc It is a new generation of front-end tool written in compiled language , Yes JS The build tool written has a system level speed advantage

  • ESBuild Can be used to compile JS Code and module packaging , swc It is claimed that it can support both, but its packaging tool is still in the early stage of development

  • At present, these two tools cannot completely replace Webpack And other mainstream tools have developed a huge ecosystem in recent years

  • When the existing infrastructure is stable and the replacement cost is high , You can try to use new tools incrementally (loader) perhaps Vite This is based on ESBuild Construction tools for secondary packaging

Extended thinking

  • Continue to pay attention to the new development of front-end Ecology , Make good use of the open source community to improve R & D efficiency and experience .

  • While using new tools , Understand or participate in the technical principles behind it , Go It can be used as a server language , Rust It can be used as a system programming language , Learning a new language can open up a new Xintiandi , Beauty is not true ?

Reference material :

原网站

版权声明
本文为[phial03]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/187/202207060317123497.html