preface

It is my honor to help you, and your star is the biggest support for me!

Babel is known to be a conversion compiler compatible with older browsers with imperfect ES6 support.

Babel actually does two things:

  • The syntax conversion
  • The new API is polyfill compatible

So without further discussion, let’s go straight to the common scenarios for compatibility with older browsers.

Skip to the conclusion

practices

polyfill.io

If your project uses ES5 syntax, but uses some ES6+ API features, you can directly introduce:

<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
Copy the code

To be compatible with apis not supported by Web applications.

IO reads the User-Agent header for each request and returns the appropriate Polyfill for the requesting browser. You can also specify which features of polyfills to load. For more information, see the official documentation.

Advantages: Polyfill loads differently for each browser’s device, and the latest ones are fully compatible with ES6 browsers with a base polyfill size of 0.

Disadvantages:

  1. The async syntax will work on newer browsers, but will throw errors on older browsers.
  2. You can’t polyfill on demand for new features that your code uses, even if your Web application only uses themes6.array.fromFeatures,polyfill.ioIt is still possible to load all the unsupported features of the browser (es6.Promise, ES6.string.includes, etc.).

@babel/preset-env is loaded on demand

One of the drawbacks of polyfill. IO mentioned above is that it can’t be introduced on demand, so let’s introduce babel7@babel /preset-env

@babel/preset-env defaults to the necessary code syntax conversion and polyfill based on the compatible browser that.browserslist is filled in for

// .babelrc.js
module.exports = {
    presets: [["@babel/preset-env",
            
            {
                "modules": false.// Modules use es modules, not commonJS
                "useBuiltIns": 'usage'.// Default false, optional entry, usage}}]]Copy the code

Here’s a look at its new useBuiltIns option:

  1. False: Do not enable polyfill if in the business entryimport '@babel/polyfill', will be ignored.browserslistLoad all the polyfills in.


    Polyfill loads 284 feature packs

  2. Entry: Enable. You need to manually enable itimport '@babel/polyfill'RegeneratorRuntime undefined), according to.browserslistFilter out what you needpolyfill(similar topolyfill.ioScheme)


    There are 238 feature packs loaded in using Entry according to Browserslist (IE >10)

  3. Usage: Manual is not requiredimport '@babel/polyfill'(it is ok to add it, it is automatically removed when compiling), and will be based on.browserslist+ New apis used by business code are polyfilled as needed.


    Browserslist (IE >10)+ code used, only 51 feature packages loaded in

    Since we usually use the dependencies package of NPM for business development, Babel does not detect the dependencies package code by default.

    That is, if a dependency uses Array.from but its own business code doesn’t use the API, the polyfill built doesn’t have array. from, which can cause bugs for users of older browsers.

    To avoid this, open source third-party libraries are usually converted to ES5 when they are released.


UseBuiltIns: ‘usage’ mentioned above seemed to work perfectly for our needs, but when we built it we found:

// es6+ :
const asyncFun = async() = > {await new Promise(setTimeout, 2000)
  
  return 'returns string after 2s delay'
}
export default asyncFun
Copy the code

Following the above useBuiltIns: ‘usage’ configuration:

import "core-js/modules/es6.promise";
import "regenerator-runtime/runtime";

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); }}function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }

var asyncFun =
/*#__PURE__*/
function () {
  var _ref = _asyncToGenerator(
  /*#__PURE__*/
  regeneratorRuntime.mark(function _callee() {
    return regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            _context.next = 2;
            return new Promise(setTimeout, 2000);

          case 2:
            return _context.abrupt("return".'returns string after 2s delay');

          case 3:
          case "end":
            return _context.stop();
        }
      }
    }, _callee, this);
  }));

  return function asyncFun() {
    return _ref.apply(this.arguments); }; } ();export default asyncFun;

Copy the code

In the above code, we see that asyncGeneratorStep and _asyncToGenerator are inlined instead of imported.

That is, if you have multiple files that use async, each file will be inlined with the asyncGeneratorStep, _asyncToGenerator function.

