This article is based on Babel 7. If you have been using Babel 6 before, you can read this article about upgrading Babel 7 modules

How does Babel work?

The compilation process of Babel can be divided into three stages:

  • Parsing: Parsing code strings into abstract syntax trees.
  • Transformation: Transforms the abstract syntax tree.
  • Code Generation: Generates Code strings from the transformed abstract syntax tree.

@babel/ Parser (Babylon)

Use the parse API in @babel/core for lexical analysis (word segmentation + syntax analysis) to Transform into AST for Transform. Tip: can anyone interested in reading this article under the understanding: www.alloyteam.com/2017/04/ana… This article focuses on Babel’s practice in the project.

Transformation: A process that the consumer can determine

Use the transform API in @bable/core to escape; The familiar plug-ins are used in this process. If no plug-ins are used at this stage, Babel outputs the as-is code during the generation stage.

  1. How do plugins and Presets work together?
  • Preset is an official Babel plugin set and can be thought of as a plugin set Preset to ease verbose configuration. Each Preset builds only the content approved for that year;
  • Plugin /preset execution sequence: For escaped nodes, Babel first follows the plugins array configuration of the configuration file, and then reads each preset in the presets in reverse order, as shown in the following figure:

A collection of plugins for each stage of Babel

  • @babel/plugin-proposal-class-properties
  • @babel/plugin-proposal-decorators
  • @babel/plugin-proposal-export-default-from
  • Babeljs. IO/docs/en/bab…
  1. Common default presets:
  • @babel-preset-env: Presets ES2015+ to ES5 based on user-defined configuration items. For details, see the @babel-preset-env configuration description
  • @ Babel/preset – react: escape the react code, details can be read: www.babeljs.cn/docs/babel-…
  • Babel-preset -typescript: Escapes typescript code. Read this article to learn more about the plugin: iamturns.com/typescript-… For react projects, @babel/preset-env + @babel/preset- React is ok for presets, but with the popularity of TypeScript, Babel 7 brings @babel/preset-typescript to developers for TS development. As you can see from the following figure, this plugin is growing rapidly.

  • Configuration is recommended
// react + typescript "presets": [ [ "@babel/preset-env", { "targets": "Modules ":" CJS ", "useBuiltIns": "Usage"}], "@babel/react", "@babel/preset-typescript", # this preset needs to be installed if projects develop in typescript]Copy the code

Generation:

Generate ES5 code via AST with babel-Generator; TIP: If you configure Babel without using any presets and plugins, the generated code is the same as the original code.

What you need to know @babel-polyfill

Background:

  1. Since Babel itself is only responsible for syntax transformations, such as converting ES6+ syntax to ES5. However, if there are objects or methods that the browser does not support, for example:
    • Global objects: Promise, WeakMap, etc.
    • Global static functions: array. from, Object.assign, etc.
    • Example methods: array.prototype.includes, etc. At this point, you need to introduce @babel-Polyfill to simulate implementing these objects and methods.
  2. @babel-Polyfill consists of core-JS and Regenerator.
    • Babel-polyfill: Provides mock implementations of various objects and methods newly defined in specifications such as ES5, ES6, ES7, etc.
    • Regenerator: Provides generator support if generator and async functions are used in application code.

Inadequate:

  1. In the past, polyfill was used in projects. There were two common ways to do polyfill:
    • Import @babel-polyfill in the entry js file
    • In the Entry configuration of webpack, write @babel-Polyfill
  2. @babel-polyfill will pollute the global space and the built-in prototype objects like Map, array.prototype.find exist in the global space.

@babel-polyfill VS @babel-runtime

@babel-Runtime background appears

The deficiencies of @babel-Polyfill are not significant in daily business development, but there are potential problems with the introduction of @babel-Polyfill in common third-party library development: For example, in our project, we have customized a new API implementation, but the introduced third-party general library uses Polyfill and implements the same method, so our original method will be overwritten (at least there is a risk). In order to avoid such problems, Babel introduces @babel-Runtime to realize most of the functions of @babel-polyfill. It separates the global built-in objects that developers rely on into modules and imports them through modules to avoid contamination of the global scope. So, if you are a development library or tool, you can use @babel-Runtime.

