前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Babel 配置实验报告

Babel 配置实验报告

作者头像
河马嘴不大
发布2022-12-24 12:20:09
1K0
发布2022-12-24 12:20:09
举报

写在前面的话

JavaScript语言的更新,也伴随着 Babel 的成长, 对于一个前端而言,JavaScript 新的 API 自然很香,但代价就是我们要转译它,比较常用工具的就是 Babel 。

ECMA有很多版本,6、7、8、9 ….,前端浏览器也有多种,每种也有着不同的版本,为了实现这多对多的关系,Babel 也表示非常难,最后的结果就是,随着 Babel 的升级,前端同学有一堆包要学习和了解,如 @babel/cli@babel/core@babel/polyfill@babel/preset-env 等等,Babel 目前最新的版本是 7.7.0,前一次比较重大的升级是 7.4.0,本着客户第一(Babel 很香)的原则, 下面对 Babel 的配置做了一些实验,本文主要是对于 Babel 的使用,不针对工作原理。

一、实验目的

测试 Babel 的不同配置对于 JavaScript 编译结果的影响

二、实验环境和要求

依赖包版本

@babel/core 7.7.0 @babel/cli 7.7.0 @babel/preset-env 7.7.1 @babel/runtime 7.7.0 @babel/plugin-transform-runtime 7.7.0 @babel/corejs@3 7.7.0

实验基础数据

创建实验基本的样本文件 index.js

代码语言:javascript
复制
const cat = 'May'
const arrow = () => `Name: ${cat}`.padStart(2);
const promise = new Promise();
let map = new Map();

我们通过对 Babel 的配置进行修改,实验在不同配置下的编译结果 。 本实验使用 babel-cli 命令行直接编译和输出文件,对应的命令如下: npx babel index.js --out-file index_compile.js 将样本文件 index.js 编译输出到 index_compile.js 目标浏览器不配置,采用 Babel 默认,即转换所有 ECMAScript 2015+。

三、实验内容

初始化准备

首先安装实验用到的依赖包

代码语言:javascript
复制
npm install --save-dev @babel/core @babel/cli @babel/preset-env

项目根目录下创建 babel.config.js 文件,用于配置 Babel

代码语言:javascript
复制
module.exports = {};

####

配置一

不进行配置

代码语言:javascript
复制
module.exports = {};

实验结果:

代码语言:javascript
复制
const cat = 'May';

const arrow = () => `Name: ${cat}`.padStart(2);

const promise = new Promise();
let map = new Map();

除了多了两行空白,没有什么其它变化,说明 Babel 是基于插件架构的,假如你什么插件也不提供,那么 Babel 什么也不会做,即你输入什么输出的依然是什么。

配置二

增加 ES+ 转换集合包 @babel/preset-env

代码语言:javascript
复制
module.exports = {
  presets: ['@babel/preset-env'],
};

实验结果:

代码语言:javascript
复制
"use strict";

var cat = 'May';

var arrow = function arrow() {
  return "Name: ".concat(cat).padStart(2);
};

var promise = new Promise();
var map = new Map();

对样本中的 constlet 以及箭头函数和模板字符串语法进行了处理,但对于 padStartPromiseMap 并没有处理,说明 @babel/preset-env 只能处理 ES+ 中新增的基本语法,不能对新增类和类的扩展属性进行处理。

这里就需要我们使用 @babel/polyfill ,实现新的内置函数、实例方法的转换。在我们使用 @babel/preset-env 的同时,它有个 useBuiltIns 选项,用来控制怎么样处理 @babel/polyfill。这里的 useBuiltIns 有三个可选属性: 'entry' | 'usage' | false,默认是 false

为了实现编译结果的可运行,我们需要改变样本代码,如下:

代码语言:javascript
复制
import "@babel/polyfill";

const cat = 'May';
const arrow = () => `Name: ${cat}`.padStart(2);
const promise = new Promise();
let map = new Map();
配置三
代码语言:javascript
复制
module.exports = {
  presets: [
    ['@babel/preset-env', {
      useBuiltIns: false,
    }]
  ],
};

实验结果:

代码语言:javascript
复制
"use strict";

require("@babel/polyfill");

var cat = 'May';

var arrow = function arrow() {
  return "Name: ".concat(cat).padStart(2);
};

var promise = new Promise();
var map = new Map();

实验结果同配置二,做了基本语法的转译,直接引入了 @babel/polyfill 整个包。

配置四
代码语言:javascript
复制
module.exports = {
  presets: [
    ['@babel/preset-env', {
      useBuiltIns: 'usage',
    }]
  ],
};

实验结果:

代码语言:javascript
复制
"use strict";

require("core-js/modules/web.dom.iterable");

