关于SegmentFault:babel7知识

平时在开发的过程中,咱们可能并不太须要非常理解babel的内容,仅仅晓得它可能将新个性的代码转换成可能在旧版本浏览器中运行的代码。然而这一次想要趁着本人搭建脚手架的机会去进一步的理解babel的常识,所以写了这篇文章。以下内容是babel 7.4之后的版本,也就是@babel/polyfill被废除须要独立装置core-jsregenerator-runtime 模块的版本。

babel命令行工具 @babel/cli

@babel/cli是babel的命令行工具,次要提供babel命令。另外还须要装置@babel/core能力应用babel去编译。

npm install --save-dev @babel/core @babel/cli

将命令配置在 package.json 文件的 scripts 字段中:

// package.json
"scripts": {
    "compiler": "babel src --out-dir lib --watch"
}

这样就可能通过npm run compiler来执行编译,然而babel自身什么都不做,须要增加插件来帮忙babel实现工作。

plugin

babel所有性能都建设在各种的plugin上,应用形式是装置相应的plugin再去配置文件中去应用。例如箭头函数转换插件,
装置@babel/plugin-transform-arrow-functions,而后在.babelrc配置文件中去指定对应的插件

//.babelrc
{
  plugins: ["@babel/plugin-transform-arrow-functions"],
};

而后执行npm run compiler,能够看到箭头函数曾经被编译实现

然而如果咱们每个性能都去一个个增加对应的plugin会很麻烦,多以咱们就须要preset预设去间接增加一组插件。

preset

preset就是一组插件的汇合,最罕用的preset就是@babel/preset-env

@babel/preset-env

它的作用是依据指标环境去进行语法转换和导入对应的polyfill

须要留神的是,@babel/preset-env会依据你配置的指标环境,生成插件列表来编译。默认状况下,如果你没有在 Babel 配置文件中(如 .babelrc)设置 targets 或 ignoreBrowserslistConfig,@babel/preset-env 会应用 package.jsonbrowserslist 配置源。

咱们能够模仿生产环境和开发环境的浏览器版本

const product = ["ie >= 9"];
const development = ["last 2 Chrome versions"];

通过设置不同浏览器环境应用@babel/preset-env去编译雷同代码,能够看到最终的后果也会不同。

module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        // targets: product,
        targets: development,
      },
    ],
  ],
};

babel 只负责对语法进行编译,比方当咱们写箭头函数,babel 会帮你把它编译成一般函数。然而对一些新的扩大办法,新的类来说babel就不能转换了。这时就须要去引入polyfillpolyfill的中文意思是垫片,所谓垫片就是垫平不同浏览器或者不同环境下的差别,让新的内置函数、实例办法等在低版本浏览器中也能够应用。

polyfill

babel v7.4版之后,须要间接装置core-jsregenerator-runtime去代替之前的@babel/polyfillcroe-js 提供了 ES5、ES6 标准中新定义的各种对象、办法的polyfill,regenerator-runtime 用来实现 ES6/ES7 中 generators、yield、async 及 await 等相干的 polyfill。

首先,咱们须要装置他们到生产环境中,因为须要在生产环境中运行其中的polyfill

npm install --save core-js regenerator-runtime

@babel/preset-env的配置项中把useBuiltIns设置成usage,这样会依据指标浏览器环境去引入所须要的polyfill。须要留神点是,设置useBuiltIns还须要同时设置corejs

//.babelrc
const product = ["ie >= 9"];
const development = ["last 2 Chrome versions"];

module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        targets: development,
        useBuiltIns: "usage",
        corejs: 3,
      },
    ],
  ],
};
//index.js
const isHas = [1, 2, 3].includes(2);

const getData = () =>
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(100);
    }, 1000);
  });

const main = async () => {
  const res = await getData();
  console.log(res);
};

main();

编译后的文件:

能够看到,编译后的文件中只引入了所用到的polyfill。

useBuiltIns还能够设置成其余值,比方entry,这须要在我的项目入口文件手动引入polyfills,例如@babel/polyfill或者core-js

//.babelrc
module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        targets: product,
        useBuiltIns: "entry",
        corejs: 3,
      },
    ],
  ],
};



//index.js
// 入口文件引入core-js
require("core-js");

然而这种形式会引入全量的polyfill。

useBuiltIns默认值为false,代表每个文件里不主动增加polyfill,或不将import "@babel/polyfill"转换为独自的polyfill。

@babel/plugin-transform-runtime

@babel/plugin-transform-runtime能够重复使用 Babel 注入的帮忙程序

在应用@babel/preset-env配合useBuiltIns: usage时,文件中会引入一些辅助办法例如_classCallCheck,当多处文件都应用到class时同样也会在每个文件中去引入这些辅助办法,这样会增大打包体积并且齐全没有必要屡次去引入同样的辅助办法。

