# babel是什么

babel 是一个JavaScript编译器,主要作用是将ECMAScript2015+ 版本的js代码转换为向后兼容的js语法,以便能够运行在当前和旧版本的浏览器环境中

# babel核心概念

babel

babel 能做什么?

  • 语法转换
  • 通过Polyfill方式垫平不同浏览器或者不同环境下的差异,让新的内置函数、实例方法等在低版本浏览器中也可以使用。(@babel/polyfill模块)
  • 源码转换

# 核心库 @babel/core

Babel的核心功能包含在@babel/core模块中,不安装@babel/core则无法使用

# @babel/cli

Babel提供的命令行工具,主要是提供babel这个命令,适合安装在项目里。

# babel 插件

插件

Babel构建在插件之上,使用插件可以组成一个转换通道,Babel的插件分为两种:

  • 语法插件:只允许babel解析特定类型的语法(不是转换),可以在AST转换时使用,以支持解析新语法

  • 转换插件:会启用相应的语法插件(因此不需要同时指定这两种插件),如果不启用响应的语法插件,意味着无法解析,就更不用说转换了。

插件的使用

如果插件发布在npm上,可以直接填写插件的名称,Babel会自动检查它是否已经被安装在node_modules目录下,在项目目录下新建.babelrc文件,配置如下

{
  "plugins": ["@babel/plugin-transform-arrow-functions"] // 也可以指定插件的相对路径 ["./node_modules/@babel/plugin-transform-arrow-functions"]
}

执行npm run compiler, "compiler": "babel src --out-dir lib --watch"

# @babel/preset-env

TIP

如果要将其他js特性转换为低版本,需要使用其他plugin,如果我们一个个配置的话,会非常繁琐。@babel/preset-env的主要作用就是对我们所使用的并且目标浏览器中缺失的功能进行代码转换和加载polyfill,在不进行配置的情况下,@babel/preset-env所包含的插件将支持所有最新的js特性(ES2015、ES2016等,不包含stage阶段),将其转换成ES5代码。

注意:由于@babel/preset-env默认会将任何模块类型都转译成CommonJS类型,这样会导致tree-shaking失效,因为tree-shaking只会对ES6module生效,所以需要设置modules:false属性来解决这个问题。

{
  "presets": ["@babel/preset-env", { "modules": false }]
}

需要说明的是,@babel/preset-env会根据你配置的目标环境,生成插件列表来编译。对于基于浏览器或Electron的项目,官方推荐使用.browerslistrc文件来指定目标环境。默认情况下,如果你没有在Babel配置文件中(如.babelrc)设置targetsignoreBrowserslistConfig,@babel/preset-env会使用browerslist配置源。

如果不是要兼容所有浏览器和环境,推荐指定目标环境,这样能使编译代码变得更小 如仅包含浏览器市场份额超过1%的用户所需的polyfill和代码转换(忽略没有安全更新的浏览器,如IE10 和BlackBerry)

> 1%
not dead

# @babel/polyfill

polyfill

语法转换只能将高版本的语法转换为低版本的,但是新的内置函数,实例方法是无法转换的。此时,就需要用到polyfill,也就是垫片。所谓垫片,就是垫平不同浏览器或者不同环境下的差异,让新的内置函数、实例方法等在低版本浏览器中也可以使用

@babel/polyfill模块包括core-js和一个自定义的 regenerator runtime 模块,可以模拟完整的ES2015+环境(不包含第四阶段前的提议)

这意味的可以使用诸如PromiseWeakMap之类的新的内置组件、Array.fromObject.assgin 之类的静态方法、Array.prorotype.includes 之类的方法,以及生成器函数(yield函数,前提是使用了@babel/plugin-transform-regenerator插件)

V7.4.0版本开始@babel/polyfill已经被废弃,需要单独安装core-jsregenerator-runtime 模块,首先,安装@babel/polyfill依赖

npm install @babel/polyfill --save

不使用--save-dev是因为这是一个需要在源码运行之前运行的垫片

我们需要将完整的polyfill在代码之前加载,修改我们的src/index.js,也可以在webpack中进行配置

entry:[
  require.resolve('./polyfills'),
  path.resolve('./index')
]

