博客:姜瑞濤的官方網站
原文鏈接:https://www.jiangruitao.com/babel/transform-runtime/
版權采用《署名-非商業性使用-禁止演繹 4.0 國際》許可協議 轉載需注明原文作者、鏈接與版權協議
本節主要講@babel/plugin-transform-runtime以及@babel/runtime。
在我們用Babel做語法轉換的時候(注意,這里是單純的做語法轉換,暫時不使用polyfill補齊API),需要Babel在轉換后的代碼里注入一些函數才能正常工作,先看一個例子。
github配套代碼是babel13的例子。
Babel配置文件如下,用@babel/preset-env做語法轉換:
{
"presets": [
"@babel/env"
],
"plugins": [
]
}
轉換前的代碼使用了ES6的class類語法:
class Person {
sayname() {
return 'name'
}
}
var john = new Person()
console.log(john)
Babel轉碼后生成的代碼如下:
"use strict";
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 Person = /*#__PURE__*/function () {
function Person() {
_classCallCheck(this, Person);
}
_createClass(Person, [{
key: "sayname",
value: function sayname() {
return 'name';
}
}]);
return Person;
}();
var john = new Person();
console.log(john);
可以看到轉換后的代碼上面增加了好幾個函數聲明,這就是注入的函數,我們稱之為輔助函數。@babel/preset-env在做語法轉換的時候,注入了這些函數聲明,以便語法轉換后使用。
但樣這做存在一個問題。在我們正常的前端工程開發的時候,少則幾十個js文件,多則上千個。如果每個文件里都使用了class類語法,那會導致每個轉換后的文件上部都會注入這些相同的函數聲明。這會導致我們用構建工具打包出來的包非常大。
那么怎么辦?一個思路就是,我們把這些函數聲明都放在一個npm包里,需要使用的時候直接從這個包里引入到我們的文件里。這樣即使上千個文件,也會從相同的包里引用這些函數。通過webpack這一類的構建工具打包的時候,我們只會把使用到的npm包里的函數引入一次,這樣就做到了復用,減少了體積。
@babel/runtime就是上面說的這個npm包,@babel/runtime把所有語法轉換會用到的輔助函數都集成在了一起。
我們先安裝這個包:
npm install --save @babel/runtime
npm install --save-dev @babel/cli @babel/core @babel/preset-env
然后到node_modules目錄下看一下這個包結構
_classCallCheck, _defineProperties與 _createClass這個三個輔助函數就在圖片所示的位置,我們直接引入即可。
github配套代碼是babel13a的例子。
我們手動把輔助函數替換掉函數聲明,之前文件的代碼就變成如下所示:
"use strict";
var _classCallCheck = require("@babel/runtime/helpers/classCallCheck");
var _defineProperties = require("@babel/runtime/helpers/defineProperties");
var _createClass = require("@babel/runtime/helpers/createClass");
var Person = /*#__PURE__*/function () {
function Person() {
_classCallCheck(this, Person);
}
_createClass(Person, [{
key: "sayname",
value: function sayname() {
return 'name';
}
}]);
return Person;
}();
var john = new Person();
console.log(john);
這樣就解決了代碼復用和最終文件體積大的問題。不過,這么多輔助函數要一個個記住並手動引入,平常人是做不到的,我也做不到。這個時候,Babel插件@babel/plugin-transform-runtime就來幫我們解決這個問題。
@babel/plugin-transform-runtime有三大作用,其中之一就是自動移除語法轉換后內聯的輔助函數(inline Babel helpers),使用@babel/runtime/helpers里的輔助函數來替代。這樣就減少了我們手動引入的麻煩。
github配套代碼是babel13b的例子。
現在我們除了安裝@babel/runtime包提供輔助函數模塊,還要安裝Babel插件@babel/plugin-transform-runtime來自動替換輔助函數:
npm install --save @babel/runtime
npm install --save-dev @babel/cli @babel/core @babel/preset-env @babel/plugin-transform-runtime
現在,我們的Babel配置文件如下:
{
"presets": [
"@babel/env"
],
"plugins": [
"@babel/plugin-transform-runtime"
]
}
轉換前a.js代碼:
class Person {
sayname() {
return 'name'
}
}
var john = new Person()
console.log(john)
執行"npx babel a.js -o b.js"命令后,轉換生成的b.js里代碼如下:
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var Person = /*#__PURE__*/function () {
function Person() {
(0, _classCallCheck2["default"])(this, Person);
}
(0, _createClass2["default"])(Person, [{
key: "sayname",
value: function sayname() {
return 'name';
}
}]);
return Person;
}();
var john = new Person();
console.log(john);
可以看到,它生成的代碼比我們完全手動引入@babel/runtime里的輔助函數更加優雅。實際前端開發的時候,我們除了安裝@babel/runtime這個包外,一定會安裝@babel/plugin-transform-runtime這個Babel插件包的。
下一節接着講@babel/plugin-transform-runtime的使用。
注:
1.每個轉換后的文件上部都會注入這些相同的函數聲明,那為何不用webpack一類的打包工具去掉重復的函數聲明,而是要單獨再引一個輔助函數包?
webpack在構建的時候,是基於模塊來做去重工作的。每一個函數聲明都是引用類型,在堆內存不同的空間存放,缺少唯一的地址來找到他們。所以webpack本身是做不到把每個文件的相同函數聲明去重的。因此我們需要單獨的輔助函數包,這樣webpack打包的時候會基於模塊來做去重工作。