Skip to content

Babel 的原理

Babel 是一个 JavaScript 编译器,其提供对于 ES6 语法编写的代码转换成向后兼容的 JS 语法,从而可以运行的旧的环境或者浏览器中

其主要流程分为三个步骤: Parser 解析 -> 转换(transform) -> generate(生成)

在解析的流程中一般分为两个步骤: 语法分析 和 词法分析

  1. Parser 解析

在解析的过程中,通过分词将代码分为 空格、注释、字符串、数字、标识符、括号等,同时在词法分析的过程中对其进行语法分析从而构建成一个 AST 语法树。

  1. transform

这个阶段主要是通过一个个插件将上面的 AST 语法书按照对应的要求进行转换、删减、报错等

  1. generate

最后将转换后的 AST 语法树转换成源代码

@babel/core

babel 的核心提供 babel 整个转换流程的控制

@babel/preset-env

其是根据配置的目标环境或者开发框架提供对应的插件集合

  • @babel/preset-react
  • @babel/preset-typescript
json
{
  "presets": [
    [
      "@babel/preset-env",
      {
        // 指定兼容的浏览器版本
        "targets": {
          "ie": "11"
        },
        // 基础库 core-js 的版本,一般指定为最新的大版本
        "corejs": 3,
        // Polyfill 注入策略
        "useBuiltIns": "usage",
        // 不将 ES 模块语法转换为其他模块语法
        "modules": false
      }
    ]
  ]
}
  • targets : 这是根据 broswerlist 的写法,表示兼容 ie11。而 ie11 是不支持 Object.entries 这个 api 与箭头函数语法的,所以我们要通过@babel/preset-env 去转换。更多浏览器版本兼容写法可以参考 browserslist

  • corejs: 3, 表示使用哪个版本的 corejs 进行 polyfill, 可选值 2、3、false;其中版本 2 会有一些在原型上的 api 缺失,比如 Array.prototype.includes(),false 就是不进行 polyfill,这明显不符合我们的预期。

  • "useBuiltIns": "usage", 表示按需加载 polyfill, 比如上面我只用到了 Object.entries,那么打包时就只把这个 Object.entries 的 polyfill 注入到全局 window 上面,其他没有用到 polyfill 就不注入,大大减少打包后 bundle 的体积。

  • "modules": false ,意思是不将 ES 模块语法转换为其他模块语法,可选值有 'commonjs', 'amd', 'umd', 'systemjs' 'auto' ,我的 index.js 的写法是 esm 写法,这里 false 关闭就是保持 esm 写法,不然就会自动给你转 commonjs 写法。

其中一个最大的问题就是 preset-env 中 useBuildIns 配置的按需加载 polyfill 是会注入到全局的 window 上面的, 所以如果我们不需要全局注入那就使用

@babel/plugin-transform-runtime

json
{
  "plugins": [
    // 添加 transform-runtime 插件
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": 3
      }
    ]
  ],
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "ie": "11"
        },
        "corejs": 3,
        // 关闭 @babel/preset-env 默认的 Polyfill 注入
        "useBuiltIns": false,
        "modules": false
      }
    ]
  ]
}

@babel/polyfill