babel是一个javascript编译器,是前端开发中的一个利器。它突破了浏览器实现es标准的限制,使我们在开发中可以使用最新的javascript语法。目前情况来说,es5 规范已经足以覆盖绝大部分浏览器,因此常规来说转到 es5 是一个安全且流行的做法。
babel 总共分为三个阶段:解析,转换,生成。
babel 本身不具有任何转化功能,它把转化的功能都分解到一个个 plugin 里面。因此当我们不配置任何插件时,经过 babel 的代码和输入是相同的。
插件分为两种:
举个简单的例子,当我们定义或者调用方法时,最后一个参数之后是不允许增加逗号的,如 callFoo(param1, param2,) 就是非法的。如果源码是这种写法,经过 babel 之后就会提示语法错误。 但最近的 JS 提案中已经允许了这种新的写法(让代码 diff 更加清晰)。为了避免 babel 报错,就需要增加语法插件babel-plugin-syntax-trailing-function-commas
比起语法插件,转译插件其实更好理解,比如箭头函数 (a) => a 就会转化为 function (a) {return a}。完成这个工作的插件叫做 babel-plugin-transform-es2015-arrow-functions。
同一类语法可能同时存在语法插件版本和转译插件版本。如果我们使用了转译插件,就不用再使用语法插件了。
es2015 是一套规范,包含大概十几二十个转译插件。如果每次要开发者一个个添加并安装,配置文件很长不说,npm install 的时间也会很长,更不谈我们可能还要同时使用其他规范呢。
为了解决这个问题,babel 还提供了一组插件的集合。
preset 分为以下几种:
Stage 0 - 稻草人: 只是一个想法,经过 TC39 成员提出即可。
Stage 1 - 提案: 初步尝试。
Stage 2 - 初稿: 完成初步规范。
Stage 3 - 候选: 完成规范和浏览器初步实现。
Stage 4 - 完成: 将被添加到下一年度发布。
例如 syntax-dynamic-import 就是 stage-2 的内容,transform-object-rest-spread 就是 stage-3 的内容。
此外,低一级的 stage 会包含所有高级 stage 的内容,例如 stage-1 会包含 stage-2, stage-3 的所有内容。
stage-4 在下一年更新会直接放到 env 中,所以没有单独的 stage-4 可供使用。
几条原则:
preset 的逆向顺序主要是为了保证向后兼容,因为大多数用户的编写顺序是 [‘es2015’, ‘stage-0’]。这样必须先执行 stage-0 才能确保 babel 不报错。因此我们编排 preset 的时候,也要注意顺序,其实只要按照规范的时间顺序列出即可。
因为 env 最为常用也最重要,所以我们有必要重点关注。
env 的核心目的是通过配置得知目标环境的特点,然后只做必要的转换。例如目标浏览器支持 es2015,那么 es2015 这个 preset 其实是不需要的,于是代码就可以小一点(一般转化后的代码总是更长),构建时间也可以缩短一些。
如果不写任何配置项,env 等价于 latest,也等价于 es2015 + es2016 + es2017 三个相加(不包含 stage-x 中的插件)。env 包含的插件列表。
babel 默认只转换 js 语法,而不转换新的 API,比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign)都不会转码。
举例来说,es2015 在 Array 对象上新增了 Array.from 方法。babel 就不会转码这个方法。如果想让这个方法运行,必须使用 babel-polyfill。(内部集成了 core-js 和 regenerator)
使用时,在所有代码运行之前增加 require(‘babel-polyfill’)。或者更常规的操作是在 webpack.config.js 中将 babel-polyfill 作为第一个 entry。因此必须把 babel-polyfill 作为 dependencies 而不是 devDependencies。
babel-polyfill 主要有两个缺点:
babel 会转换 js 语法,之前已经提过了。以 async/await 举例,如果不使用这个 plugin (即默认情况),转换后的代码大概是:
// babel 添加一个方法,把 async 转化为 generator
function _asyncToGenerator(fn) { return function () {....}} // 很长很长一段
// 具体使用处
var _ref = _asyncToGenerator(function* (arg1, arg2) {
yield (0, something)(arg1, arg2);
});
不用过于纠结具体的语法,只需看到,这个 _asyncToGenerator 在当前文件被定义,然后被使用了,以替换源代码的 await。但每个被转化的文件都会插入一段 _asyncToGenerator 这就导致重复和浪费了。
在使用了 babel-plugin-transform-runtime 了之后,转化后的代码会变成:
/ 从直接定义改为引用,这样就不会重复定义了。
var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
// 具体使用处是一样的
var _ref = _asyncToGenerator3(function* (arg1, arg2) {
yield (0, something)(arg1, arg2);
});
从定义方法改成引用,那重复定义就变成了重复引用,就不存在代码重复的问题了。
内部集成了:
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules)/, // 排除文件
loader: 'babel-loader'
}
]
}
}
在.babelrc配置文件中 可以如下简单的配置:
{
"presets": ['env']
}
我们还可以仅仅配置项目所支持的浏览器的配置
支持每个浏览器最后两个版本和safari大于等于7版本所需的polyfill代码转换,我们可以如下配置:
{
'presets': [
['env', {
'target': {
'browsers': ['last 2 versions', 'safari >= 7']
}
}]
]
}
支持市场份额超过5%的浏览器,可以如下配置:
{
'presets': [
['env', {
'target': {
'browsers': '> 5%'
}
}]
]
}
指定浏览器版本,可以如下配置
{
'presets': [
['env', {
'target': {
'chrome': 56
}
}]
]
}
如果通过Babel编译Node.js代码的话,可以设置 “target.node” 是 ‘current’, 含义是 支持的是当前运行版本的nodejs。
{
"presets": [
["env", {
"targets": {
"node": "current"
}
}]
]
}
modules
该参数的含义是:启用将ES6模块语法转换为另一种模块类型。将该设置为false就不会转换模块。默认为 ‘commonjs’.
该值可以有如下:
‘amd’ | ‘umd’ | ‘systemjs’ | ‘commonjs’ | false
以前我们需要使用babel来将ES6的模块语法转换为AMD, CommonJS,UMD之类的模块化标准语法,但是现在webpack都帮我做了这件事了,所以我们不需要babel来做,因此需要在babel配置项中设置modules为false,因为它默认值是commonjs, 否则的话,会产生冲突。
"presets": [
'env',
{
'modules': false
}
]
include: 包含一些插件,默认为 [];
比如包含箭头函数,可以如下配置:
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "safari >= 7"]
},
"include": ["transform-es2015-arrow-functions", "es6.map"]
}]
]
}
exclude; 排除哪些插件,默认为 [];
比如 排除生成器,可以如下配置:
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "safari >= 7"]
},
"exclude": ["transform-regenerator", "es6.set"]
}]
]
}
淘汰 es201x 的目的是把选择环境的工作交给 env 自动进行,而不需要开发者投入精力。凡是使用 es201x 的开发者,都应当使用 env 进行替换。但这里的淘汰 (原文 deprecated) 并不是删除,只是不推荐使用了,不好说 babel 8 就真的删了。
与之相比,stage-x 就没那么好运了,它们直接被删了。这是因为 babel 团队认为为这些 “不稳定的草案” 花费精力去更新 preset 相当浪费。stage-x 虽然删除了,但它包含的插件并没有删除(只是被更名了,可以看下面一节),我们依然可以显式地声明这些插件来获得等价的效果。完整列表
为了减少开发者替换配置文件的机械工作,babel 开发了一款 babel-upgrade 的工具,它会检测 babel 配置中的 stage-x 并且替换成对应的 plugins。
这是 babel 7 的一个重大变化,把所有 babel-* 重命名为 @babel/*
{
"presets": [
- "env"
- "@babel/preset-env"
]
}
使用方式如下:
# 不安装到本地而是直接运行命令,npm 的新功能
npx babel-upgrade --write
# 或者常规方式
npm i babel-upgrade -g
babel-upgrade --write
https://www.cnblogs.com/tugenhua0707/p/9452471.html
https://segmentfault.com/a/1190000014309163