乐趣区

关于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/…
退出移动版