Babel 学习与使用

JavaScript在不断的发展,各种新的标准和提案层出不穷。许多使用JavaScript开发的产品,具体点,那就以浏览器为代表吧!产品肯定是落后于实现标准的,这就导致许多新的JavaScript特性无法被兼容,明明那么方便简洁特性,却用不上,这是不能被接受的。

Babel 核心概念

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

Plugins

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

Presets

我们知道需要哪个插件就引入哪个插件。但是新增的语法那么多,一个个的添加插件,实在不方便。

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/polyfill(由core-js2和regenerator-runtime组成的一个集成包)

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/register

另一个使用 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.jsonbabel.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": []
  },

======================================================================================================

参考文档:
babel@7.x 基础核心详解
babel中文文档
Babel 配置用法解析

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值