This code is obviously repetitive, so what can be done to optimize it? The answer is @ the Babel/plugin – transform – runtime

@babel/plugin-transform-runtime

Babel inserts helpers code at the top of each required file, which can cause multiple files to have duplicate helpers code. The @babel/ plugin-transform-Runtime helpers option allows these modules to be removed

// .babelrc.js
module.exports = {
    "plugins": [["@babel/plugin-transform-runtime",
            {
                "corejs": false.// The default value can be left blank
                "helpers": true.// By default, it can not be written
                "regenerator": false.// Preset -env already uses the global regeneratorRuntime, and the transform-Runtime is no longer needed for the non-polluting global regeneratorRuntime
                "useESModules": true.Use es Modules helpers to reduce commonJS syntactic code}]],presets: [["@babel/preset-env",
            
            {
                "modules": false.// Modules use ES modules, not commonJS
                "useBuiltIns": 'usage'.// Default false, optional entry, usage}}]]Copy the code
// Add the newly configured code import"core-js/modules/es6.promise";
import "regenerator-runtime/runtime";
import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";

var asyncFun =
/*#__PURE__*/
function () {
  var _ref = _asyncToGenerator(
  /*#__PURE__*/
  regeneratorRuntime.mark(function _callee() {
    return regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            _context.next = 2;
            return new Promise(setTimeout, 2000);

          case 2:
            return _context.abrupt("return".'returns string after 2s delay');

          case 3:
          case "end":
            return _context.stop();
        }
      }
    }, _callee, this);
  }));

  return function asyncFun() {
    return_ref.apply(this, arguments); }; } ();export default asyncFun;
Copy the code

And as you can see, there’s no longer the inline helpers code, and we’re done.

conclusion

If there are no specific requirements, the best configuration to use Babel 7 is:

  1. NPM i-s@babel /polyfill @babel/runtime && NPM I -d @babel/preset-env @babel/plugin-transform-runtime

  2. Configuration. The babelrc. Js

// .babelrc.js
module.exports = {
    "plugins": [["@babel/plugin-transform-runtime",
            {
                "corejs": false.// The default value can be left blank
                "helpers": true.// By default, it can not be written
                "regenerator": false.// Preset -env already uses the global regeneratorRuntime, and the transform-Runtime is no longer needed for the non-polluting global regeneratorRuntime
                "useESModules": true.Use es Modules helpers to reduce commonJS syntactic code}]],presets: [["@babel/preset-env",
            {
                "modules": false.// Modules use ES modules, not commonJS
                "useBuiltIns": 'usage'.// Default false, optional entry, usage}}]]Copy the code

PS: For more information about the use of option configurations for @babel/preset-env and @babel/ plugin-transform-Runtime, please refer to my personal summary

Think and Explore (Modern Build)

The problem with this solution is that if you use the latest browsers, you don’t need any syntactic conversions or polyfills.

Can we use the usage scheme to transform + polyfill code as needed in the earlier version of the browser, and not perform any syntax conversion and polyfill in the newer version of the browser?

Must be able to!

Uild-es2015 Code in Production Today Uild-es2015 code in Production Uild-Es2015 code in production Toger.uild-es2015 code in production Toger.uild-es2015

The principle is embodied in the following code:

<script type="module" src="main.js"></script>
<script nomodule src="main.legacy.js"></script>
Copy the code

Browsers that support ES Module recognize type=” Module “and nomodule, load main.js and ignore main.legacy.js,

Browsers that do not yet support ES Module do the opposite and only load main.legacy.js.

Then it’s clear how to optimize:

  1. By configuring the Babel best practices described above, add the script labels between such code filesnomoduleattribute
  2. By configuring@babel/preset-envThe option totarget.esmodules = trueGenerate ES6+ code that can be parsed by modern browsers and add script tags to such code files without converting all syntax or adding polyfilltype="module"

Vue-cli 3.0 officially provides the Modern Build function

Create-react-app is not expected until the next iteration of version 3.0. At this stage, the implementation needs to write the Webpack plug-in to implement the Module /nomodule insertion