require("core-js/modules/es6.array.iterator");

require("core-js/modules/es6.string.iterator");

require("core-js/modules/es6.map");

require("core-js/modules/es6.promise");

require("core-js/modules/es6.object.to-string");

require("core-js/modules/es7.string.pad-start");

var cat = 'May';

var arrow = function arrow() {
  return "Name: ".concat(cat).padStart(2);
};

var promise = new Promise();
var map = new Map();

同时我们得到了一些 Warning:

Warning1

代码语言:javascript
复制
WARNING: We noticed you re using the `useBuiltIns` option without declaring a core-js version. Currently, we assume version 2.x when no version is passed. Since this default version will likely change in future versions of Babel, we recommend explicitly setting the core-js version you are using via the `corejs` option.

大致意思是,如果我们使用了 useBuiltIns 选项,建议配置 corejs 选项,如果不配置,默认提供 2.x 版本的 corejs

Warning2

代码语言:javascript
复制
When setting `useBuiltIns: 'usage'`, polyfills are automatically imported when needed.
Please remove the `import '@babel/polyfill'` call or use `useBuiltIns: 'entry'` instead.

这个警告是,让我们移除 import '@babel/polyfill ,polyfill 会被自动按需导入加载。所以我们在设置useBuiltIns'usage' 时,不需要手动引入 @babel/polyfill

这里的编译结果,不但对 ES+ 的新增语法进行了转译,而且对类和类的类的扩展属性也进行了转译,结果是比较符合我们期待的,能够直接运行在浏览器上。这里我们看不到 import "@babel/polyfill"; 它被拆成小模块,按需引入。

配置四
代码语言:javascript
复制
module.exports = {
  presets: [
    ['@babel/preset-env', {
      useBuiltIns: 'entry',
    }]
  ],
};

实验结果:

代码语言:javascript
复制
"use strict";

require("core-js/modules/es6.array.copy-within");

require("core-js/modules/es6.array.fill");

require("core-js/modules/es6.array.find");

## 中间还有 200+ 个包省略。。。。。。

require("core-js/modules/web.immediate");

require("core-js/modules/web.dom.iterable");

require("regenerator-runtime/runtime");

var cat = 'May';

var arrow = function arrow() {
  return "Name: ".concat(cat).padStart(2);
};

var promise = new Promise();
var map = new Map();

这里同样也得到了同配置四中的 warning1,需要我们配置 corejs 选项,但没有 warning2,同时这里把 import "@babel/polyfill" 拆成小包,全量引入。

我们综合一下配置二三四,分别对 useBuiltIns 的三个可选 option 分别进行了实验,得出了如下结论

  • false:不处理 polyfill
  • ‘usage’:按需加载 polyfill,且不需要手动引入@babel/polyfill 文件
  • ‘entry’:必须手动引入 @babel/polyfill 文件,会把 @babel/polyfill 切为小包,全量引入,但要注意的是,这里的全量并不是真的全量,因为我们没有配置目标浏览器,Babbel 默认转了全量的 ECMAScript 2015+,如果配置了如: targets: "chrome>60" ,会在配置四的编译结果中,包减少到 20+ ,也就是 ‘entry’ 会加载目标浏览器所需的 polyfill
配置五
代码语言:javascript
复制
module.exports = {
  presets: [
    ['@babel/preset-env', {
      useBuiltIns: 'usage',
      corejs: 3
    }]
  ],
};

实验结果:

代码语言:javascript
复制
"use strict";

require("core-js/modules/es.array.iterator");

require("core-js/modules/es.map");

require("core-js/modules/es.object.to-string");

require("core-js/modules/es.promise");

require("core-js/modules/es.string.iterator");

require("core-js/modules/es.string.pad-start");

require("core-js/modules/web.dom-collections.iterator");

var cat = 'May';

var arrow = function arrow() {
  return "Name: ".concat(cat).padStart(2);
};

var promise = new Promise();
var map = new Map();

为了解决配置三四中的warning1,我们手动手动设置了 corejs 选项,区别于默认值 2 ,我们设置了 3

配置四的编译结果相比,引用部分发生了变化,默认的 core-js:2 处理依赖是 require("core-js/modules/es6.map"); 这里的 core-js:3require("core-js/modules/es.map");

使用 core-js@3 的原因是,core-js@2 分支中已经不会再添加新特性,新特性都会添加到 core-js@3。例如你使用了 Array.prototype.flat(),如果你使用的是 core-js@2,那么其不包含此新特性。为了可以使用更多的新特性,建议大家使用 core-js@3

到这里好像一切近乎完美,但还有个问题没有处理,抽象和剥离。

配置六

我们改变样本文件:

