这里借用一下官方的定义javascript
Babel是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便可以运行在当前和旧版本的浏览器或其余环境中。前端
简单的理解就是,对较高版本的语法作一次向下兼容,使得较低版本的浏览器可以识别并运行这些代码java
先来看一下这个词babel(bāb(ə)l,音译为 掰bou,中文译为巴别塔。node
个人理解是:react
es6
之类的只是语言规范,而浏览器要实现这一规范可能就须要至关漫长的一个时间了。react
的jsx因此急需一个工具去解决这些问题,而后babel
就应运而生。webpack
再来看一下百度上对巴别塔的解释git
巴别塔;是《圣经·旧约·创世记》第11章故事中人们建造的塔。根据篇章记载,当时人类联合起来兴建但愿能通往天堂的高塔;为了阻止人类的计划,上帝让人类说不一样的语言,令人类相互之间不能沟通,计划所以失败,人类自此各散东西。此事件,为世上出现不一样语言和种族提供解释。es6
是否是感受很是🐂🍺github
这里想到了我司的一个项目,这个项目的主要做用就是使用函数计算在移动端截图,我司名字是moka,因此这个项目起名为mokapture,我我的感受仍是挺有意思的web
babel
会经过具体的某个插件对相应的代码进行转码,好比箭头函数对应的插件就是@babel/plugin-transform-arrow-functions
,链式调用对应的插件就是@babel/plugin-proposal-optional-chaining
,咱们不可能一个一个的安装并一个一个引入使用这些插件,那么presets
就为咱们提供了一组插件的集合。
这里重点介绍一下env,这也是咱们平时用的最多的
env 的核心目的是经过配置(browserslist
, compat-table
, and electron-to-chromium
)得知目标环境的特色,而后只作必要的转换。例如目标浏览器支持 es2015,那么 es2015 这个 preset 实际上是不须要的,因而代码就能够小一点(通常转化后的代码老是更长),构建时间也能够缩短一些。若是不写任何配置项,env默认使用最新的JS语法(不包括Stage-X阶段)。
基本的配置以及本人认为比较重要的options以下:
module.exports = {
presets: [
[
"@babel/preset-env",
{
targets: "> 0.25%, not dead",
modules: false,
useBuiltIns: "entry",
corejs: "3",
},
],
],
};
复制代码
targets
: 支持babel
转换的浏览器环境,示例代码的意思是仅包含浏览器市场份额超过0.25%的用户所需的polyfill
和代码转换(忽略没有安全更新的浏览器,如IE10和BlackBerry)。具体的语法能够参考browserslistmodules
:让 babel 以特定的模块化格式来输出代码,可选值为"amd" | "umd" | "systemjs" | "commonjs" | "cjs" | "auto" | false
。默认为auto
。
useBuiltIns
: "usage" | "entry" | false
, 默认为false
,若是使用此配置项,须要指定corejs
版本,否则会有WARNING
usage
会自动根据咱们的环境自动引入对应的core-js
和regenerator-runtime
插件对咱们的代码进行模块化解析entry
确保咱们的每一个文件只引入一次polyfill
代码false
则不自动给文件引入polyfill
,也不会给core-js
或者polyfill
作按需加载处理TC39 将提案分为如下几个阶段:
注意:这些提案可能会有变化,所以,特别是处于 stage-3 以前的任何提案,请务必谨慎使用。咱们计划在每次 TC39 会议以后,若是有可能,在提案变动时更新 stage-x 的 preset。
Preset 是逆序排列的(从后往前)。
{
"presets": ["a", "b", "c"]
}
复制代码
将按以下顺序执行: c、b 而后是 a。
这主要是为了确保向后兼容,因为大多数用户将 "es2015" 放在 "stage-0" 以前,这样必须先执行 stage-0 才能确保 babel 不报错。另外:Plugin 会运行在 Preset 以前,从前向后执行。
Babel 是一个编译器(输入源码 => 输出编译后的代码)。就像其余编译器同样,编译过程分为三个阶段:解析、转换和打印输出。如今,Babel 虽然开箱即用,可是什么动做都不作。它基本上相似于 const babel = code => code; ,将代码解析以后再输出一样的代码。若是想要 Babel 作一些实际的工做,就须要为其添加插件。
咱们能够看到babel主要仍是依靠各类插件来对咱们的代码进行编译。下面咱们来看一下babel的插件到底有多强大吧。先来看一段代码:
// index.js
const study = (a, b) => a + b;
复制代码
这是一段很常见的使用箭头函数的代码,那么咱们如何使用babel去编译这段代码呢,让咱们先依次来执行下列代码
npm install --save-dev @babel/core
npm install --save-dev @babel/cli
npm install --save-dev @babel/plugin-transform-arrow-functions
复制代码
安装完这几个模块以后咱们再配置一下babel
// babel.config.js
module.exports = {
presets: [],
plugins: ["@babel/plugin-transform-arrow-functions"],
};
复制代码
而后在终端里执行
npx babel index.js --out-file index-compiled.js
复制代码
咱们打开index-compiled.js
看一下
// index-compiled.js
const study = function (a, b) {
return a + b;
};
复制代码
babel
把咱们的箭头函数给转换成了function
形式
All transformations will use your local configuration files.
全部的转换都将用本地的配置文件(.babelrc、babel.config.js或者package.json),core即便核心嘛,咱们也能够看到core的仓库里的目录结构 主要是将代码转成ast,方便各个插件分析语法进行相应的处理
用于命令行使用babel,好比咱们上面👆代码的npx babel index.js --out-file index-compiled.js
babel-node is a CLI that works exactly the same as the Node.js CLI, with the added benefit of compiling with Babel presets and plugins before running it.
babel-node
是babel
附带的第二个CLI,工做原理与Node.js的CLI彻底相同,只是它会在运行以前编译ES6
代码,node
环境下能够直接运行代码,而不须要转码。babel-node
通常不用于生产环境,由于运行前动态编译,因此内存的开销很是的大。babel-node
至关于babel-polyfill
+ babel-register
babel-register
会在node
环境下,给require
绑定一个钩子函数,这个钩子会讲全部以.es6
, .es
, .jsx
, .mjs
, and .js
为后缀的文件都将用babel
进行转码。因此babel-register
只会对require
的文件转码,并不会对自身文件转码,由于是实时转码,因此不适用于生产环境。而且若是你想用babel-register
的话,还要一并引入babel-polyfill
。
babel
默认只转换 js 语法,而不转换新的 API,好比 Iterator
、Generator
、Set
、Maps
、Proxy
、Reflect
、Promise
这种全局Api或者对象,以及一些定义在全局对象上的方法(好比Object.assign
)都不会转码。因此在使用这些方法时就必需要使用babel-polyfill
(包含core-js
和regenerator-runtime
)。
这里咱们改造一下代码,npm install --save-dev @babel/preset-env
,而后修改一下babel.config.js
// babel.config.js
module.exports = {
presets: ["@babel/preset-env"],
plugins: [],
};
复制代码
这里再看一下babel-polyfill
// index.js
import "@babel/polyfill";
new Promise((resolve, reject) => {
resolve(1);
});
复制代码
// index-compiled.js
"use strict";
require("@babel/polyfill");
new Promise(function (resolve, reject) {
resolve(1);
});
复制代码
咱们注意到@babel/polyfill
的引用方式变成require
了,怎么变成import
呢, 在babel.config.js
中,将preset-env
的配置项中添加modules: false
便可。因此此时的babel.config.js
文件变成了:
// babel.config.js
module.exports = {
presets: [
[
"@babel/preset-env",
{
modules: false,
useBuiltIns: "entry",
corejs: "3",
},
],
],
plugins: [],
};
复制代码
咱们再来执行一下,会发现@babel/polyfill
的引用方式变回import
了。
通常状况下咱们在使用polyfill
时都是经过webpack
的入口文件配置,好比:
module.exports = {
entry: ["babel-polyfill", "./app/js"]
};
复制代码
因此,babel-polyfill
要安装在生产环境dependencies
中。
由于babel-polyfill
会把全部的方法都加在原型上,好比Array.isArray
这个方法,babel-polyfill
会在Array
的protorype
上挂载这个方法,即Array.prototype.isArray
,这将致使:
babel-polyfill
打包出来的体积很是大,由于全部的原型上都挂载的有兼容的方法,我只想用Array.isArray
,可是Object.assign
也会被挂载兼容方法。固然也有优化的方法,配置useBuiltIns: usage
。因此plugin-transform-runtime
会是一个比较不错的选择
当咱们在使用一些稍复杂的语法时,babel
会借用一些函数进行转换,好比说es6
的class
// index.js
class Circle {}
复制代码
通过babel
的默认转换后:
// index-compiled.js
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var Circle = function Circle() {
_classCallCheck(this, Circle);
};
复制代码
咱们能够看到,babel
会使用一些函数来帮助处理这些复杂的转换,那咱们能够预想到,每一个文件都这么转换以后代码量将会是多么的庞大,而且还重复了不少的这些helper
函数,因此babel-plugin-transform-runtime
其实就是把这些helper
函数统一收集起来,下次直接在文件中引用便可,咱们安装一下babel-plugin-transform-runtime
npm install --save-dev @babel/plugin-transform-runtime
复制代码
配置babel.config.js
// babel.config.js
module.exports = {
presets: [
[
"@babel/preset-env",
{
modules: false,
useBuiltIns: "entry",
corejs: "3",
},
],
],
plugins: ["@babel/plugin-transform-runtime"],
};
复制代码
此时运行再看咱们编译后的class
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
var Circle = function Circle() {
_classCallCheck(this, Circle);
};
复制代码
已经变成了引入的方式。咱们注意到@babel/plugin-transform-runtime
实际上是引用的@babel/runtime
的代码。
此外@babel/plugin-transform-runtime
还有一个最重要的做用就是为咱们提供了一个配置项corejs
,他能够给babel-polyfill
提供一个沙箱环境,这样就不会污染到全局变量,并且无反作用。可是这项要开启@babel/plugin-transform-runtime
的沙箱模式,必需要配置corejs
须要注意的是
corejs2
只支持全局变量(promise。。。
)和静态属性(Array.from。。。
),而不支持实例属性(includes。。。
),若是要用实例属性,就要使用corejs3
。可是corejs3
目前仍是提案阶段,因此还需配置proposals: true
,因此此时babel.config.js
为:
module.exports = {
presets: [
[
"@babel/preset-env",
{
modules: false,
useBuiltIns: "entry",
corejs: "3",
},
],
],
plugins: [
[
"@babel/plugin-transform-runtime",
{
corejs: { version: 3, proposals: true },
},
],
],
};
复制代码