//index.js
class A {}


//.babelrc.js
const product = ["ie >= 9"];
const development = ["last 2 Chrome versions"];

module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        targets: product,
        useBuiltIns: "usage",
        corejs: 3,
      },
    ],
  ],
};

编译后果:

"use strict";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var A = function A() {
  _classCallCheck(this, A);
};

为了解决这个问题就须要应用@babel/plugin-transform-runtime,应用该插件,所有辅助办法都将援用模块 @babel/runtime,这样就能够防止编译后的代码中呈现反复的辅助办法,无效缩小包体积。

@babel/plugin-transform-runtime须要配合@babel/runtime来应用,@babel/plugin-transform-runtime在开发时应用,最终代码须要依赖@babel/runtime

npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
//index.js
class A {}

//.babelrc.js
const product = ["ie >= 9"];
const development = ["last 2 Chrome versions"];

module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        targets: product,
        useBuiltIns: "usage",
        corejs: 3,
      },
    ],
  ],
  //应用@babel/plugin-transform-runtime
  plugins: [["@babel/plugin-transform-runtime"]],
};

编译后果:

"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));

var A = function A() {
  (0, _classCallCheck2.default)(this, A);
};

能够看到这些辅助办法都是从@babel/runtime中引入。

@babel/plugin-transform-runtime能够创立一个沙盒环境来防止对全局环境的净化

之前在应用@babel/preset-env编译promise和includes时会引入core-js中的全局变量或者在对应的原型链中增加相应的办法,这样都造成了全局环境的净化。尽管这对于应用程序或命令行工具是能够的,然而如果你的代码是要公布供别人应用的库,或者无奈齐全管制代码运行的环境,则将成为一个问题。

首先,独自应用@babel/plugin-transform-runtime只可能解决辅助办法,如果想要去引入polyfill就须要配合@babel/runtime-corejs3应用。

同样还是在生产环境装置@babel/runtime-corejs3

npm install @babel/runtime-corejs3 --save

这里须要在.babelrc中去除@babel/preset-env配置中对于polyfill的局部免得与@babel/runtime-corejs3反复。

//index.js
const isHas = [1, 2, 3].includes(2);

const getData = () =>
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(100);
    }, 1000);
  });

getData();

//.babelrc.js
module.exports = {
  presets: [["@babel/preset-env"]],
  plugins: [
    [
      "@babel/plugin-transform-runtime",
      {
        corejs: 3,
      },
    ],
  ],
};

编译后果:

"use strict";

var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault");

var _setTimeout2 = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/set-timeout"));

var _promise = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/promise"));

var _includes = _interopRequireDefault(require("@babel/runtime-corejs3/core-js-stable/instance/includes"));

var _context;

var isHas = (0, _includes["default"])(_context = [1, 2, 3]).call(_context, 2);

var getData = function getData() {
  return new _promise["default"](function (resolve, reject) {
    (0, _setTimeout2["default"])(function () {
      resolve(100);
    }, 1000);
  });
};

getData();

能够看到,应用@babel/plugin-transform-runtime会用一个长期变量去保留polyfill中的一些值,并不是间接去批改原型链或者新增Promise办法。

在个别开发中应用@babel/preset-env配合useBuiltIns: usage,在开发第三方库时应用@babel/plugin-transform-runtime

在下面介绍@babel/plugin-transform-runtime的一些应用时能够看到,它不仅可能解决引入屡次helper辅助办法的问题,而且在只引入所需polyfill时还不会净化全局环境,那还有必要应用@babel/preset-envuseBuiltIns吗?

其实@babel/plugin-transform-runtime配合@babel/runtime-corejs3引入polyfill有一个很大的有余就是不可能通过设置指标环境去引入所须要的polyfil。,咱们在一般开发时只须要在package.json中的browserslist去设置开发环境和生产环境的浏览器版本,而后通过应用@babel/preset-envuseBuiltIns就可能依据不同的运行环境去引入适当的polyfill。

然而在开发第三方库时,不能确定代码的运行环境,所以就须要利用@babel/plugin-transform-runtime来保障引入的polyfill不去净化全局环境。

最初总结

个别开发: 通过useBuiltIns: usage去保障引入失当的polyfill,通过@babel/plugin-transform-runtime保障辅助函数都是援用@babel/runtime`。

const product = ["ie >= 9"];
const development = ["last 2 Chrome versions"];

module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        //代替browserslist设置浏览器版本
        targets: product,
        useBuiltIns: "usage",
        corejs: 3,
      },
    ],
  ],
  plugins: [["@babel/plugin-transform-runtime"]],
};

参考文章

  • https://juejin.cn/post/684490…
  • https://zhuanlan.zhihu.com/p/…
  • https://zhuanlan.zhihu.com/p/…

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理