公众号
前言
如果喜爱我的文章,欢送关注微信公众号:前端学社。精彩内容不容错过
思考问题
带着一下三个问题阅读文章,上面三个问题也是面试中常常会被问到的,因为作为 工具开发者(这里要和业务项目区离开) 如果想提供一个高质量的 npm 包给他人应用,必然会遇到以下问题
@babel/runtime
的庐山真面是什么?@babel/plugin-transform-runtime
又是什么?@babel/runtime
和@babel/plugin-transform-runtime
之间是什么关系?
@babel/runtime 是什么
咱们先看一下一个代码演示
Demo 代码仓库地址 tutor-runtime01
step1: 装置 npm 包
npm install --save-dev @babel/cli @babel/core @babel/preset-env
step2: 批改 .babelrc
配置文件内容
{
"presets": [
["@babel/preset-env"]
]
}
转换之前的代码:
async function foo () {return 1}
因为 async 在 chrome55 版本 才反对 ,所以咱们设置指标环境配置表文件 .browserslistrc
内容如下:
# async 在 chrome55 版本,50 版本不反对 async
chrome 50
执行npm run build
, 查看转换后的后果
"use strict";
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {try { var info = gen[key](arg); var value = info.value; } catch (error) {reject(error); return; } if (info.done) {resolve(value); } else {Promise.resolve(value).then(_next, _throw); } }
function _asyncToGenerator(fn) {return function () {var self = this, args = arguments; return new Promise(function (resolve, reject) {var gen = fn.apply(self, args); function _next(value) {asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) {asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
function foo() {return _foo.apply(this, arguments);
}
function _foo() {_foo = _asyncToGenerator(function* () {return 1;});
return _foo.apply(this, arguments);
}
转换之后,发现 @babel/preset-env
在做语法转换的时候,注入了这些函数申明 ,以便语法转换后应用。这些函数申明称之为 辅助函数。
问题来了,假如你是 工具开发者 , 编译并公布后的 npm 包外面 蕴含了很多相似 asyncGeneratorStep
和_asyncToGenerator
自定义 辅助函数 。如果有一个业务的我的项目,依赖了 10 个 npm 包,每个 npm 包都自定了相似 asyncGeneratorStep
和_asyncToGenerator
自定义 辅助函数 , 相当于反复减少了 10 次,这样会导致包的体积十分大。所以, 接下来要解决如何将雷同的辅助函数共用。
如何解决呢?
答案: 把这些 辅助函数 申明放到一个 npm 包外面,须要的时候间接通过 import
或require
形式从 npm 包引入。这样即便在多的文件,然而引入门路是一样的。通过 webpack 这一类的构建工具打包的时候,npm 包只会引入一次,达到了复用的成果。
那么有没有将所有可能须要的 辅助函数 集中到一起的 npm 包呢?
答案: @babel/runtime
就是
此刻,你明确了什么是 @babel/runtime
是什么了吗?如果不明确在看一遍后面的内容。
@babel/plugin-transform-runtime 是什么?
下面咱们提到,@babel/runtime
蕴含了所有辅助函, 理论开发中,不可能手动引入 @babel/runtime/helpers
外面的辅助函数。须要借助 @babel/plugin-transform-runtime
的神奇力量。
其实 @babel/plugin-transform-runtime
只做了以下两件事件。
- 1:主动移除语法转换后内联的辅助函数,应用
@babel/runtime/helpers
里的辅助函数来代替; -
2:当代码里应用了
core-js
的 API,主动引入@babel/runtime-corejs3/core-js-stable/
,以此来代替全局引入的core-js/stable
;如果你还不理解core-js 那么先去看看我之前写的文章《手把手教你如何配置 Babel(2)——揭开 core-js 的面纱》
上面离开对这两局部进行 demo 演示:
先看一下如何 主动移除语法转换后内联的辅助函数:Demo 代码仓库地址 tutor-runtime02
装置 npm 包
npm i @babel/runtime
npm i -D @babel/cli @babel/core @babel/preset-env @babel/plugin-transform-runtime
增加 .babelrc
配置
{
"presets": [
["@babel/preset-env"]
],
"plugins": ["@babel/plugin-transform-runtime"]
}
执行npm run build
查看后果:
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
function foo() {return _foo.apply(this, arguments);
}
function _foo() {_foo = (0, _asyncToGenerator2.default)(function* () {return 1;});
return _foo.apply(this, arguments);
}
能够看到,编译后的后果中,主动导入了所须要的辅助函数。
仔细的人可能发现了,本章节的两个 demo 中,并没有设置 babel/preset-env
的参数。也就是 只做了语法转换,没有补齐 API。那么 @babel/plugin-transform-runtime
是如何补齐 API 的呢?让咱们提揭示持续往下看
如何补齐 API
在《手把手教你如何配置 Babel(3)—实在我的项目中如何去打补丁》章节外面,咱们通过如下形式全局引入 polyfill
import `@babel/polyfill`
// 或者 import 'core-js'
这种形式是在全局补齐了 API,对浏览器全局对象进行了从新赋值。例如 Promise, 重写了 window.Promise 及其原型链
所以,当不想扭转或补齐浏览器的 window.Promise,就不能手动的导入 @babel/polyfill
或core-js/stable 与 regenerator-runtime/runtime
。此时,咱们能够借助 @babel/plugin-transform-runtime
实现 API 转换。
@babel/plugin-transform-runtime
参数很多, 咱们先看一下corejs
。
corejs option | Install command |
---|---|
false | npm install –save @babel/runtime |
2 | npm install –save @babel/runtime-corejs2 |
3 | npm install –save @babel/runtime-corejs3 |
corejs:2 和 corejs:3 区别
- corejs 值为 2:仅反对全局变量 (例如:Promise) 和动态属性(例如:Array.from)
- corejs 值为 3:不仅反对反对全局变量 (例如:Promise) 和动态属性(例如:Array.from),而且还反对实例属性(例如:[].includes)
所以 3 版本反对的性能更多,上面咱们用 2 版本做一个 demo 解说。3 版本的 demo,大家能够去下载源码看一下 - corejs:2 代码仓库地址
- corejs:3 代码仓库地址
配置.babelrc
{
"presets": [
["@babel/preset-env"]
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
// 这里应用的 2 版本 对应须要装置 npm install --save @babel/runtime-corejs2
"corejs": 2
}
]
]
}
装置 npm 包
npm i --save @babel/runtime-corejs2
npm i --save-dev @babel/cli @babel/core @babel/preset-env @babel/plugin-transform-runtime
编译前代码:
Promise.resolve()
编译后代码:
"use strict";
var _interopRequireDefault = require("@babel/runtime-corejs2/helpers/interopRequireDefault");
var _promise = _interopRequireDefault(require("@babel/runtime-corejs2/core-js/promise"));
_promise.default.resolve();
参考文献
-
Babel
- Babel 官网
- 99% 的前端开发者没弄明确的 babel 常识
- Babel 插件有啥用
- 一文搞清楚前端 polyfill
- @babel/plugin-transform-runtime
- @babel/plugin-transform-runtime 到底是什么?
-
browserslist
- browserslist
- browserslist 指标浏览器配置表
- browserslist 详解
-
Monorepo
- Monorepo
- 应用 MonoRepo 治理你的前端我的项目
- NPM 模块中的 scope