JS代码编译—Babel

什么是babel

官网:javascript编译器。最初从 6to5 库发展而来。

效果:

  • 将es6语法,转换成es5语法。比如将箭头函数,转化成普通函数。
  • 默认只转换语法,不转换API,比如 Iterator、Set、Map、Reflect

过程:解析(parsing)、转换(transforming)和生成(generating)

核心组成

  • @babel/cli :语法转换的核心依赖包,它本身不具备转换处理能力,只提供上层的API。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    var babelCore = require("@babel/core");
    var sourceCode = `let fn = (num) => num + 1`;

    var options = {
    code: true, //是否生成解析的代码
    ast: true, //是否生成抽象语法树
    sourceMaps: true, //是否生成sourceMap
    plugins: [], // 无插件
    presets: [], // 无preset
    };

    babelCore.transform(sourceCode, options, function (err, result) {
    console.log(sourceCode);
    console.log(result.code);
    console.log(result.map);
    console.log(result.ast);
    });

    转换功能被拆分到各个插件中,比如 @babel/plugin-transform-react-jsx

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    // 转换react.js代码
    const fs = require('fs')
    const babel = require("@babel/core")

    /* 第一步:模拟读取文件内容。 */
    fs.readFile('./element.js',(e,data)=>{
    const code = data.toString('utf-8')
    /* 新版转换 jsx 文件:转换后为 jsx(xxx, xxx) 这种 */
    const result1 = babel.transformSync(code, {
    plugins: [
    [
    "@babel/plugin-transform-react-jsx", {
    "runtime": "automatic"
    }
    ]
    ],
    });
    /* 第三步:输出转换后的内容 */
    console.log(result1.code)
    console.log('-'.repeat(40))
    /* 老版转换 jsx 文件:转换后为 React.createElement 这种 */
    const result2 = babel.transformSync(code, {
    plugins: ["@babel/plugin-transform-react-jsx"],
    });
    /* 第三步:输出转换后的内容 */
    console.log(result2.code)
    })
  • @babel/parser :将源码解析成AST

  • @babel/generator :将转换好的AST生成新代码。

  • @babel/cli :内置babel的CLI命令

    1
    2
    npm install --g @babel/cli
    babel input.js -o output.js

配置文件

  • 位置:package.json,.babelrc,.babelrc.js,babel.config.js 都可
  • 常用配置:可以参考前面代码中的 babelCore.transform 函数的入参

插件系统(plugins)

Plugin类型:

  • 语法插件:
    • 阶段:解析(parsing)
    • 作用:不具备任何功能性,只是将对应语法的解析功能打开。解析语法的功能均在 @babel/parser 中实现。
    • 命名规则:以 @babel/plugin-syntax 开头,比如 @babel/plugin-syntax-typescript@babel/plugin-syntax-jsx
  • 转换插件:
    • 阶段:转换(transforming)
    • 作用:将原代码的AST转成目标代码的AST
    • 命名规则:
      • @babel/plugin-transform 开头:正式转换插件。比如 @babel/plugin-transform-runtime
      • @babel/plugin-proposal 开头:提案转换插件。比如 @babel/plugin-proposal-class-properties

预设系统(presets)

一个个去配置各种插件,太过麻烦,浪费精力。因此,出现了 presets 的概念,它可以理解成「插件套餐」,每一个preset内部,都组合搭配了多个插件供开发者使用。

语法转换(**@babel/preset-env**)

作用:

一个智能预设,能直接通过指定浏览器版本,来将代码语法降到对应浏览器支持的写法。

默认情况下,@babel/env等于@babel/preset-es2015、@babel/preset-es2016和@babel/preset-es2017三个套餐的叠加

写法:

1
2
3
4
5
6
7
8
9
10
11
12
{
"presets": [
[
"@babel/preset-env",
{
"targets": "last 2 Chrome versions", // 兼容最新2个版本的chrome
"useBuiltIns": "entry", // 是否引入polyfill,可以配置三个值:false(不引入)、usage(按需引入)和entry(项目入口处引入)
"corejs": "3.22" // 引入哪个版本的core-js,可以选择2(默认)或者3,只有当useBuiltIns不为false时才会生效
}
]
]
}

API转换

@transform/polyfill vs @transform/runtime

  • polyfill:改造目标浏览器,让你的浏览器拥有本来不支持的特性
  • runtime:改造你的代码,让你的代码能在所有目标浏览器上运行,但不改造浏览器

举例:

在IE中,如果引入的是polyfill,那么打开控制执行 Reflect.ownKeys 是可以的;如果引入的是runtime,会报错。

分析:

  • polyfill:修改的是全局对象。如果检测到不存在,会修改 window 的属性。
  • runtime:将你的代码中对新API的使用,换个新名字,然后实现这个函数。比如 Refelct.ownKeys 可能会被换成 _es6_reflect_ownkeys ,并且实现 _es6_reflect_ownkeys

polyfill 方案

安装:

1
2
npm install --save-dev @babel/cli @babel/core @babel/preset-env @babel/plugin-transform-runtime
npm install --save @babel/runtime core-js@3

配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"presets": [
[
"@babel/preset-env", {
"useBuiltIns": "usage",
"corejs": 3
}
]
],
"plugins": [
["@babel/plugin-transform-runtime"]
]
}

总结:

  • 不推荐。会修改全局对象,不如 runtime-corejs3 方案。
  • 不再使用 @babel/polyfill ,而是根据需要安装 core-js@3regenerator-runtime
    • core-js@3:处理Array.from、Object.assign 等方法
    • regenerator-runtime:处理生成器函数(function *)
  • 使用 @babel/runtime ,避免重复生成babel的「辅助函数」,减少生成代码体积,比如 _classCallCheck_createClass

Untitled.png

  • @babel/runtime 要配合 @babel/plugin-transform-runtime 使用。此时, @babel/plugin-transform-runtime 的作用是是移除辅助函数,将其替换为 @babel/runtime/helpers 中函数的引用

runtime-corejs3 方案(推荐)

安装:

1
2
npm install --save-dev @babel/cli @babel/core @babel/preset-env @babel/plugin-transform-runtime
npm install --save @babel/runtime-corejs3

配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"presets": [
[
"@babel/preset-env" // 不再使用polyfill,无需配置 useBuiltIns & corejs ,
]
],
"plugins": [
[
"@babel/plugin-transform-runtime", {
"corejs": 3
}
]
]
}

总结:

  • 推荐,不会污染全局变量
  • @babel/runtime-corejs3 相当于 @babel/runtime 加上不污染全局的 core-jASTs@3

Untitled.png

babel实战

参考资料

bookmark

bookmark

bookmark