Skip to content

Babel 运行过程 #102

@coconilu

Description

@coconilu

概述

Babel是一个编译器。ECMAScript每年都会推出新的规范,为JavaScript带来新的特性。但是并不是所有浏览器都能及时实现这些新的特性。所以当我们想要享受使用JavaScript新特性而又不受限于浏览器的更新速度的时候,babel可以帮到我们。它可以把ES5+版本编译为ES5版本,从而让浏览器可以正常运行。

ES5+:

[1, 2, 3].map(n => n ** 2);

ES5:

[1, 2, 3].map(function (n) {
  return Math.pow(n, 2)
})

总结功能

  1. JavaScript的编译器
  2. 让JavaScript可以支持最新的语法,且不用担心兼容性
  3. 支持JSX和React
  4. 支持Flow和TypeScript的类型注解
  5. 支持拓展插件
  6. 支持调试
  7. 支持修改代码,已经被社区广泛使用,比如eslint对代码错误或风格的检查,IDE的错误提示、格式化、高亮、自动补全,UglifyJS压缩代码,webpack打包构建

与webpack的关系

webpack可以说是一个构建工具,它可以装配很多的插件或者编译器(loaders),babel是其中的一种转译器。

通过webpack构建项目,我们可以在打包JavaScript代码的时候,使用babel(babel-loader)把JavaScript新特性转换到JavaScript低版本可以理解的兼容模式。

babel编译过程

转译分为三个阶段:

  1. parsing,把代码解析成AST
  2. transforming,使用插件处理AST
  3. generating,把处理过的AST转化为最终的代码

polyfill

是对JavaScript新特性的补充。比如当我们需要把ES6转换为ES5的时候,ES6中的新对象和新API在ES5中是不存在的,那么运行的时候就会报错。

所以我们需要polyfill来补充新的对象和新的API,才能让代码在ES5环境中正常运行。

plugins

插件对于整个编译过程来说是重头戏,在第二步的使用需要使用插件去处理AST语法树。

插件不仅可以把ES6转化为ES5,还可以做很多别的事,只有想不到。

比如React就是通过插件支持JSX语法的。

presets

Presets 是可共享的 .babelrc 配置,也可以认为是一个 babel 插件的数组。

preset 和 plugin差别在于,preset的执行顺序是倒着的,plugin的执行顺序是正的。且Plugin 会运行在 Preset 之前。

官方预设了一些插件集:

babel-preset-env 相当于 es2015 ,es2016 ,es2017 及最新版本。
还有:babel-preset-20XX,babel-preset-react,babel-preset-flow

Plugin 会运行在 Preset 之前。Plugin 的执行顺序是从前往后,Preset 刚好相反。

配置

我们可以通过执行指令的时候传入一些配置,也可以通过.babelrc写好预设的配置。

Babel 会在正在被转录的文件的当前目录中查找一个 .babelrc 文件。 如果不存在,它会遍历目录树,直到找到一个 .babelrc 文件,或一个 package.json 文件中有 "babel": {} 。

配置Babel

Babel支持的配置方式有:

  1. babel.config.js
module.exports = function (api) {
  api.cache(true);

  const presets = [ ... ];
  const plugins = [ ... ];

  return {
    presets,
    plugins
  };
}
  1. .babelrc
{
  "presets": [...],
  "plugins": [...]
}
  1. package.json
{
  "name": "my-package",
  "version": "1.0.0",
  "babel": {
    "presets": [ ... ],
    "plugins": [ ... ],
  }
}
  1. .babelrc.js
const presets = [ ... ];
const plugins = [ ... ];

module.exports = { presets, plugins };

常用options字段

  1. env:指定在不同环境下使用的配置。比如production和development两个环境使用不同的配置,就可以通过这个字段来配置。env字段的从process.env.BABEL_ENV获取,如果BABEL_ENV不存在,则从process.env.NODE_ENV获取,如果NODE_ENV还是不存在,则取默认值"development"
  2. plugins:要加载和使用的插件列表,插件名前的babel-plugin-可省略;plugin列表按从头到尾的顺序运行
  3. presets:要加载和使用的preset列表,preset名前的babel-preset-可省略;presets列表的preset按从尾到头的逆序运行(为了兼容用户使用习惯)

Babel运作方式

Babel 6.x

支持如下配置:

const presets = [
  [
    "@babel/env",
    {
      targets: {
        edge: "17",
        firefox: "60",
        chrome: "67",
        safari: "11.1",
      },
    },
  ],
];

module.exports = { presets };

那么会在代码运行之前导入@babel/polyfill,用以支持新特性。

当然还可以使用useBuiltIns优化:

const presets = [
  [
    "@babel/env",
    {
      targets: {
        edge: "17",
        firefox: "60",
        chrome: "67",
        safari: "11.1",
      },
      useBuiltIns: "usage",
    },
  ],
];

module.exports = { presets };

会把代码:

Promise.resolve().finally();

转化为:

require("core-js/modules/es.promise.finally");

Promise.resolve().finally();

如果不使用useBuiltIns: "usage",我们将不得不把整个polyfill文件(@babel/polyfill)放在所有代码的前面。

Babel 7.x

Babel有所不同,会在代码运行之前导入如下:

import "core-js/shim"; // included < Stage 4 proposals
import "regenerator-runtime/runtime";

参考

官方文档
Babel中文文档
你真的会用 Babel 吗?
【JavaScript】深入理解Babel原理及其使用
Babel 用户手册
Babel 插件手册

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions