babel

109 阅读6分钟

Babel工作的三个阶段

image.png 更详细一点: •babylon 进行解析得到 AST

•plugin 用 babel-traverse 对 AST 树进行遍历转译,得到新的AST树

•用 babel-generator 通过 AST 树生成 ES5 代码

核心: babel-core: bable转译器本身,提供了babel的转译的API,如babel.transform等,用于对代码进行转译。wepack的babel的babel-loader就是调用这些API来完成转译的。 babylon : js的词法解析器。 -babel-traverse : 用于对AST(abstract syntax tree)抽象语法树的遍历,主要是给plugin用 babel-generator : 根据AST生成 代码

过程

Parse(解析):将源代码转换成更加抽象的表示方法(例如抽象语法树) 一般来说,Parse 阶段可以细分为两个阶段:词法分析(Lexical Analysis, LA)和语法分析(Syntactic Analysis, SA)。词法分析之后,代码就已经变成了一个 Tokens 数组了,现在需要通过语法分析把 Tokens 转化为上面提到过的 AST。 Transform(转换):对(抽象语法树)做一些特殊处理,让它符合编译器的期望 Babel 对于 AST 的遍历是深度优先遍历,对于 AST 上的每一个分支 Babel 都会先向下遍历走到尽头,然后再向上遍历退出刚遍历过的节点,然后寻找下一个分支。 Babel 会维护一个称作 Visitor 的对象,这个对象定义了用于 AST 中获取具体节点的方法。

var visitor = {
    ArrowFunction(path) {
        path.replaceWith(t.FunctionDeclaration(id, params, body));
    }
};

Generate(代码生成):将第二步经过转换过的(抽象语法树)生成新的代码(babel-generator)

常见配置项

babel-polyfill

7.4+版本已废弃,取而代之的是core-js+regenerator,regenerator是async await generator等功能的实现 babel-polyfill 是为了模拟一个完整的ES2015+环境,babel-polyfill是一次性引入你的项目中的,并且同项目代码一起编译到生产环境。而且会污染全局变量。像Map,Array.prototype.find这些就存在于全局空间中。之所以出现他,是因为Babel默认只转换js语法,像Map,Set等API是不做转换的

为什么会被废弃?

1.babel-polyfill是整体引入的,会导致项目体积增大,(babel7之后可以使用useage解决) 2.对于静态方法会修改类函数,对于实例函数会直接修改原型

babel-runtime

babel-runtime 更像是一种按需加载的实现,比如你哪里需要使用 Promise,只要在这个文件头部 import Promise from 'babel-runtime/core-js/promise' 就行了

babel-transform-runtime(@babel/plugin-transform-runtime

作用:提取辅助函数和内置函数,自动polyfill不污染不污染全局变量 通常仅在开发时使用,但是运行时最终代码需要依赖 @babel/runtime,所以 @babel/runtime 必须要作为生产依赖被安装)

babel-runtime不会污染全局空间和内置对象原型。事实上babel-runtime是一个模块,你可以把它作为依赖来达成ES2015的支持。 比如环境不支持Promise,你可以在项目中加入

require(‘babel-runtime/core-js/promise’)

可以实现按需加载,但是在实际项目开发过程中,我们往往会写很多新的es6 api,每次都要手动引入相应的包比较麻烦,维护起来也不方便,每个文件重复引入也造成代码的臃肿。 要解决这个问题,就要用到 babel-plugin-transform-runtime这个插件是用来复用辅助函数的。它会分析我们的 ast 中,是否有引用 babel-rumtime 中的垫片(通过映射关系),如果有,就会在当前模块顶部插入我们需要的垫片。 babel-runtime有个缺点,它不模拟实例方法,即内置对象原型上的方法,所以类似Array.prototype.find,你通过babel-runtime是无法使用的

babel-preset-env

不设置的话默认是2015~2020 能根据当前的运行环境,自动确定你需要的 plugins 和 polyfills。通过各个 es标准 feature 在不同浏览器以及 node 版本的支持情况,再去维护一个 feature 跟 plugins 之间的映射关系,最终确定需要的 plugins(@babel/preset-env 会根据你配置的目标环境,生成插件列表来编译。)

{
  "presets": [
    ["env", {
      "targets": {
        "chrome": 52,
        "browsers": ["last 2 versions", "safari 7"]
      },
      "modules": false,
      "useBuiltIns": "usage",// 是否开启自动支持 polyfill,它能自动给每个文件添加其需要的poly-fill 开启此选项必须保证已经安装了babel-polyfills
      "debug": false
    }]
  ]
}

此时Babel 转换了浏览器不支持的箭头函数和 Class,但是 Promise 并没有变化。这是因为 Babel 只转换不兼容的新语法,而对新的 API,如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise、Object.assign() 等,是不会转换的。这时候就需要 polyfill 了

polyfill的方式:useBuiltIns

core-js

core-js 它是JavaScript标准库的 polyfill(垫片/补丁), 新功能的es'api'转换为大部分现代浏览器都可以支持运行的一个'api' 补丁包集合。

core-js在babel-preset-env中的应用

2.1."useBuiltIns: false" 此时不对'api 垫片做操作',你可以引入你想要的处理'es api'的垫片例如此时 需要在文件引入'import "core-js/stable"' 和 'import "regenerator-runtime/runtime"',则无视配置的浏览器兼容, 引入所有的 polyfill。 2.2."useBuiltIns: entry"根据配置的浏览器兼容,引入浏览器不兼容的 polyfill。需要在入口文件手 文件引入'import "core-js/stable"' 和 'import "regenerator-runtime/runtime"',会自动根据 browserslist 替换成浏览器不兼容的所有 polyfill。这里需要指定 core-js 的版本(也就是要设置'corejs'字段版本) 2.3.useBuiltIns: usage" 会根据配置的浏览器兼容,以及你代码中用到的 API 来进行 polyfill,实现了按需添加。

总结 推荐使用."useBuiltIns: usage" 配置他自己动会做帮我们引入'corejs垫片',不用在手动全局引入,并且会对 指定浏览器版本进行配合

babel7

截止至2022 6月最新的Babel版本

  • 不再只是stage-x这种形式
  • 主推env,减少配置成本
  • 配置文件新增了babel.config.js
  • 修改包名到Babel命名空间下

截屏2022-11-13 下午6.57.13.png

现代化构建

基于大部分现代浏览器对es6语法的支持,不再需要向下兼容到很低的浏览器版本。 1.设置babel的target为esModule,Babel会忽略browsers的配置,而是基于浏览器对esModule的支持生成代码

target:{
  esmodule:true
  }

2.基于现代浏览器<script type='module'/>以及<script nomodule/>来生成两个版本的代码 需要注意的是第一个标签会有跨域的问题。

3.在脚手架加入插件生成上述的结构,并且生成两个版本的代码,新的插入到前者,旧的插入到后者