How to implement polyfill using @babel-Runtime

  1. Install @babel/plugin-transform-runtime and @babel-runtime
# babel-plugin-transform-runtime code conversion for the build process
npm install --save-dev babel-plugin-transform-runtime 
npm install --save babel-runtime
Copy the code
  1. Add @babel/plugin-transform-runtime to plugins
{
  "plugins": [["@babel/plugin-transform-runtime",
      {
        "absoluteRuntime": false."corejs": false."helpers": true."regenerator": true."useESModules": false}]]Copy the code

@babel-runtime + @babel/plugin-transform-runtime

The @babel/ plugin-transform-Runtime plugin does three things:

  1. Core-js aliasing: automatically import @babel-Runtime /core-js, map global static methods and global built-in objects to corresponding modules;
# js
var sym = Symbol();

var promise = new Promise();

console.log(arr[Symbol.iterator]());
After the transformation of #
"use strict";

var _getIterator2 = require("@babel/runtime-corejs2/core-js/get-iterator");

var _getIterator3 = _interopRequireDefault(_getIterator2);

var _promise = require("@babel/runtime-corejs2/core-js/promise");

var _promise2 = _interopRequireDefault(_promise);

var _symbol = require("@babel/runtime-corejs2/core-js/symbol");

var _symbol2 = _interopRequireDefault(_symbol);

function _interopRequireDefault(obj) {
  return obj && obj.__esModule ? obj : { default: obj };
}

var sym = (0, _symbol2.default)();

var promise = new _promise2.default();

console.log((0, _getIterator3.default)(arr));
Copy the code
  1. Helper aliasing: Remove inline utility functions and import them through the @babel-Runtime /helpers module
# js
class Person() {}

After the transformation of #
"use strict";

var _classCallCheck2 = require("@babel/runtime/helpers/classCallCheck");

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);

function _interopRequireDefault(obj) {
  return obj && obj.__esModule ? obj : { default: obj };
}

var Person = function Person() {
  (0, _classCallCheck3.default)(this, Person);
};
Copy the code
  1. Regenerator aliasing: used to automatically import @babel-Runtime/Regenerator module when async/generator is used in a project
# js
function* foo() {}

After the transformation of #
"use strict";

var _regenerator = require("@babel/runtime/regenerator");

var _regenerator2 = _interopRequireDefault(_regenerator);

function _interopRequireDefault(obj) {
  return obj && obj.__esModule ? obj : { default: obj };
}

var _marked = [foo].map(_regenerator2.default.mark);

function foo() {
  return _regenerator2.default.wrap(
    function foo$(_context) {
      while (1) {
        switch ((_context.prev = _context.next)) {
          caseZero:case "end":
            return _context.stop();
        }
      }
    },
    _marked[0],
    this
  );
}
Copy the code

@babel-runtime + @babel/plugin-transform-runtime disadvantages

The @babel-Runtime and @babel/ plugin-transform-Runtime implementations do not extend instance methods (e.g. Array.prototype.includes()). To put it simply, if you use polyfill with @babel-Runtime implementations, you will get an error that some of the prototype methods you are using are not supported, which is not acceptable, so I recommend using @babel-polyfill for project development.

@babel/preset-env Preset is brought by @babel-polyfill for on-demand reference

TIP: See the next section for more on @babel/preset-env by using @babel/preset-env in the presets, Set useBuiltInts to Usage or entry for on-demand import; When used with @babel/preset-env (note that @babel-Polyfill still needs to be installed.)

  1. If useBuiltIns: ‘usage’ is specified in.babelrc, do not introduce @babel-polyfill in webpack.config.js and entry js files.
  2. If useBuiltIns: ‘entry’ is specified in.babelrc, @babel/polyfill is included at the top of the application entry point by require or import.
  3. If the useBuiltIns key is not specified or useBuiltIns: false is explicitly set in.babelrc, add @babel/polyfill directly to the configuration corresponding to webpack.config.js.

