乐趣区

关于javascript:手把手教你如何配置Babel4babelruntimebabelplugintransformruntime

公众号

前言

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

思考问题

带着一下三个问题阅读文章,上面三个问题也是面试中常常会被问到的,因为作为 工具开发者(这里要和业务项目区离开) 如果想提供一个高质量的 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 包外面,须要的时候间接通过 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 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
退出移动版