目录
◼️ @babel/core | @babel/cli | @babel/preset-env 到底做了哪些事情
3、babel-plugin-transform-runtime
🎋 babel-runtime 和 babel-plugin-transform-runtime
🌲 前言
在此之前或许你已经用过babel,也许听说过什么 babel-loader babel-core、babel-cli、babel-plugin-…、babel-preset-env。
反正各种乱七八糟的做项目随便用一下就可以了,对他只有个一知半解,甚至不知道他到底是干什么的,反正项目要用,照着用就行了,至少博主之前的状态是这样,如果只对他有个一知半解甚至都不了解,那么项目出bug了你都不知道怎么去调试,只能复制 –> 粘贴 –> 百度。基于此,写下自己对Babel的理解。Babel是什么?
🌲 Babel是什么?
我们在babel的官方网站找到这样一句话:
Babel is a JavaScript compiler | Babel 是一个 JavaScript 编译器
Babel 是一个工具链,主要用于将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。下面列出的是 Babel 能为你做的事情:
- 语法转换
- 通过 Polyfill 方式在目标环境中添加缺失的功能(通过引入第三方 polyfill 模块,例如 core-js)
- 源码转换(codemods)
- 更多参考资料!(请查看这些 视频 以获得启发)
Babel是一个编译器,针对JavaScript,为什么会有Babel这样一个工具的存在?
本文默认你对es6、es7等有所涉足,我们在写es6+语法的时候是不是很方便,什么promise、async await,可是es6+语法写的虽然很酸爽,但是浏览器他不兼容啊,你想想你写的代码在浏览器上跑不起来,再好的语法、再好的特性又有什么用?
这个时候Babel这样一个工具出来了,它可以将我们写的es6+语法转换为浏览器兼容的语法,比如将箭头函数转换为普通函数,有了这样一个工具我们就即可以写酸爽的语法,又可以让使浏览器兼容。相信到这你已经知道了Babel的概念,并且可以脑补出Babel可以干什么。
🌲 Babel 的工作原理
Babel 使用 AST 把不兼容的代码编译成 es5 版本,因为大多数浏览器都支持这个版本的 JavaScript 代码。
◼️ 如何设置
整个配置过程包括:
1. 在控制台运行如下命令安装所需的包(package):
npm方式
npm install --save-dev @babel/core @babel/cli @babel/preset-env
Yarn方式
yarn add --dev @babel/core @babel/cli @babel/preset-env
2. 应用程序的根目录会默认创建一个 babel.config.json 文件(需要 v7.8.0
或更高版本)。Babel 将遍历 src 目录下的所有 JS 文件。
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1"
},
"useBuiltIns": "usage",
"corejs": "3.6.5"
}
]
]
}
上述浏览器列表仅用于示例。请根据你所需要支持的浏览器进行调整。参见 此处 以了解
@babel/preset-env
可接受哪些参数。
如果你使用的是 Babel 的旧版本,则文件名为 babel.config.js
。
const presets = [
[
"@babel/preset-env",
{
targets: {
edge: "17",
firefox: "60",
chrome: "67",
safari: "11.1",
},
useBuiltIns: "usage",
corejs: "3.6.4",
},
],
];
module.exports = { presets };
package.json
文件我们也不陌生,可以直接通过 npm init
命令来创建。或者我们还可以将 .babelrc 或
.babelrc.json 中的配置信息作为 babel
键(key)的值添加到 package.json
文件中,如下所示:
{
"name": "demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"babel": {
"presets": [...],
"plugins": [...]
}
}
当然我们除了将把配置写在上述这几种配置文件中,我们也可以写在构建工具的配置里。对于不同的构建工具,Babel
也提供了相应的配置项,例如 webpack
的 babel-loader
的配置项,其本质和配置文件是一样的。
配置文件中的主要配置项就是 plugins
和 presets
这两个数组,plugins
为插件数组,presets
为预设数组。其他的例如 ignore
、minified
等我们一般用不到。
3. 运行此命令将 src
目录下的所有代码编译到 lib
目录:
./node_modules/.bin/babel src --out-dir lib
你可以利用 npm@5.2.0 所自带的 npm 包运行器将
./node_modules/.bin/babel
命令缩短为npx babel
🎋 想要更详细的分步讲解,了解其工作原理以及每个工具的功能,可以参看:使用指南 · Babel 中文文档 | Babel中文网。
◼️ Babel 有哪些值得注意的选项
{
"presets":
[
[
"@babel/oreset-env",
{
"targets":
{
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1"
},
"useBuiltIns": "usage",
"corejs": "3.6.5"
}
]
],
}
- 如果要缩减输出代码,需要增加选项
"minified": true
- 如果要忽略某些文件,则添加
ignore: ["file or directory path"]
- 只编译特定的文件或文件夹,则添加如下代码:
// For Files
"only" : ["./src/some_file.js"],
// For Directory
"only" : ["./src"],
◼️ @babel/core | @babel/cli | @babel/preset-env 到底做了哪些事情
- @babel/core:Babel 的所有核心功能都在这里
- @babel/cli:提供了 CLI 工具,使我们能够运行 npm run build
- @babel/preset-env:提供预置功能
🌲 Babel 的使用
- 单体文件
- 命令行
- 配合Webpack使用
本文将介绍Babel配合webpack使用的情况
◼️ 配置文件
babel的配置文件常用的有几种:.babelrc,.babelrc.json、babelrc.js、babel.config.json、babel.config.js
以下是官网关于配置文件类型的介绍:Config Files
Configuration File Types | 配置文件类型
Babel has two parallel config file formats, which can be used together, or independently.History
Version Changes v7.21.0
Support .babelrc.cts
andbabel.config.cts
(Experimental)v7.8.0
Support .babelrc.mjs
andbabel.config.mjs
v7.7.0
Support .babelrc.json
,.babelrc.cjs
,babel.config.json
,babel.config.cjs
- Project-wide configuration
babel.config.*
files, with the following extensions:.json
,.js
,.cjs
,.mjs
,.cts
.- File-relative configuration
.babelrc.*
files, with the following extensions:.json
,.js
,.cjs
,.mjs
,.cts
..babelrc
file, with no extension.package.json
files, with a"babel"
key.
大致翻译一下:
Babel 有两种并行的配置文件格式,可以一起使用,也可以分开使用。
◾ 项目范围的配置 ——全局配置
你是否采用的是单一仓库(monorepo)模式?你是否需要编译
node_modules
?
那么 babel.config.json 文件可以满足你的的需求!
👉 根目录:babel.config.json会影响整个项目中的代码,包含node_modules中的代码
babel.config.* 文件,具有不同的拓展名(.json
,.js
,.cjs
, .mjs
,.cts
.)
babel.config.js 是按照 commonjs 导出对象,可以写js的逻辑。
◾ 相对文件的配置 ——局部配置
你的配置文件是否仅适用于项目的某个部分?那么 .babelrc.json 文件适合你!
👉 项目中:babelrc 只会影响本项目中的代码
.babelrc.* 文件,具有不同的拓展名(.json
,.js
,.cjs
, .mjs
,.cts
.)
.babelrc 文件,不带扩展名
babel官方建议使用 babel.config.json 格式的配置文件。 Babel 自身使用的就是这种格式
🎋 配置文件的使用场景,更多详细信息:配置 Babel · Babel 中文文档 | Babel中文网
◼️ Babel 配置文件的优先级
从低到高依次为:
- babel.config.json
- babelrc.json
- @babel/cli
依照优先级,babel.config.json
会被 .babelrc
覆盖,依次类推。
注意:.babelrc
文件放置在项目的根目录中时和 babel.config.js
效果一致,我们只需要选择一种创建即可。如果两种类型的配置文件都存在,.babelrc
会覆盖 babel.config.js
的配置。
babel.config.js
或是babel.config.json
当前项目维度 (Project Wide)的配置文件,相当于一份全局配置,如果 babel 决定应用这个配置文件,则一定会应用到所有文件的转换。.babelrc
或是.babelrc.js、.babelrc.json
相对文件(File Relative)的配置文件,babel
决定一个 js 文件应用哪些配置文件时,会执行如下策略: 如果要转换的这个 js 文件在当前项目内,则会先递归向上搜索最近的一个.babelrc
文件(直到遇到package.json目录),将其与全局配置合并。如果这个 js 文件不在当前项目内,则只应用全局配置,忽略与这个文件相关的.babelrc
。这两个我更愿意将其称为全局配置 (babel.config.js) 和局部配置 (.babelrc) 便于理解一些。
由此便引入了一个非常关键的对 **当前项目** 的定义问题。
这个定义在官方文档就有说明
New in Babel 7.x, Babel has a concept of a "root" directory, which defaults to the current working directory. For project-wide configuration, Babel will automatically search for a babel.config.json file, or an equivalent one using the supported extensions, in this root directory.大白话就是: 当前所在目录即为*当前项目*根目录,babel 会读取当前所在目录的 babel.config.js 作为全局配置。
—— 为了清晰记忆上面的规则我来稍微总结一下:
babel 会在当前执行目录搜索 babel.config.js, 若有则读取并作为全局配置,若无则全局配置为空。然后在转换一个具体的js文件时会去判断,如果这个文件在当前执行目录外面,则只应用全局配置。如果这个文件在当前执行路径内,则会去基于这个文件向上搜索最近的一个 .babelrc ,将其与全局配置合并作为转换这个文件的配置。
babel的配置文件的配置方法都一样,本文以 .babelrc 配置文件为主
在初次接触Babel我们只要用到一下两项配置:预设 和 插件
预设只是插件的集合,您可以在plugins
阵列中单独包含插件,也可以在阵列中包含插件集合presets
,如果插件是集合(预设)的一部分,则不必单独包含它plugins
。
当你包含它们时,npm包也是如此package.json
。
预设是插件的集合或正如他们所说:
预设是可共享的.babelrc配置或简单的一系列babel插件。
* .babelrc
{
"presets": [...],
"plugins": [...]
}
下面介绍presets与plugins
🌲 Plugins
Plugins顾名思义插件。
Babel的代码转换是通过在配置文件中应用插件(或预设)来启用的。
babel 本身不具有任何转化功能,我们要的代码要转换某些功能,比如将es6转换为es5,我们就需要下载相应的插件,并且将这些插件配置到.babelrc文件的plguins里面。
如何使用插件
如果插件是在npm上,你可以传入插件的名称,Babel会检查它是否安装在node_modules中。这是添加到插件配置选项,它接受一个数组。
* .babelrc
{
"plugins": ["babel-plugin-myPlugin", "@babel/plugin-transform-runtime"]
}
你也可以为你的插件指定一个相对/绝对路径。
* .babelrc
{
"plugins": ["./node_modules/asdf/plugin"]
}
默认情况下,Babel希望插件的名称中有一个Babel -plugin或Babel -preset前缀。为了避免重复,Babel有一个名称规范化阶段,将在加载项时自动添加这些前缀。
有关配置插件或预置路径的更多细节,请参阅名称规范化:参数 · Babel 中文文档 | Babel中文网
插件的使用方法:举个例子
Babel 使用插件来执行代码转换,插件其实就是用 JavaScript 所写的程序片段。比如,将箭头函数转换为浏览器能识别的普通函数,我们就需要用到 @babel/plugin-transform-arrow-functions插件,并将其添加到配置文件,它的代码实现如下:
1. 首先下载插件
npm i @babel/plugin-transform-arrow-functions -D
2. 添加至配置文件
* .babelrc
{
"plugins":[
"@babel/plugin-transform-arrow-functions"
]
}
这样babel就能够将箭头函数转换为普通函数了
//转换前
var a = () => {};
//转换后
var a = function () {};
🙋♂️ Q:什么?你怎么知道要用这个插件?
🤷 A:看文档啊!插件 · Babel 中文文档 | Babel中文网
转换插件
转换插件用于转换你的代码。
转换插件将启用相应的语法插件,因此你不必同时指定这两种插件。
语法插件
大多数语法都可以通过Babel进行转换。在更罕见的情况下(如果转换还没有实现,或者没有默认的方式来实现),你可以使用@babel/plugin-syntax-bigint这样的插件,只允许Babel解析特定类型的语法。或者您想保留源代码,因为您只想让Babel进行代码分析或代码删除。
注意:如果已经使用了相应的转换插件,则不需要指定语法插件,因为它会自动启用它。
* .babelrc
或者,你也可以通过 Babel 解析器传递任何 plugins 参数 :
JSON
{
"parserOpts": {
"plugins": ["jsx", "flow"]
}
}
插件顺序
插件的排列顺序很重要。
这意味着如果两个转换插件都将处理“程序(Program)”的某个代码片段,则将根据转换插件或 preset 的排列顺序依次执行。
- 插件在 Presets 前运行。
- 插件顺序从前往后排列。
- Preset 顺序是颠倒的(从后往前)。
例如:
* .babelrc
{
"plugins": ["transform-decorators-legacy", "transform-class-properties"]
}
先执行 transform-decorators-legacy
,在执行 transform-class-properties
。
重要的时,preset 的顺序是 颠倒的。如下设置:
* .babelrc
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
将按如下顺序执行: 首先是 @babel/preset-react
,然后是 @babel/preset-env
。
插件参数
插件和 preset 都可以接受参数,参数由插件名和参数对象组成一个数组,可以在配置文件中设置。
如果不指定参数,下面这几种形式都是一样的:
* .babelrc
{
"plugins": ["pluginA", ["pluginA"], ["pluginA", {}]]
}
要指定参数,请传递一个以参数名作为键(key)的对象。
* .babelrc
{
"plugins": [
[
"transform-async-to-module-method",
{
"module": "bluebird",
"method": "coroutine"
}
]
]
}
preset 的设置参数的工作原理完全相同:
* .babelrc
{
"presets": [
[
"env",
{
"loose": true,
"modules": false
}
]
]
}
插件开发
请参考 babel-handbook 学习如何创建自己的插件。
一个简单的用于反转名称顺序的插件(来自于首页):
JavaScript
export default function() {
return {
visitor: {
Identifier(path) {
const name = path.node.name;
// reverse the name: JavaScript -> tpircSavaJ
path.node.name = name
.split("")
.reverse()
.join("");
},
},
};
}
🌲 Presets
Presets顾名思义预设。预设是可共享的.babelrc
配置或简单的一系列babel插件.
我们要转换一些语法就得使用各种插件,并且添加到配置文件,如果每次项目需要的babel插件都差不多,而我们每次都要进行重复的下载,配置工作,这样效率是不是很低,很繁琐。
这个时候我们就可以利用presets这个功能,将一些常用的babel插件配置放入预设中,下载后直接将这个预设放入配置文件即可。具体操作可参照文档:预设(Presets) · Babel 中文文档 | Babel中文网
如何使用预设
在Babel配置中,如果预设是在npm上,你可以传入预设的名称,Babel会检查它是否已经安装在node_modules中。这被添加到presets配置选项中,该选项接受一个数组。
* .babelrc
{
"presets": ["babel-preset-myPreset", "@babel/preset-env"]
}
除此之外,您也可以指定预设的相对路径或绝对路径。
{
"presets": ["./myProject/myPreset"]
}
默认情况下,Babel希望插件的名称中有一个Babel -plugin或Babel -preset前缀。为了避免重复,Babel有一个名称规范化阶段,将在加载项时自动添加这些前缀。
有关配置插件或预置路径的更多细节,请参阅名称规范化:参数 · Babel 中文文档 | Babel中文网
预设的使用方法,举个例子
例如,你经常看到的 @babel/preset-env、@babel/preset-react
1.下载preset
npm i @babel/preset-env
2. 配置文件
* .babelrc
{
"presets":[ "preset-env" ]
}
🙋♂️ Q:我怎么知道有哪些预设?
🤷 A:看文档 ,在npm上搜preset
🙋♂️ Q:我怎么知道预设里面有哪些插件?
🤷 A:要知道预设里面有哪些插件,最好的方式就是搜npm看他的依赖项
可以看到preset-env有 76个插件
不过,preset 分为以下几种:
◾ 第一种:官方内容 env, react, flow 等
官方提供的预设
babel官方已经针对常用环境编写了一些预设(preset):
- @babel/preset-env for compiling ES2015+ syntax
- @babel/preset-typescript for TypeScript
- @babel/preset-react for React
- @babel/preset-flow for Flow
◾ 第二种:Stage-X(实验性质的预设),这里面包含的都是当年最新规范的草案,每年更新。
TC39 将提案分为以下几个阶段:
- Stage 0 - 设想(Strawman):只是一个想法,经过 TC39 成员提出即可,可能有 Babel插件。
- Stage 1 - 建议(Proposal):这是值得跟进的,初步尝试。
- Stage 2 - 草案(Draft):初始规范。
- Stage 3 - 候选(Candidate):完成规范并在浏览器上初步实现。
- Stage 4 - 完成(Finished):将添加到下一个年度版本发布中。
这也就是你在早期的项目或文档中看到 stage-0,stage-1的字眼,原来他们都是预设,不过现在你不用纠结这个问题Babel7已经放弃stage预设了。
如何创建预设
如需创建一个自己的预设(无论是为了本地使用还是发布到 npm),需要导出(export)一个配置对象。
可以是返回一个插件数组...
JavaScript
module.exports = function() {
return {
plugins: ["pluginA", "pluginB", "pluginC"],
};
};
preset 可以包含其他的 preset,以及带有参数的插件。
JavaScript
module.exports = () => ({
presets: [require("@babel/preset-env")],
plugins: [
[require("@babel/plugin-proposal-class-properties"), { loose: true }],
require("@babel/plugin-proposal-object-rest-spread"),
],
});
有关更多信息,请参考 babel 手册 中关于 preset 的部分。
预设的排列顺序
Preset 是逆序排列的(从后往前)。
* .babelrc
{
"presets": ["a", "b", "c"]
}
将按如下顺序执行: c
、b
然后是 a
。
这主要是为了确保向后兼容,由于大多数用户将 "es2015" 放在 "stage-0" 之前。
预设的参数
插件和 preset 都可以接受参数,参数由插件名和参数对象组成一个数组,可以在配置文件中设置。
如果不指定参数,下面这几种形式都是一样的:
* .babelrc
{
"presets": [
"presetA", // bare string
["presetA"], // wrapped in array
["presetA", {}] // 2nd argument is an empty options object
]
}
要指定参数,请传递一个以参数名作为键(key)的对象。
* .babelrc
{
"presets": [
[
"@babel/preset-env",
{
"loose": true,
"modules": false
}
]
]
}
🌲 Polyfill
Plolyfill 垫片。| 使用指南 · Babel 中文文档 | Babel中文网
babel默认只转换新的 JavaScript 语法,比如箭头函数、扩展运算(spread)不转换新的 API,例如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign)都不会转译。如果想使用这些新的对象和方法,则需要为当前环境提供一个垫片(polyfill)。
Polyfill主要有三种
1、@babel/polyfill
Babel 包含一个polyfill 库即@babel/polyfill。@babel/polyfill模块包含 core-js 和一个自定义的 regenerator runtime 来模拟完整的 ES2015+ 环境。
◼️ 什么是regenerator?
npm地址:regenerator-runtime - npm
github地址:https://github.com/facebook/regenerator
官方对他的描述直译过来是;
Standalone runtime for Regenerator-compiled generator and
async
functions.
重新生成编译的生成器和异步函数的独立运行时 | Regenerator编译的生成器和异步函数的独立运行时。
在网上找到这样一句话,解释道:
生成器函数、async、await函数经babel编译后,regenerator-runtime模块用于提供功能实现。
博主对于生成器(regenerator)也是一知半解,这里我们就简单的理解成,项目中要使用async、await函数就必须使用这个库吧!🤔 (对于regenerator有知道更详细的,欢迎下方留言已作解释)。
◼️ 什么是core-js?
npm地址:core-js - npm
下面是babel官方对core-js的解释:
Modular standard library for JavaScript. Includes polyfills for ECMAScript up to 2023: promises, symbols, collections, iterators, typed arrays, many other features, ECMAScript proposals, some cross-platform WHATWG / W3C features and proposals like URL. You can load only required features or use it without global namespace pollution.
直译:JavaScript的模块化标准库。包括到2023年的ECMAScript的Polyfill: promises、symbols、 collections 、iterators、 typed 、arrays、许多其他特性、ECMAScript提案、跨平台WHATWG / W3C特性和提案,比如URL。您可以只加载所需的特性,或者在不污染全局命名空间的情况下使用它。
简单粗暴的理解就是,你要使用一些js高级特性,如promise就得使用这个库。
经过上面潦草的描述,我们在总结一下:
由于babel只能将es6+语法转换为低级语法,而当我们使用一些高级特性时比如 async、await类似的Api,babel就显得无能为力了,因为babel无法实现这些高级Api的功能,这个时候就需要一个垫片(polyfill),而babel又包含了一个polyfill叫@babel/polyfill,这个polyfill本身也无法实现像async等高级API的功能,但是市面上有现成的封装好的类库实现了,于是@babel/polyfill将他们包含进来。这样当我们引入@babel/polyfill时,就可以丝滑的写高级语法了!
◼️ 使用方式
1. 下载
npm方式
npm install --save @babel/polyfill
注意,使用
--save
参数而不是--save-dev
,因为这是一个需要在你的源码之前运行的 polyfill。也就是说polyfill要在源代码之前运行!
Yarn方式
yarn add @babel/polyfill
2. 在入口文件导入
import "@babel/polyfill";
🚨 从 Babel 7.4.0 版本开始,这个软件包已经不建议使用了,建议直接包含
core-js/stable
(用于模拟 ECMAScript 的功能):import "core-js/stable";
当然在webpack中你也可以这样干,在@babel/polyfill的描述有这样一段
The polyfill is provided as a convenience but you should use it with @babel/preset-env and the useBuiltIns option so that it doesn’t include the whole polyfill which isn’t always needed. Otherwise, we would recommend you import the individual polyfills manually
提供polyfill是为了方便,但您应该将其与@babel/preset-env和use Built-Ins选项一起使用,这样它就不会包括并非总是需要的整个polyfill。否则,我们建议您手动导入单个polyfill
大概意思就是建议我们将@babel/polyfill和@babel/preset-env配合使用并根据需求设置 useBuiltlns选项,这样就不至于将这个polyfill加载进来,显得很大。不然就使用手动导入各个polyfill的方式。
—— 在webpack中我们可以将@babel/polyfill和@babel/preset-env配合使用!
主要是设置 useBuiltlns选项。更多preset-env的配置请参照:@babel/preset-env · Babel
{
"presets" : [
["@babel/preset-env",{
"targets": {
"browsers": [
"last 2 versions",
"not ie <= 9"
]
},
"useBuiltIns": "usage"
}]
]
}
useBuiltlns:false:此时不对 polyfill 做操作。如果引入 @babel/polyfill,则无视配置的浏览器兼容,引入所有的 polyfill。
useBuiltlns:usage:按需加载polyfill,根据配置的浏览器兼容以及代码所用到的polyfill,不至于将所有polyfill加载进来,使用这种方式我们不用手动导入polyfill,但是需要安装。
useBuiltlns:entry:根据配置的浏览器兼容,引入浏览器不兼容的 polyfill。需要在入口文件手动添加 import @babel/polyfill,会自动根据 browserslist 替换成浏览器不兼容的所有 polyfill。
—— @babel/polyfill带来的问题
babel-polyfill,通过改写全局prototype的方式实现,它会加载整个polyfill,针对编译的代码中新的API进行处理,并且在代码中插入一些帮助函数,比较适合单独运行的项目。
babel-polyfill解决了Babel不转换新API的问题,但是直接在代码中插入帮助函数,会导致污染了全局环境,并且不同的代码文件中包含重复的代码,导致编译后的代码体积变大。虽然这对于应用程序或命令行工具来说可能是好事,但如果你的代码打算发布为供其他人使用的库,或你无法完全控制代码运行的环境,则会成为问题。
后来@babel/polyfill凉了,通过官方我们知道,从 Babel 7.4.0起 @babel/polyfill已经被弃用了。
之前使用的方式现在改成了
//import "@babel/polyfill"; //之前的写法
import "core-js/stable";
import "regenerator-runtime/runtime";
2、babel-runtime
npm install --save babel-runtime
为了解决 @babel/polyfill带来的问题,Babel提供了单独的包babel-runtime用于提供编译模块的工具函数,启用插件babel-plugin-transform-runtime后,Babel就会使用babel-runtime下的工具函数。
babel-runtime插件能够将这些工具函数的代码转换成require语句,指向为对babel-runtime的引用。每当要转译一个api时都要手动加上require(‘babel-runtime’)。简单说 babel-runtime 更像是一种按需加载的实现,比如,你哪里需要使用 Promise,只要在这个文件头部 require Promise from ‘babel-runtime/core-js/promise’就行了。
不过如果你许多文件都要使用 Promise,难道每个文件都要 import 一遍不成?
3、babel-plugin-transform-runtime
为了方便使用 babel-runtime,解决手动 require 的苦恼。它会分析我们的 ast 中,是否有引用 babel-rumtime 中的垫片(通过映射关系),如果有,就会在当前模块顶部插入我们需要的垫片。
transform-runtime 是利用 plugin 自动识别并替换代码中的新特性,你不需要再引入,只需要装好 babel-runtime 和 配好 plugin 就可以了。
好处是按需替换,检测到你需要哪个,就引入哪个 polyfill,如果只用了一部分,打包完的文件体积对比 babel-polyfill 会小很多。而且 transform-runtime 不会污染原生的对象,方法,也不会对其他 polyfill 产生影响。
所以, transform-runtime 的方式更适合开发工具包,一方面是体积够小,另一方面是用户(开发者)不会因为引用了我们的工具包而污染了全局的原生方法,产生副作用,还是应该留给用户自己去选择。
🎋 babel-runtime 和 babel-plugin-transform-runtime
在大多数情况下,你应该安装 babel-plugin-transform-runtime 作为开发依赖(使用 --save-dev),并且将 babel-runtime 作为生产依赖(使用 --save)。这个看vue-cli生成的 package.json就能发现。
因为babel编译es6到es5的过程中,babel-plugin-transform-runtime这个插件会自动polyfill es5不支持的特性,这些polyfill包就是在babel-runtime这个包里(core-js 、regenerator等)
npm install —save-dev babel-plugin-transform-runtime
npm install —save babel-runtime
🎋 使用技巧分享
以下内容摘自:babel从入门到精通_babel学习
◼️ @babel/preset-env 包含了一些基本es语法转换的插件(箭头函数、类转换等等),同时还支持polyfill,有usage跟entry模式,但是preset-env添加polyfill会像之前使用@babel/polyfill一样,会污染全局变量。
◼️ @babel/plugin-transform-runtime 主要是利用@babel/runtime提取了一些公共的babel帮助函数,同时也支持polyfill的添加,添加的polyfill都是以一个局部变量的形式引入,不会污染全局变量。
◾ 如果你做的是一个二方库,然后需要被别人依赖,那么建议使用@babel/plugin-transform-runtime来引入polyfill,因为你要尽可能的专注于做自己的事,而不是说去影响别人,语法转换可以使用preset-env预设,比如以下配置:
module.exports = {
presets: [['@babel/preset-env']],
plugins: [
[
'@babel/plugin-transform-runtime',
{
corejs: { version: 3, proposals: true },
helpers: true,
useESModules: true,
regenerator: true,
absoluteRuntime: './node_modules'
}
]
]
}
◾ 如果你做的是一个普通的业务项目的话,可以用preset-env来转换语法和polyfill,然后再利用@babel/plugin-transform-runtime来引入helpers跟generator做到代码重复利用,比如以下配置:
module.exports = {
presets: [
[
'@babel/preset-env',
{
corejs: 3,
useBuiltIns: 'usage'
}
]
],
plugins: [
[
'@babel/plugin-transform-runtime',
{
corejs: false,
helpers: true,
useESModules: false,
regenerator: true,
absoluteRuntime: './node_modules'
}
]
]
}
◾ @babel/plugin-transform-runtime 常见配置参照:@babel/plugin-transform-runtime · Babel
◼️ 下面再介绍几个与Babel相关的:
- @babel/core:babel的核心库
- babel-loader:使用webpack时作为有个loader在代码混淆之前进行代码转换
- @babel/preset-env:babel预设的一种
- @babel/cli:允许使用babel命令转译文件
到此相信你已经对babel有个初步了解,还有很多细节本文没有提及,欢迎指教。
🔆 参考文献
Babel 中文文档 | Babel中文网 · Babel 中文文档 | Babel中文网
预设(Presets) · Babel 中文文档 | Babel中文网