What you need to know about @babel/preset-env

Using the trend

Since its release on September 17, 2018, weekly downloads have been steadily increasing, and it is recommended to use @babel/ Preset -env in the project.

A simple introduction

@babel-preset-env will automatically fix Babel and polyfill, build ES2015 and above languages based on your actual browser and runtime environment, without configuration, @babel-preset-env behaves the same as babel-preset-latest (or can be said as babel-preset- ES2015, babel-preset- ES2016, babel-preset- ES2017) Combined to show consistency).

advantages

  1. The required Babel plug-in can be automatically adopted depending on the target environment of the configuration;
    • Simplifies Babel configuration
    • Necessary plug-ins are introduced to optimize the package volume.
  2. Support polyfill configuration, introduce relevant polyfill on demand, optimize code volume; TIP: You can go to www.npmjs.com/package/@ba… To check the plugins that the @babel/preset-env version of the project depends on, and ensure that the new JS language features used in the code are supported. If not, upgrade @babel/preset-env or install the corresponding plugin to support it.

Inadequate:

For experimental properties (babel-preset-latest not supported), plugins or presets need to be installed and configured manually. IO /docs/en/plu… To query the relevant experimental attributes of the corresponding plug-in, to introduce support for business development;

Configuration guide

Browser Configuration

  • Polyfill and code translation is required for the last two versions of each browser and for versions of IE greater than or equal to 7.
"babel": {
  "presets": [["env",
      {
        "targets": {
          "browsers": ["last 2 versions"."ie >= 7"]}}]]},Copy the code
  • Support browsers with more than 5% market share.
"targets": {
  "browsers": "5%" >
}
Copy the code
  • Specify browser version
"targets": {
   "chrome"56} :Copy the code

Polyfill configuration: useBuiltIns (default false)

@babel-preset-env only supports conversion of syntax by default, useBuiltIns configuration is required for conversion of APIS and instance methods to provide polyfill for new features in the standard library, support for built-in objects, static methods, instance methods, generator functions. @babel-preset-env can be used to introduce needed polyfills based on a particular environment.

  • Optional values include: “usage” | | “entry” false, the default is false, said no polyfills processing;
  • Entry: According to the browser version in Target, polyfills are split into fills. Only polyfills that are not supported by browsers are imported.
  • Usage: Check the usage of ES6/7/8 etc., only load polyfills;

Node.js configuration is relevant

If you are compiling your Node.js code via Babel, babel-preset-env is useful, set “targets. Node “to “current”, and favor the current running version of nodejs:

const presets = [
  [
    "@babel/env",
    {
      node: 'current',}]]; module.exports = { presets };Copy the code

Spec: Enables more spec compliant conversions, but slower, default to false

Loose: Whether to use loose mode. The default value is false

Modules: the ES6 module into other module specification, optional “adm” | “umd” | “systemjs |” # # # # “commonjs” | “CJS |” false, the default is false

Debug: Enables debug. The default value is false

Include: An array of plugins to use

Exclude: An array containing unused plugins

Babel 6 migrates Babel 7

It is recommended that you familiarize yourself with the changes that Babel 7 brings before the migration so that you can make specific optimizations and specific configuration changes in your project

Babel 7 brings changes

Being familiar with important changes can help us better configure projects, use the latest supported features, and improve coding efficiency

  1. The default with env was released over a year ago and now replaces all usage:
  • babel-preset-es2015
  • babel-preset-es2016
  • babel-preset-es2017
  • babel-preset-latest
  1. Remove stage default usage and migrate to github.com/babel/babel… Upgrade; Substitution can also be made using the command line with babel-upgrade
  2. Remove suggestion from @babel-polyfill: see here: github.com/babel/babel…
  3. Package renamed: Babylon is now renamed @babel/parser
  4. One of the most important changes to Babel 7 is the addition of namespaces to all packages. This reduces the number of accidental or intentional naming problems and makes it clear that Babel has a naming convention that distinguishes it from the rest of the community. For example, babel-preset-env is renamed @babel/preset-env;
  5. Replace -transform with -propoal in all non-annual TC39 plugins to better distinguish whether a proposal is javascript official. Such as:
  • @ Babel/plugin – tranform – function – bind with @ Babel/plugin – proposal – function – the bind
  1. Remove the year from the package. Some plugins have -es2015 characters and so on. @babel/plugin-transform-es2015-classes was replaced with @babel/plugin-transform-classes
  2. Presets that separate React and Flow
  3. The configuration options for Babel will now be more stringent than in previous Babel6 versions. A comma-separated list like this: “presets”: “ES2015, ES2016” worked in previous versions, but will now work in arrays. But this doesn’t affect the CLI, which can still use comma-separated strings.
