JavaScript在不断的发展,各种新的标准和提案层出不穷。许多使用JavaScript开发的产品,具体点,那就以浏览器为代表吧!产品肯定是落后于实现标准的,这就导致许多新的JavaScript特性无法被兼容,明明那么方便简洁特性,却用不上,这是不能被接受的。
|
Babel 是什么 |
Babel是一个JavaScript编译器,也是一个工具链,集成了众多的插件和工具。
Babel 主要将 ECMAScript 2015+ (ES6+)版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。简单点理解就是,通过应用Babel可以让开发者在跟不上JavaScript新标准的环境中用上JavaScript新标准语法特性。
注意:使用babel进行es6转es5时,转化之后默认是严格模式
Babel 实际做了什么 |
- 语法转换
箭头函数,扩展运算符等 - 通过 Polyfill 方式在目标环境中添加缺失的特性 (通过 @babel/polyfill 模块)
有些ES6最新api,比如像Promise、Generator、Symbol,目标运行环境(常见:浏览器)还没有普遍提供实现,babel借助core-js对可以自动给js代码添加polyfill,以便最终在浏览器运行的代码能够正常使用那些api;babel始终能提供对最新es提案的支持; - 源码转换 (codemods)
如:react框架普遍使用JSX语法来编写模板,当然vue框架也可以使用 jsx,借助babel,jsx也能被正确地转换为ES代码;
开箱即用的babel什么也不做。babel需要配置后才能生效, babel的功能都是通过plugin来完成的。
babel7.x中 @babel 是什么含义 |
从babel7.0开始,babel系列的包都以 @babel开头,这个跟babel没关系,是npm包的一种形式。@符号是一个限定符,便是一个范围限定,表明包的身份主体。以@开头的npm包,一定是官方自己推出或者官方认可推出的包,比较有权威性质。
babel7 -:
npm install babel-cli babel-core -D
babel7 +:
npm i @babel/cli @babel/core -D
|
babel的功能都是通过plugin来完成的。只是安装babel,不做配置,那将毫无意义!
在使用插件之前,有必要了解下babel的核心包和命令行工具。
@babel/core |
核心,肯定要装上的!主要是对代码进行转换的核心方法。
@babel/cli |
cli全称command-line interface
,译为命令行接口。@babel/cli就是babel带有的内置cli,可以用来让我们从命令行来编译我们的文件。
借助这两个工具包,可以创建基本的babel实验环境了。
创建一个简单的babel例子 |
1.初始化项目 |
mkdir babel_study
cd babel_study
npm init -y
2.安装@babel/cli @babel/core |
把babel的核心库以及babel的命令行管理工具babel-cli安装到项目的(开发环境)依赖里面。
npm i @babel/cli @babel/core -D
//等价
//npm install @babel/cli @babel/core --save-dev
3.书写代码 |
在根目录下新建两个文件夹 src 和 dist 分别用来存放资源文件和产出文件
在 src 目录下创建 index.js 并写入以下代码:
// src/index.js
let foo = () =>{}
4.配置和安装插件 |
在根目录创建babel.config.js
(babel默认配置文件)
安装箭头函数插件
npm i @babel/plugin-transform-arrow-functions -D
配置箭头函数插件
const plugins=["@babel/plugin-transform-arrow-functions"]
module.exports={plugins}
5.启动测试babel |
//方式一 整个src目录的js文件都使用插件转码并输出到dist目录下
npx babel src --out-dir dist
//方式二 指定某文件输出到某个目录下并重新命名
npx babel .\src\index.js --out-file .\dist\index_babel.js
还有一个很好用的参数 --watch
,可以实时监测js文件的改变并输出,会占据终端进程
npx babel .\src\index.js --watch --out-file .\dist\index_babel.js
查看 dist 目录下的产出文件
let foo = function () {};
包含有 let
关键字,说明用的插件并不具备let
声明转码的功能,所以当我们选择自己配置插件,来进行代码转换的时候,就得对自己所需的插件非常清楚才行。
插件名称说明 |
从babel7开始,babel所有的包,不仅是plugin的包,还有preset的包,都全部变为了@babel这个形式的限定包。
几个常用插件:
@babel/plugin-transform-arrow-functions
用于箭头函数转码
@babel/plugin-transform-block-scoping
块级作用域转码
@babel/plugin-transform-for-of
for-of循环转码
👉更多babel插件
如果要使用babel7,在确定需要使用某一个plugin的时候,一定要先确定它是不是babel自己的包,如果是,就要通过@babel来安装,否则很有可能会安装到babel6的相关包;如果它不是babel自己的包,那肯定不能用@babel这个scope来安装,用它确定的名称即可。
比如转换箭头函数这个插件,如果是babel6,它的npm包名称则为: babel-plugin-transform-es2015-arrow-functions
;如果是babel7,它的npm包名称则为: @babel/plugin-transform-arrow-functions
启用插件 |
//babel.config.js
const presets = [];
const plugins = [];
module.exports = {presets, plugins}
插件元素 |
plugins 是一个数组,它的每个元素代表一个单独的 plugin:
plugins:["pluginA", ["pluginB", {}]]
插件元素的类型 |
- 纯字符串,用来标识一个plugin
- 一个数组,这个数组的第一个元素是字符串,用来标识一个plugin,第二个元素,是一个对象字面量,可以往plugin传递options配置
插件的启用顺序 |
插件是可以配置多个,babel中的preset可以看作是一组专有用途的插件。插件这么多,对源代码进行解析,肯定要有一个处理的先后顺序,前一个plugin的处理结果,将作为下一个plugin的输入。
- 插件在 Presets 前运行
- 插件顺序从前往后排列
- Preset 顺序是颠倒的(从后往前)
插件类型 |
- syntax
如:@babel/plugin-syntax-dynamic-import - transform
如:@babel/plugin-transform-arrow-functions - proposal
如:@babel/plugin-proposal-private-methods
插件的options |
常用的有以下几个:
- loose
启用松散式的代码转换,假如某个插件支持这个option,转换后的代码,会更加简单,代码量更少,但是不会严格遵循ES的规格,通常默认是false - spec
启用更加符合ES规格的代码转换,默认也是false,转换后的代码,会增加很多helper函数,代码量更大,但是代码质量更好 - legacy
启用旧的实现来对代码做转换。 - useBuiltIns
如果为true,则在转换过程中,会尽可能地使用运行环境已经支持的实现,而不是引入polyfill
|
我们知道需要哪个插件就引入哪个插件。但是新增的语法那么多,一个个的添加插件,实在不方便。
Preset 是什么 |
preset译为预设,在babel中可以理解为预先设置好的一组插件。
官方 Preset |
@babel/preset-env |
@babel/preset-env
是一个智能预设,允许您使用最新的JavaScript,而不需要微观管理您的目标环境需要哪些语法转换(可选的,浏览器填充)。这既使您的生活更容易,也使JavaScript包更小!
@babel/preset-flow |
如果您使用了 Flow,则建议您使用此预设(preset)。Flow 是一个针对 JavaScript 代码的静态类型检查器
@babel/preset-react |
解析react用的,主要针对jsx语法转换。 此预设(preset)始终包含以下插件:
- @babel/plugin-syntax-jsx
- @babel/plugin-transform-react-jsx
- @babel/plugin-transform-react-display-name
如果开启了 development 参数,还将包含以下插件:
- @babel/plugin-transform-react-jsx-self
- @babel/plugin-transform-react-jsx-source
@babel/preset-typescript |
解析typescript用的,它包含以下插件:@babel/plugin-transform-typescript
你需要为 @babel/cli 和 @babel/node 命令行工具指定 --extensions “.ts” 参数,以使其能够处理 .ts 文件。
Preset 用法 |
安装 |
npm i @babel/preset-env -D
配置 |
//babel.config.js
const plugins=[]
const presets=["@bable/preser-env"]
module.exports={plugins,presets}
执行 |
npx babel .\src\index.js -o dist\index_compile.js
查看 dist\index_compile.js
"use strict";
var foo = function foo() {};
自定义 Preset |
新建一个模块并返回一个plugins插件对象数组,比如取名:my-preset.js
module.exports = function() {
return {
plugins: [
"pluginA",
"pluginB",
"pluginC",
]
};
}
const presets = [
'./my-preset.js'
];
const plugins = [
];
module.exports = {presets, plugins}
Preset 启用顺序 |
preset 启用顺序与配置数组中的索引顺序是相反的。
Preset options |
不同的preset支持不同的options,options的配置方式跟plugin是一样的。
在babel7.4开始 ,不再支持@babel/polyfill
。 所以现在要用preset-env替代,为此必须要单独安装 core-js
(v3)。
corejs |
用来指定preset-env进行polyfill时,要指定的corejs版本 ,默认是 2。{ version: 2 }
modules |
这个用于配置是否启用将ES6的模块转换其它规范的模块。
“amd” | “umd” | “systemjs” | “commonjs” | “cjs” | “auto” | false, defaults to “auto”.
等等
@babel/preset-env |
不支持所有stage-x的plugins。通过查看preset-env的package.json文件,就能知道它支持哪些plugins:
现在把还在处于proposal阶段的plugin都命名为了 -proposal
形式的plugin。 非proposal的plugin都变为 -transform
形式的plugin了。
为什么上面的 package.json还会包含几个 -proposal
的plugin呢?
这是因为以上几个-proposal的plugin目前已经进展到 stage-4了,它变为 -trasform
的plugin是早晚的事,所以preset-env才会包含它们。
preset-env不是万能的。 如果我们用到某一个处于proposal阶段的ES新特性,而且preset-env不提供转码支持的话,就得自己单独配置plugins了。
ESMAScript阶段 |
一种新的语法从提案到变成正式标准,需要经历五个阶段。每个阶段的变动都需要由TC39委员会批准。
Stage 0 - 设想(Strawman):只是一个想法,可能有 Babel插件。
Stage 1 - 建议(Proposal):这是值得跟进的。
Stage 2 - 草案(Draft):初始规范。
Stage 3 - 候选(Candidate):完成规范并在浏览器上初步实现。
Stage 4 - 完成(Finished):将添加到下一个年度版本发布中。
有关详细信息,请务必查看 current TC39 proposals 及其 process document。
也就是说只有stage 4阶段的提案,才可能在每年7月份的时候,加入到当前的新标准发布中。
我们现在通常所说的ES6,其实是泛指ES2015发布以后,所有的ES版本,包括ES6、8、9、10。当别人说ES8、9、10的时候,说的其实就是ES2017、ES2018、ES2019。了解以上这些之后,对于一个前端开发者来说,关注TC39每年都有哪些新的提案,每年新的ESMA-262发布,都添加了哪些特性进入标准,应该是一个保持关注技术新特性的方式。
|
babel默认是只会去转义js语法的,不会去转换新的API,比如像Promise、Generator、Symbol这种全局API对象,babel是不会去编译的,这个时候就要掏出 @babel/polyfill 了。用法很简单,先安装一波,然后我们只需要在入口文件顶部引入 @babel/polyfill 就可以使用新增的API了。
比如下面的例子 使用@babel/preset-env
let foo = () =>{}
let bar=Symbol('bar')
查看编译后的文件
"use strict";
var foo = function foo() {};
var bar = Symbol('bar');
Symbol依然还是Symbol,没有被转义。
使用@babel/polyfill |
安装
npm i @babel/polyfill -D
引入
import'@babel/polyfill'
let foo = () =>{}
let bar=Symbol('bar')
编译后文件
"use strict";
require("@babel/polyfill");
var foo = function foo() {};
var bar = Symbol('bar');
📌小细节
import
被编译成了require
,如果想要编译出来的模块引入规范还是import
,则可以在preset-env的配置项中添加"modules": false
即可。
按需引入polyfill |
但是@babel/polyfill 会把所有浏览器环境的的polyfill都引入,整个包的体积就会很大。可以使用 preset-env 的配置项中的useBuiltIns属性来按需引入polyfill。
const plugins=[]
const presets=[["@babel/preset-env",{"modules": false, "useBuiltIns": "usage",}]]
module.exports={plugins,presets}
编译后文件
import "core-js/modules/es7.symbol.async-iterator.js";
import "core-js/modules/es6.symbol.js";
var foo = function foo() {};
var bar = Symbol('bar');
@babel/polyfil 是由core-js2和regenerator-runtime组成的一个集成包,现在core-js3已经发布了,而且很稳定。但是core-js2在18年的时候已经不再维护了;@babel/polyfil引入的是2不是3,并且 @babel/polyfill 在babel7.4.0已经不再推荐使用了,要废掉(好像是因为@babel/polyfill不支持core-js2平滑的过渡到core-js3)。所以core-js官方现在推荐我们使用polyfill的时候直接引入core-js和regenerator-runtime/runtime这两个包完全取代 @babel/polyfil 来为了防止重大更改。
const plugins=[]
const presets=[["@babel/preset-env",{"modules": false, "useBuiltIns": "usage", "corejs": "3",}]]
module.exports={plugins,presets}
编译后文件
import "core-js/modules/es.symbol.js";
import "core-js/modules/es.symbol.description.js";
import "core-js/modules/es.object.to-string.js";
import '@babel/runtime-corejs3';
var foo = function foo() {};
var bar = Symbol('bar');
|
另一个使用 Babel 的方法是通过 require 钩子(hook)。require 钩子 将自身绑定到 node 的 require 模块上,并在运行时进行即时编译。
安装
npm install @babel/core @babel/register --save-dev
引入
require("@babel/register");
node 后续运行时所需要 require 进来的扩展名为 .es6、.es、.jsx、 .mjs 和 .js 的文件将由 Babel 自动转换。Polyfill 不会被引入进来且默认忽略 node_modules目录下的文件的 require 请求。
require("@babel/register");
require("xx.jsx");
xx.jsx将由Babel 自动转换。
|
Babel有两种并行的配置文件格式,可以一起使用,也可以独立使用。
项目范围的配置 (Project-wide configuration) |
babel.config.json
文件,可用不同的扩展名(.js,.cjs,.mjs)
对于项目范围的配置,Babel将在此根目录中自动搜索一个babel.config.json文件或使用支持的扩展名的等效文件。
相对文件配置 (File-relative configuration) |
.babelrc
文件,可用不同的扩展名(.json,.js,.cjs,.mjs)
package.json中"babel"
对象
补充说明 |
1.同一配置文件类型的配置文件不能共存,会报错。比如:根目录下不能共存babel.config.json
和babel.config.js
,因为两者是项目范围内等效的配置文件。
同理:相对文件配置范围的也不能并存,package.json中"babel"
对象不能与.babelrc
共存
Error: Multiple configuration files found. Please remove one:
- babel.config.js
- G:\webpack_study\babel_study\babel.config.json
2.不同配置文件类型的配置文件(不是指文件格式,文件格式不同,文件是可以等效的)是可以共存且相互补充的。
//.babelrc
{
"plugins":["@babel/plugin-transform-for-of"]
}
//babel.config.js
const plugins=["@babel/plugin-transform-arrow-functions"]
module.exports={plugins}
文件
for (let s of [['k','a'],['k','b']]) {}
let foo = () =>{}
编译后
for (var _i = 0, _arr = [['k', 'a'], ['k', 'b']]; _i < _arr.length; _i++) {
let s = _arr[_i];
}
let foo = function () {};
常用配置格式 |
js |
const plugins=[]
const presets=[]
module.exports={plugins,presets}
json |
{
"presets": [],
"plugins": []
}
package.json |
"babel": {
"presets": [],
"plugins": []
},
======================================================================================================