何在前端JavaScript開發中使用ES新特性?babel是什么?.babelrc文件又是干什么用的?文件中的presets和plugins又是什么鬼?本文將解答上述疑問。
babel是一個JavaScript編譯器
ES規范今年來更新較為頻繁,近幾個版本推出很多新的特性,而用戶的瀏覽器版本眾多,很多用戶用的是老版本瀏覽器,老版本瀏覽器不支持ES新特(nodejs也存在同樣問題,參見),babel就是用來讓你可以使用ES新特性,又可以使代碼運行在老版本瀏覽器上。
Babel 通過語法轉換器支持最新版本的 JavaScript 。 語法轉換插件允許你立刻使用新語法,無需等待瀏覽器支持。
解析-->轉換-->生成代碼
1.在構建工具中(以webpack為例)使用:
// 在webpack.config.js中增加babel-loader,如下所示 module: { rules: [ { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" } ] }
2.在babel-cli中使用
npm install -g babel-cli babel src -d lib // 將src中的代碼轉譯并將生成的文件放到lib目錄下 復制代碼
babel的配置是要告訴babel工具使用哪些插件轉譯代碼,主要作用于babel工作的第二階段(轉譯)
babel工作前會從.babelrc文件讀取配置,根據配置對JavaScript進行轉譯。
下面以.babelrc文件的配置方式為例介紹,配置項主要有兩個,presets,plugins;
{ "presets": ["env"], "plugins": ["babel-plugin-transform-object-assign"] }
plugins配置具體插件,而對于ES新特性需要配置的插件多達幾十個,如果一一配置就太麻煩來,于是babel給我們提供一些插件集,插件集配置在presets配置項中,目前官方提供的插件集有:
其中 env(babel-preset-env)相當于 es2015 ,es2016 ,es2017 及最新版本。
4. stage-X
Stage-x preset 中的任何轉換都是對未被批準為 JavaScript 版本一部分的語言的變化(如 es6 / es2015 ),其分為以下5各階段:
Stage 0 - 稻草人: 只是一個想法,可能是 babel 插件。 Stage 1 - 提案: 初步嘗試。 Stage 2 - 初稿: 完成初步規范。 Stage 3 - 候選: 完成規范和瀏覽器初步實現。 Stage 4 - 完成: 將被添加到下一年度發布。
上述配置的插件和插件集也是需要通過npm安裝的
npm install babel-preset-env --save-dev npm install babel-plugin-transform-object-assign --save-dev
babel presets(轉換插件集)配置
"presets": ["env"] // 默認將運行所有transfrom和集成所有的polyfill
也可以僅僅配置項目所支持瀏覽器所需的polyfill和transform。只編譯所需的代碼會使你的代碼包更小。如下所示,該轉換只支持每個瀏覽器最后兩個版本和safari大于等于7的版本所需的polyfill和代碼轉換。
、前言
雖然在 JavaScript 中對象無處不在,但這門語言并不使用經典的基于類的繼承方式,而是依賴原型,至少在 ES6 之前是這樣的。當時,假設我們要定義一個可以設置 id 與坐標的類,我們會這樣寫:
// Shape 類 function Shape(id, x, y) { this.id = id; this.setLocation(x, y); } // 設置坐標的原型方法 Shape.prototype.setLocation = function(x, y) { this.x = x; this.y = y; };
上面是類定義,下面是用于設置坐標的原型方法。從 ECMAScript 2015 開始,語法糖 class被引入,開發者可以通過 class 關鍵字來定義類。我們可以直接定義類、在類中寫靜態方法或繼承類等。上例便可改寫為:
class Shape { constructor(id, x, y) { // 構造函數語法糖 this.id = id; this.setLocation(x, y); } setLocation(x, y) { // 原型方法 this.x = x; this.y = y; } }
一個更符合“傳統語言”的寫法。語法糖寫法的優勢在于當類中充滿各類靜態方法與繼承關系時,class 這種對象模版寫法的簡潔性會更加突出,且不易出錯。但不可否認時至今日,我們還需要為某些用戶兼容我們的 ES6+ 代碼,class 就是 TodoList 上的一項:
作為當下最流行的 JavaScript 編譯器,Babel 替我們轉譯 ECMAScript 語法,而我們不用再擔心如何進行向后兼容。
本地安裝 Babel 或者利用 Babel CLI 工具,看看我們的 Shape 類會有哪些變化。可惜的是,你會發現代碼體積由現在的219字節激增到2.1KB,即便算上代碼壓縮(未混淆)代碼也有1.1KB。轉譯后輸出的代碼長這樣:
"use strict";var _createClass=function(){function a(a,b){for(var c,d=0;d<b.length;d++)c=b[d],c.enumerable=c.enumerable||!1,c.configurable=!0,"value"in c&&(c.writable=!0),Object.defineProperty(a,c.key,c)}return function(b,c,d){return c&&a(b.prototype,c),d&&a(b,d),b}}();function _classCallCheck(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}var Shape=function(){function a(b,c,d){_classCallCheck(this,a),this.id=b,this.setLocation(c,d)}return _createClass(a,[{key:"setLocation",value:function c(a,b){this.x=a,this.y=b}}]),a}();
Babel 僅僅是把我們定義的 Shape 還原成一個 ES5 函數與對應的原型方法么?
一、揭秘
好像沒那么簡單,為了摸清實際轉譯流程,我們先將上述類定義代碼簡化為一個只有14字節的空類:
class Shape {}
首先,當訪問器走到類聲明階段,需要補充嚴格模式:
"use strict"; class Shape {}
而進入變量聲明與標識符階段時則需補充 let 關鍵字并轉為 var:
"use strict"; var Shape = class Shape {};
到這個時候代碼的變化都不太大。接下來是進入函數表達式階段,多出來幾行函數:
"use strict"; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Shape = function Shape() { _classCallCheck(this, Shape); };
該階段不僅替換了 class,還在類中調用了叫做 _classCallCheck 的方法。這是什么呢?
這個函數的作用在于確保構造方法永遠不會作為函數被調用,它會評估函數的上下文是否為 Shape 對象的實例,以此確定是否需要拋出異常。接下來,則輪到 babel-plugin-minify-simplify上場,這個插件做的事情在于通過簡化語句為表達式、并使表達式盡可能統一來精簡代碼。運行后的輸出是這樣的:
"use strict"; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function"); } var Shape = function Shape() { _classCallCheck(this, Shape); };
可以看到 if 語句中由于只有一行代碼,于是花括號被去掉。接下來上場的便是內置的 Block Hoist ,該插件通過遍歷參數排序然后替換,Babel 輸出結果為:
"use strict"; function _classCallCheck(a, b) { if (!(a instanceof b)) throw new TypeError("Cannot call a class as a function"); } var Shape = function a() { _classCallCheck(this, a); };
最后一步,minify 一下,代碼體積由最初的14字節增為338字節:
"use strict";function _classCallCheck(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}var Shape=function a(){_classCallCheck(this,a)};
二、再說一些
這是一個什么都沒干的類聲明,但現實中任何類都會有自己的方法,而此時 Babel 必定會引入更多的插件來幫助它完成代碼的轉譯工作。直接在剛剛的空類中定義一個方法吧。
class Shape { render() { console.log("Hi"); } }
我們用 Babel 轉譯一下,會發現代碼中包含如下這段:
var _createClass = function () { function a(a, b) { for (var c, d = 0; d < b.length; d++) c = b[d], c.enumerable = c.enumerable || !1, c.configurable = !0, "value" in c && (c.writable = !0), Object.defineProperty(a, c.key, c); } return function (b, c, d) { return c && a(b.prototype, c), d && a(b, d), b; }; }();
類似前面我們遇到的 _classCallCheck ,這里又多出一個 _createClass ,這是做什么的呢?我們稍微把代碼狀態往前挪一挪,來到 babel-plugin-minify-builtins 處理階段(該插件的作用在于縮減內置對象代碼體積,但我們主要關注點在于這個階段的 _createClass 函數是基本可讀的),此時 _classCallCheck 長成這樣:
var _createClass = 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); } } return function(Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; } ();
可以看出 _createClass 用于處理創建對象屬性,函數支持傳入構造函數與需定義的鍵值對屬性數組。函數判斷傳入的參數(普通方法/靜態方法)是否為空對應到不同的處理流程上。而 defineProperties 方法做的事情便是遍歷傳入的屬性數組,然后分別調用 Object.defineProperty 以更新構造函數。而在 Shape 中,由于我們定義的不是靜態方法,我們便這樣調用:
_createClass(Shape, [{ key: "render", value: function render() { console.log("Hi"); } }]);
T.J. Crowder 在 How does Babel.js create compile a class declaration into ES2015? 中談到 Babel 是如何將 class 轉化為 ES5 兼容代碼時談到了幾點,大意為:
這些概述大致總結了類定義在兩個 ES 版本中的一些差異,其他很多方面比如 extends ——繼承關鍵字,它的使用則會使 Babel 在轉譯結果加上 _inherits 與 _possibleConstructorReturn兩個函數。篇幅所限,此處不再展開詳述。
三、最后
語法糖 class 給我們帶來了很多寫法上的便利,但可能會使我們在代碼體積上的優化努力“付諸東流”。
另一方面,如果你是一名 React 應用開發者,你是否已經在想將代碼中的所有 class 寫法換為 function 呢?那樣做的話,代碼體積無疑會減少很多,但你一定也知道 PureComponent 相比 Component 的好處。所以雖然 function 給你的代碼體積減負了,但他在哪里又給你無形增加負擔了呢?
因此,真的不推薦開發者用 class 這種寫法么,你覺得呢?
融界2024年3月30日消息,據國家知識產權局公告,上海聯影醫療科技股份有限公司申請一項名為“網頁轉譯方法、裝置、應用商店系統、設備和介質“,公開號CN117786256A,申請日期為2023年12月。
專利摘要顯示,本申請涉及一種網頁轉譯方法、裝置、應用商店系統、設備和介質,包括:獲取html頁面數據;其中,html頁面數據包含文本節點的樣式標簽、圖片存儲路徑、文本和圖片的分組信息;根據文本和圖片的分組信息,圖形在用戶界面的窗體中生成容器控件;根據文本節點的樣式標簽,生成文本樣式列表,并將文本樣式列表和相應文本加入對應容器控件;根據圖片存儲路徑獲取圖片,并將圖片加入對應容器控件;執行容器控件,并在圖形用戶界面的窗體中以WPF格式呈現容器控件的執行結果。通過在圖形用戶界面的窗體生成容器控件,使WPF端中文本和圖像的位置準確且格式一致,解決了網頁端內容轉換為WPF格式后,WPF端呈現內容還原度低的問題。
本文源自金融界
*請認真填寫需求信息,我們會在24小時內與您取得聯系。