# right
{
    "presets": ["@babel/preset-env"."@babel/preset-react"]}# wrong
{
   "presets": "@babel/preset-env, @babel/preset-react"
}
Copy the code
  1. From now on all plug-ins and default exposures must be a function, not an object, to help with caching.
  2. JSX Fragment Support (<>)
render() {
  return( <> <ChildA /> <ChildB /> </> ); } / / output 👇render() {
  return React.createElement(
    React.Fragment,
    null,
    React.createElement(ChildA, null),
    React.createElement(ChildB, null)
  );
}
Copy the code
  1. TypeScript Support (@babel/preset- TypeScript) pre-use (React code block with type declarations)
interface Person {
  firstName: string;
  lastName: string;
}

function greeter(person : Person) {
  return "Hello, " + person.firstName + "" + person.lastName;
}
Copy the code

After use (removes type declaration)

function greeter(person) {
  return "Hello, " + person.firstName + "" + person.lastName;
}
Copy the code
  1. Automatic Polyfilling (experimental) In Babel 6, polyfill is supported in the following ways:
import "@babel/polyfill";
Copy the code

But it includes the entire Polyfill, and if your browser already supports it, you may not need to import everything. This is the same problem that @babel/preset-env is trying to solve syntactically, so we apply it here to polyfill. The useBuiltins: “Entry” option accomplishes this by splitting the original import into only necessary imports. But we can do better by importing just the polyfill used in the code base. The option “useBuiltIns:” Usage “is our first attempt to enable something similar (detailed). For a detailed description of @babel/preset-env, refer to the @babel-Preset -env configuration description. Understand each configuration and use the new Babel API to configure and optimize the project.

Recommended migration steps:

Step 1: Go to the root directory of the project: Execute the Babel upgrade script and make the following three types of changes:

  1. Replace babel-xx with @babel/xx, with the new plug-in
  2. Upgrade babel-loader to a version that supports Babel 7
  3. Install deprecated stage-specific default plugins that correspond to each stage’s plugin query library
# NPM is a new feature that runs commands directly instead of installing them locally
npx babel-upgrade --write

# or the normal way
npm i babel-upgrade -g
babel-upgrade --write
Copy the code

Take a project that relies on Babel 6 as an example:

  • Babel-polyfill transforms the package namespace and upgrades it at the same time. For users, they just need to modify the corresponding package in the configuration file. There are no other pain points, so it is very friendly.
  • Install babel-preset-stage-1, a preset that relies on plugins to ensure that the preset function is available
    • @babel/plugin-proposal-export-default-from
    • @babel/plugin-proposal-logical-assignment-operators
    • @babel/plugin-proposal-optional-chaining
    • @babel/plugin-proposal-pipeline-operator
    • @babel/plugin-proposal-nullish-coalescing-operator
    • @babel/plugin-proposal-do-expressions
  • Convert all TC39 proposals to -proposals such as babel-plugin-transform-decorators-legacy -> @babel/plugin-proposal-decorators

