目录
本文为本人略读官网文档后的大略笔记,实在不适合他人阅读
前言
Babel是一个JavaScript编译器(用于转换浏览器不支持的最新版本js代码)。
Babel工作过程(3阶段):解析=>转换=>生成。
Babel支持source map(方便调试编译代码)。
Babel可转换ReactJSX和删除类型注释。
Babel可集成到构建工具(Webpack、Browserify、Grunt、Gulp等)。
1. 插件:
babel的转换功能是通过插件来实现。
2. preset:
语法糖(一系列插件的组合)
3. polyfill:
抹平不同浏览器之间的差异。
1. 插件
不配置任何插件时,经过babel的代码和输入是相同的。
因为:babel本身不具有任何转化功能,它把转化的功能都分解到一个个plugin中。
- plugin插件分为2种
- 语法插件
使babel能够正确解析语法
例
babel不能识别callFoo(param1, param2,)
需要添加babel-plugin-syntax-trailing-function-commas 来正确解析。
- 转译插件
将ES2015+转换浏览器可识别的js代码
例
箭头函数 (a) => a 转化为 function (a) {return a}
需要添加 babel-plugin-transform-es2015-arrow-functions 来完成转换
- 配置方式
方式1 package.json文件中
{
"name": "helloworld",
"version": "1.0.0",
"babel": {
"presets": [ ... ],
"plugins": [ ... ],
}
}
方式2 .babelrc独立文件
{
"presets": […],
"plugins": […]
}
方式3 使用webpack工具时 (具体参考webpack了解篇3.1)
webpack.config.js文件
module.exports = {
devtool: 'eval-source-map',
entry: __dirname + "/src/main.js", // 入口文件(唯一)
output: {
path: __dirname + "/build", // 输出文件路径(打包后的文件存放的地方)
filename: "bundle.js", // 输出文件名(打包后输出文件的文件名)
},
module: {
rules: [
{
test: /(\.jsx|\.js)$/,
use: {
loader: "babel-loader",
options: {
presets: [
"env", "react"
]
}
},
exclude: /node_modules/
}
]
}
}
具体配置
"presets": [
// 情况1 : 不带配置项,直接写名字
"stage-2"
// 情况2 : 带了配置项,则变成数组
[
// 第一个元素依然是名字
"env",
// 第二个元素是对象,列出配置项
{
"module": false
}
],
]
说明:
1. env、stage-2 (见preset)
2.modules
可取值 : amd, umd, systemjs, commonjs 和 false。
让babel以特定的模块化格式来输出代码 , 如果选择 false 就不进行模块化处理。
1. Plugin 会运行在 Preset 之前。
2. Plugin 会从前到后顺序执行。
3. Preset 刚好相反(会从后向前)。
因此编写Preset要按照规范的时间顺序列出。如['es2015', 'stage-0'],反过来则会报错
- 安装plugin
npm install babel-plugin-xxx
2. preset
光es2015就需要包含近20个转译插件。
如果每次都一个个添加并安装,会导致配置文件很长,npm install的时间也会很长。
为了解决这个问题,babel提供了一组插件的集合。
- preset分为
1. 官方内容(目前包括 env, react, flow, minify等)
2. stage-x(当年最新规范草案,每年更新)
Stage 0 - 稻草人: 只是一个想法,经过 TC39 成员提出即可。
Stage 1 - 提案: 初步尝试。
Stage 2 - 初稿: 完成初步规范。
Stage 3 - 候选: 完成规范和浏览器初步实现。
Stage 4 - 完成: 将被添加到下一年度发布。
低一级的 stage 会包含所有高级 stage 的内容。
3. es201x, latest
已经纳入到标准规范的语法。例如 es2015 包含 arrow-functions,es2017 包含 syntax-trailing-function-commas。latest 是 env 的雏形,它是一个每年更新的 preset,目的是包含所有 es201x。
但因为 env 的出现,使得 es2016、es2017、latest 都已经废弃。
- env (最常用、最重要)
env的目的是通过配置得知目标环境的特点,然后只做必要的转换。
例如目标浏览器支持 es2015,则不需要es2015这个preset ,缩短了代码和构建时间。
如果不写任何配置项,env等价于latest,也等价于es2015+es2016+es2017 三个相加(不包含 stage-x 中的插件)。
例1
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "safari >= 7"]
}
}]
]
}
如上配置将考虑所有浏览器的最新2个版本(safari大于等于7.0的版本)的特性,将必要的代码进行转换。
而这些版本已有的功能就不进行转化了。
例2
{
"presets": [
["env", {
"targets": {
"node": "6.10"
}
}]
]
}
如上配置将目标设置为 nodejs,并且支持 6.10 及以上的版本。
也可以使用 node: 'current' 来支持最新稳定版本。
例如箭头函数在 nodejs 6 及以上将不被转化,但如果是 nodejs 0.12 就会被转化了。
3. 常用模块
- babel-cli
项目不大时使用
cli 即命令行工具,允许开发者在命令行中使用 babel 命令来编译文件。
# 本地安装(建议)
# 不同项目会依赖不同版本的 Babel
$ npm install --save-dev babel-cli
# 全局安装(可直接在终端使用)
$ npm install --global babel-cli
使用
# 将编译后的结果直接输出至终端
$ babel example.js
# 将编译后的结果写入到指定的文件
$ babel ES6.js --out-file ES5.js
$ babel ES6.js -o ES5.js
# 编译整个目录
$ babel ES5 --out-dir ES6
$ babel ES5 -d ES6
- babel-node
babel-node 是 babel-cli 的一部分,不需要单独安装
作用是在node环境中,直接运行es2015代码,而不需要进行转码。
babel-node = babel-polyfill + babel-register
使用
babel-node es2015.js
- babel-register
改写require命令:
为它加上一个钩子,每当使用require加载 .js、.jsx、.es 和 .es6 后缀名的文件,就会先用 babel 进行转码。
使用时,必须首先加载 require('babel-register')
只会对require命令加载的文件转码,而不会对当前文件转码。
由于它是实时转码,所以只适合在开发环境使用
- babel-polyfill
babel是通过语法转换来转换不属于ES5的语法,如:
for-of
箭头函数
结构
块级作用域
let/const
下面情况则不作处理:
1. 全局对象(如:Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise、WeakMap)
2. 定义在全局对象上的方法(如:Array.from、Array.includes、Object.is、Object.assign)
3. generactor 函数
方式1:babel-polyfill 可用来解决上述情况
会检测运行环境是否支持Promise等,如果不支持,会创建全局的Promise等对象或者给Array等新增方法。
方式2:也可使用babel-runtime来解决上述情况
与babel-polyfill作用一样。不同的是它不会污染全局作用域。它会提供一个module, 当使用Promise时,它会把这个module的某个对象导出给使用的地方。
2个缺点:
1. 会导致打出来的包非常大,因为 babel-polyfill 是一个整体,把所有方法都加到原型链上。比如我们只使用了 Array.from,但它把 Object.defineProperty 也给加上了,这就是一种浪费了。这个问题可以通过单独使用 core-js 的某个类库来解决,core-js 都是分开的。
2. 会污染全局变量,给很多类的原型链上都作了修改,如果我们开发的也是一个类库供其他开发者使用,这种情况就会变得非常不可控。
因此通常使用babel-plugin-transform-runtime
使用时
在所有代码运行之前增加 require('babel-polyfill')。
或
在 webpack.config.js 中将 babel-polyfill 作为第一个 entry(更常用)。
因此必须把 babel-polyfill 作为 dependencies 而不是 devDependencies
- babel-runtime 和 babel-plugin-transform-runtime
babel-plugin-transform-runtime 依赖 babel-runtime
babel-plugin-transform-runtime 不支持 实例方法 (例如 [1,2,3].includes(1))
~~~~~未使用babel-plugin-transform-runtime~~~~~
// 每一个使用_asyncToGenerator的地方都会加,导致重复定义
// babel 添加一个方法,把 async 转化为 generator
function _asyncToGenerator(fn) { return function () {....}} // 很长很长一段
// 具体使用处
var _ref = _asyncToGenerator(function* (arg1, arg2) {
yield (0, something)(arg1, arg2);
});
~~~~~使用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);
});
babel-runtime内部集成了
1. core-js
转换一些内置类 (Promise, Symbols等等) 和静态方法 (Array.from 等)。
绝大部分转换是这里做的。
自动引入。
2. regenerator
作为 core-js 的拾遗补漏,主要是 generator/yield 和 async/await 两组的支持。
当代码中有使用 generators/async 时自动引入。
3. helpers
如上面的 asyncToGenerator 就是其中之一,其他还有如 jsx, classCallCheck 等等。
在代码中有内置的 helpers 使用时(如上面的第一段代码)移除定义,并插入引用。
- babel-loader
用于大型项目。
webpack的loader,用于转换ES6等浏览器不识别的js代码(详情见webpack篇)。
4. Babel 7.x
preset的变更
1. 淘汰(即不推荐使用) es201x
2. 删除 stage-x
依然可以显式地声明这些插件来获得等价的效果
babel-upgrade会检测 babel 配置中的 stage-x 并且替换成对应的 plugins
3. 强推 env (重点)
npm package 名称的变化 (重点)
把所有 babel-* 重命名为 @babel/*
1. babel-cli 变成了 @babel/cli。
2. babel-preset-env 变成了 @babel/preset-env。
简写为 @babel/env。
3. babel-plugin-transform-arrow-functions 变成了 @babel/plugin-transform-arrow-functions。
简写为 @babel/transform-arrow-functions。
4. 如果插件名称中包含了规范名称 (-es2015-, -es3- 之类的),一律删除。
例如 babel-plugin-transform-es2015-classes 变成了 @babel/plugin-transform-classes。
{
"presets": [
- "env"
+ "@babel/preset-env"
]
}
不再支持低版本 node
babel 7.0 开始不再支持 nodejs 0.10, 0.12, 4, 5 这四个版本,相当于要求 nodejs >= 6
不再支持指的是:在这些低版本 node 环境中不能使用 babel 转译代码,但 babel 转译后的代码依然能在这些环境上运行。
only 和 ignore 匹配规则的变化
在 babel 6 时,ignore 选项如果包含 *.foo.js,实际上的含义 (转化为 glob) 是 ./**/*.foo.js,也就是当前目录 包括子目录 的所有 foo.js 结尾的文件。这可能和开发者常规的认识有悖。
于是在 babel 7,相同的表达式 *.foo.js 只作用于当前目录,不作用于子目录。如果依然想作用于子目录的,就要按照 glob 的完整规范书写为 ./**/*.foo.js 才可以。only 也是相同。
这个规则变化只作用于通配符,不作用于路径。所以 node_modules 依然包含所有它的子目录,而不单单只有一层。(否则全世界开发者都要爆炸)
@babel/node 从 @babel/cli 中独立了
和 babel 6 不同,如果要使用 `@babel/node`,就必须单独安装,并添加到依赖中。
babel-upgrade
在提到删除 stage-x 时候提过这个,它的目的是帮助用户自动化地从 babel 6 升级到 7。
功能:
package.json
1. 把依赖(和开发依赖)中所有的 `babel-*` 替换为 `@babel/*`
2. 把这些 `@babel/*` 依赖的版本更新为最新版 (例如 `^7.0.0`)
3. 如果 `scripts` 中有使用 `babel-node`,自动添加 `@babel/node` 为开发依赖
4. 如果有 `babel` 配置项,检查其中的 `plugins` 和 `presets`,把短名 (`env`) 替换为完整的名字 (`@babel/preset-env`)
.babelrc
1. 检查其中的 `plugins` 和 `presets`,把短名 (`env`) 替换为完整的名字 (`@babel/preset-env`)
2. 检查是否包含 `preset-stage-x`,如有替换为对应的插件并添加到 `plugins`
等等
使用:
# 不安装到本地而是直接运行命令,npm 的新功能
npx babel-upgrade --write
# 或者常规方式
npm i babel-upgrade -g
babel-upgrade --write
网友评论