babel7知识

平时在开发的过程当中,咱们可能并不太须要十分了解babel的内容,仅仅知道它可以将新特性的代码转换成可以在旧版本浏览器中运行的代码。可是这一次想要趁着本身搭建脚手架的机会去进一步的了解babel的知识,因此写了这篇文章。如下内容是babel 7.4以后的版本,也就是@babel/polyfill被废弃须要独立安装core-jsregenerator-runtime 模块的版本。npm

babel命令行工具 @babel/cli

@babel/cli是babel的命令行工具,主要提供babel命令。另外还须要安装@babel/core才能使用babel去编译。json

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

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

// 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配置文件中去指定对应的插件babel

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

而后执行npm run compiler,能够看到箭头函数已经被编译完成async

可是若是咱们每一个功能都去一个个添加对应的plugin会很麻烦,多以咱们就须要preset预设去直接添加一组插件。函数

preset

preset就是一组插件的集合,最经常使用的preset就是@babel/preset-env工具

@babel/preset-env

它的做用是根据目标环境去进行语法转换和导入对应的polyfillpost

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

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

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 提供了 ES五、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"]],
};

参考文章

相关文章
相关标签/搜索