Step 2: Synchronize the configuration in Babel file according to the previous package.json file modification, run the development environment, according to the Webpack error one by one to solve

  • Babel-plugin-module-resolver: After Babel is upgraded to Babel 7, an error occurs in babel-plugin-module-resolve V2, which needs to be upgraded to V3.
    • Issue:github.com/tleunen/bab…

  • The @babel/plugin-proposal-pipeline-operator plug-in fails to add related configuration items
    • Treatment scheme:
 ["@babel/plugin-proposal-pipeline-operator", {
    # Since the project does not use the pipe character, I will give a minimal attribute value here, other projects can be adjusted according to specific needs
      "proposal": "minimal"
    }]
Copy the code
  • An error is reported when ES6 modules is mixed with commonJS

Step 3: Optimize project Polyfill configuration

Babel 7 comes with @babel/preset-env, which extends the configuration of polyfill to optimize the size of spacers to load on demand, see the @babel-preset-env configuration description for details

  1. Remove @babel-polyfill from import file
  2. Set @babel/preset-env useBuiltIns to Usage for preset loading on demand

  1. Set @babel/ Preset -env’s corejs property to support not only stable ECMAScript Features but also proposal ECMAScript Features

Configure Babel to support TypeScript environment development

See my JavaScript Project Migration TypeScript practice sharing article to do this (I will reorganize and publish to Nuggets the rest of the time).

  1. Install @babel/preset-typescript for the Babel configuration
# .babelrc
# yarn add @babel/preset-typescript
"presets": [["@babel/env",
      {
        "targets": "cover 95%, safari >= 7"."modules": "cjs"."corejs": 2."useBuiltIns": "usage",}],"@babel/react"."@babel/typescript"].Copy the code
  1. Create the TypeScript configuration file tsconfig.json
{
  "compilerOptions": {
    "module": "esnext"."target": "es6"."allowSyntheticDefaultImports": true."baseUrl": "."."sourceMap": true."outDir": ".. /client-dist"."allowJs": true."jsx": "react"."noEmit": true."moduleResolution": "node"."isolatedModules": true."experimentalDecorators": true."emitDecoratorMetadata": true// Vs Code needs to find the corresponding path at development time, the actual reference is configured in 'webpack'alias`
    "paths": {}},# This design is due to the historical reasons of our project. Js business code is quite large. When we run CI TSC to do type check, we only use TSX? Type of file
  "include": ["src/**/*.ts"."src/**/*.tsx"]."exclude": [
    "node_modules"]}Copy the code
  1. Webpack configuration changes to support the TypeScript development environment
    • Babel-loader extension to.ts? X file parsing
    • High similarity extensions add support for TS and TSX files

Package. json extends the TypeScript type checking command

# package.json
{
  "scripts": {
     "tsc": "tsc --noEmit"}}Copy the code
  1. Extend typescript-related Eslint configurations for code specification constraints
    • Parser replaced babel-eslint with @babel-eslint/parser
    • The extends property adds constraints on plug-ins for typescript code
      • prettier
      • prettier/react
      • prettier/@typescript-eslint
    • The plugins attribute extends the @typescript-esLint plugin

conclusion

Babel is not a black box. It is a toolset that provides developers with a variety of options. It only takes a little time to understand it. We can use it safely in our projects, with the support of new language features that the Babel team is constantly working to bring to us; If you have any questions, you can leave a comment directly to me.

reference

Gitissue.com/issues/5c18… Juejin. Cn/post / 684490… Juejin. Cn/post / 684490… Segmentfault.com/q/101000000… www.jianshu.com/p/d078b5f30… Jsweibo. Making. IO / 2019/08/05 /… Juejin. Cn/post / 684490… Blog. Hhking. Cn / 2019/04/02 /… Github.com/sorrycc/blo… Github.com/Kehao/Blog/… Juejin. Cn/post / 684490… Jsweibo. Making. IO / 2019/08/09 /… Github.com/SunshowerC/… Zhuanlan.zhihu.com/p/43249121 pdsuwwz. Making. IO / 2018/09/29 /… – Babel upgrade tread pit juejin.cn/post/684490… Juejin. Cn/post / 684490… Github.com/lmk123/blog… Segmentfault.com/a/119000001… Segmentfault.com/a/119000001… www.zcfy.cc/article/bab… Juejin. Cn/post / 684490…