polyfills.js文件内容如下:

// 可能还有一些其他的polyfill
import '@babel/polyfill';

polyfills按需引入,且不会污染全局环境和原型

@babel/preset-env提供了一个useBuiltIns参数,设置值为usage时,就只会包含代码需要的polyfill。但是需要注意:值设为usage时,必须要同时设置corejs,如果不设置,会给出警告,如果安装了@babel/polyfill,默认使用的是corejs@2corejs@2已经不会再加入新的特性,新特性会添加到corejs@3

npm i core-js@3 --save

现在修改.babelrc配置文件如下

{
  "presets":[
    [
      "@babel/env",
      {
        "useBuiltIns":"usage",
        "corejs": 3
      }
    ]
  ]
}

babel会检查所有代码,以便查找在目标环境中缺失的功能,然后仅仅把需要的polyfill包含进来。同样的代码,使用webpack构建之后,最终代码会减小不少。 如果我们源码中使用到了 async/await,那么编译出来的代码需要require("regenerator-runtime/runtime"),可以只安装regenerator-runtime/runtime来取代安装@babel/polyfill

如果我们源码中使用了class,那么babel会使用很小的服务函数来实现类似_classCallCheck、_defineProperties、_createClass等公共方法。默认情况下,它们将被添加到所有使用了calssjs中。

为了避免加载多次,此时就需要用到@babel/plugin-transform-runtime插件

# @babel/plugin-transform-runtime

TIP

@babel/plugin-transform-runtime是一个可以重复使用Babel注入的帮助函数,使用@babel/plugin-transform-runtime插件,所有帮助函数都将引用@babel/runtime模块,这样就可以避免编译后的代码中出现重复的帮助函数,有效减少包体积大小。

@babel/plugin-transform-runtime需要和@babel/runtime配合使用。 首先安装依赖, @babel/plugin-transform-runtime通常仅在开发时使用,但是运行时最终代码需要依赖@babel/runtime,所以@babel/runtime必须要作为生产依赖安装

npm i @babel/plugin-transform-runtime --D
npm i @babel/runtime --save

此时帮助函数就会从@babel/runtime中引入,而不是直接被注入到源代码当中

@babel/plugin-transform-runtime不仅可以减少帮助函数的注入来减少代码体积,还可以为代码提供一个沙盒环境,他会将诸如Promise、Set、Map的内置别名作为core-js的别名,因此可以无缝使用他们

如果我们希望@babel/plugin-transform-runtime不仅处理帮助函数,同时也能处理polyfill的话,我们需要给@babel/plugin-transform-runtime增加配置信息,首先添加新增依赖@babel/runtime-corejs3

npm i @babel/runtime-corejs3 --save

然后需要修改.babelrc文件

{
  "presets":[
    [
      "@babel/preset-env",
      {
        "modules": false
      }
    ]
  ],
  "plugins":[
    [
      "@babel/plugin-transform-runtime", {
        "corejs": 3
      }
    ]
  ]
}

如果corejs的配置是 2,那么将不会包含实例方法的polyfill(includes等实例方法不会被转换为兼容版本),需要单独引入。 如果corejs的配置是 3,那么不管是实例方法还是全局方法,都不会污染全局环境。

# 插件的顺序

顺序

插件的排序很重要 如果两个插件都将处理程序的某个代码片段,则将根据"plugins""preset"中的排列顺序依次执行。

  • "plugins""preset"前运行
  • "plugins"顺序从前往后排列
  • "preset"顺序是颠倒的(从后往前)。

# 插件的短名称

如果插件名为@babel-plugin-xxx,可以使用@babel/xxx,如果插件名为babel-plugin-xxx,可以直接使用xxx

# @babel与babel的区别

升级到babel7之后,就必须使用@babel/xxx了,可以认为@babel/xxxbabel/xxx的最新版,许多babel/xxx也都不再更新了,更新的是@babel/xxx,比如@babel/plugin-transform-runtime,最新版只能使用@babel/plugin-transform-runtime。加@符号是为了区分哪些是官方包,哪些是第三方包

上次更新: 7/11/2021, 7:46:45 PM