代码语言:javascript
复制
const promise = new Promise();
class Cat {
  constructor(name){
    this.name= name
  }
  getName(){
    return
  }
}

配置:

代码语言:javascript
复制
module.exports = {
  presets: [
    ['@babel/preset-env', {
      useBuiltIns: 'usage',
      corejs: 3
    }]
  ],
};

实验结果:

代码语言:javascript
复制
"use strict";

require("core-js/modules/es.function.name");

require("core-js/modules/es.object.define-property");

require("core-js/modules/es.object.to-string");

require("core-js/modules/es.promise");

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

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var promise = new Promise();

var Cat =
/*#__PURE__*/
function () {
  function Cat(name) {
    _classCallCheck(this, Cat);

    this.name = name;
  }

  _createClass(Cat, [{
    key: "getName",
    value: function getName() {
      return;
    }
  }]);

  return Cat;
}();

这里出现了很多公共方法, _classCallCheck_createClass 等,我们统称为 Babel 的注入帮助程序。如果每个文件都这么注入,必然是巨大的浪费资源,这个时候,需要使用 @babel/plugin-transform-runtime

首先安装依赖,@babel/plugin-transform-runtime 通常仅在开发时使用,但是运行时最终代码需要依赖 @babel/runtime,所以 @babel/runtime 必须要作为生产依赖被安装,如下 :

代码语言:javascript
复制
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime

同时,它还会为代码创建一个沙箱环境,这在我们写类库或者工具库时是很有必要的,避免污染全局变量。

配置七
代码语言:javascript
复制
module.exports = {
  presets: [
    ['@babel/preset-env', {
      useBuiltIns: 'usage',
      corejs: 3
    }]
  ],
  plugins: [
    ['@babel/plugin-transform-runtime']
  ],
};

实验结果:

代码语言:javascript
复制
"use strict";

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

require("core-js/modules/es.function.name");

require("core-js/modules/es.object.to-string");

require("core-js/modules/es.promise");

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

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

var promise = new Promise();

var Cat =
/*#__PURE__*/
function () {
  function Cat(name) {
    (0, _classCallCheck2.default)(this, Cat);
    this.name = name;
  }

  (0, _createClass2.default)(Cat, [{
    key: "getName",
    value: function getName() {
      return;
    }
  }]);
  return Cat;
}();

我们发现,之前的帮助函数,是直接定义在文件内部的,如 :

代码语言:javascript
复制
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

现在变成:

代码语言:javascript
复制
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));

@babel/runtime 统一引入,减少了文件体积。并且对变量名做了处理,避免了全局污染,但同时又发现了新问题,编译后的文件,仅仅对 class 相关的函数做了变量名处理,但是对 Promise 相关的变量名并没有处理。

配置八
代码语言:javascript
复制
module.exports = {
  presets: ['@babel/preset-env'],
  plugins: [
    ['@babel/plugin-transform-runtime', {
      'corejs': 3
    }]
  ]
};

实验结果:

代码语言:javascript
复制
"use strict";

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

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

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

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

var promise = new _promise.default();

var Cat =
/*#__PURE__*/
function () {
  function Cat(name) {
    (0, _classCallCheck2.default)(this, Cat);
    this.name = name;
  }

  (0, _createClass2.default)(Cat, [{
    key: "getName",
    value: function getName() {
      return;
    }
  }]);
  return Cat;
}();

这样感觉就比较完美了,即实现了对 polyfill 的按需加载,对注入的帮助函数的统一抽象剥离,又实现了对变量的处理,避免污染全局作用域,感觉很香。

四、实验结果和思考

我们通过对 Babel 中基本使用的 @babel/preset-env@babel/plugin-transform-runtime 进行配置,测试了不同配置下的实验结果,得出了比较合适的实践,但出现了一个灵魂的思考,既然 @babel/plugin-transform-runtime 能实现按需加载,沙箱环境,公用函数的统一抽象,还要在 useBuiltIns 里面搞三个参数干啥。这里猜测是考虑到包的体积大小。在 Babel 7.4.0 之后的版本,Babel官方明确建议了不再使用 @babel/polyfill ,建议使用 core-js/stable( polyfill ECMAScript features)和 regenerator-runtime/runtime,(needed to use transpiled generator functions)。

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=780f3ys6oy21

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-01-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 写在前面的话
  • 一、实验目的
  • 二、实验环境和要求
    • 依赖包版本
      • 实验基础数据
      • 三、实验内容
        • 初始化准备
          • 配置一
            • 配置二
              • 配置三
                • 配置四
                  • 配置四
                    • 配置五
                      • 配置六
                        • 配置七
                          • 配置八
                          • 四、实验结果和思考
                          相关产品与服务
                          云开发 CloudBase
                          云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档