公众号

前言

如果喜爱我的文章,欢送关注微信公众号:前端学社。精彩内容不容错过

思考问题

带着一下三个问题阅读文章,上面三个问题也是面试中常常会被问到的,因为作为工具开发者(这里要和业务项目区离开) 如果想提供一个高质量的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版本不反对 asyncchrome 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包外面,须要的时候间接通过importrequire形式从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/polyfillcore-js/stable与regenerator-runtime/runtime。此时,咱们能够借助@babel/plugin-transform-runtime实现 API转换。

@babel/plugin-transform-runtime参数很多,咱们先看一下corejs

corejs optionInstall command
falsenpm install --save @babel/runtime
2npm install --save @babel/runtime-corejs2
3npm 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-corejs2npm 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