【轉】babel那些事兒
從前,一提到新東西,我的反應就是相容性好不好,如果不能滿足產品經理的需求,就還是用保守的方式實現吧。畢竟前端開發是一件很靈活的事,怎麼寫都行,至於為何會用某種方法,一定是綜合考慮相容性,效能,使用者體驗,開發成本等因素後再說。相容性和新事物有時就像魚和熊掌不可兼得,必須權衡利弊,做一個決斷。但是ECMAScript 2015不一定要等所有瀏覽器都支援後才可以用,於是你會想到一個工具babel,它可以助ECMAScript 2015一臂之力,讓處於某個stage的且尚未納入es規範的特性提前支援,因為babel能夠把不同stage的es,轉成瀏覽器識別的js。
在學babel前,我有一個這樣的疑問,為啥說到babel,一般會提到es5+?他倆有啥關係?babel是伴隨ECMAScript 2015出現的嗎?如果不是,它比ECMAScript 2015出生早,還是晚呢?
看了babel的官方文件後,我恍然大悟,原來babel不是TC39的作品啊!!!如果說ECMAScript的爹孃是TC39,那麼babel的爹是james kyle,一個澳大利亞工程師,曾就職於Facebook。總之,babel和ECMAScript 2015沒有什麼血緣關係,babel也不是ECMAScript 2015出生的附屬品,babel5.0釋出的比ECMAScript 2015早。
一、babel版本有哪些?
2015年1月12日,6to5和esnext開源;
2015年2月15日,6to5重新命名為babel;
2015年3月31日,babel 5.0釋出;
2015年10月30日,babel 6.0釋出。
二、babel的用途
babel是一個JavaScript編譯器。
1、ES6轉成ES5
預設情況下,babel自帶了一組ES2015語法轉化器,這些轉化器能把ES2015轉為ES5,讓你現在就使用最新的JavaScript語法,而不用等待瀏覽器提供支援。在babel中,預設啟用stage-2及以上的proposal。如果有人說“不同stage的babel”,這種說法是錯誤的,因為babel沒有所謂的stage,babel就是一個編譯器。至於babel的配置檔案引數中為什麼會牽扯到stage,是由於babel要知道把哪個stage的es5+轉換成瀏覽器能識別的js,而TC39 Process會告訴babel是哪個stage。
2、jsx檔案轉成js檔案
babel可以把React的JSX語法轉成普通JavaScript語法。
3、babel由外掛組成,可組合拼裝。
4、babel支援source map,幫助除錯。
三、babel的工作原理
1、解析
用babylon對原始碼(比如ES6)進行詞法解析,得到AST;
2、轉換
用babel-traverse對AST樹進行遍歷轉換,得到新的AST樹;
3、生成
用babel-generator通過AST樹生成目的碼(比如ES5)。
四、babel的配置
babel的配置檔案是.babelrc,存放在專案的根目錄下。使用babel的第一步,就是配置這個檔案。什麼是.babelrc檔案呢?熟悉linux的同學一定知道,rc結尾的檔案通常代表執行時自動載入的檔案,配置等等,類似bashrc,zshrc。同樣babelrc在這裡也是有同樣的作用,而且在babel6中,這個檔案必不可少。
該檔案用來設定轉碼規則和外掛,基本格式如下。
{ "presets": [], "plugins": [] }
1、轉碼規則
presets欄位設定轉碼規則,轉碼規則可以告訴babel去處理什麼語法。
A、babel-preset-es2015
這是ES2015(最新版本的JavaScript標準,也叫做ES6)的轉碼規則。使用它後,babel可以將ES6語法轉碼為普通 JavaScript(即ES5)語法。
# 安裝ES2015轉碼規則 $ npm install --save-dev babel-preset-es2015
B、babel-preset-react
這是react的轉碼規則。使用它後,babel可以將react語法轉碼為普通JavaScript語法。
# 安裝react轉碼規則 $ npm install --save-dev babel-preset-react
C、babel-preset-stage-x
這是ES7不同階段語法提案的轉碼規則。使用它後,babel可以將ES7不同階段語法轉碼為普通JavaScript語法。
# 安裝ES7不同階段語法提案的轉碼規則(共有4個階段),選裝一個 $ npm install --save-dev babel-preset-stage-0 $ npm install --save-dev babel-preset-stage-1 $ npm install --save-dev babel-preset-stage-2 $ npm install --save-dev babel-preset-stage-3
以上每種預設都依賴於緊隨的後期階段預設。例如,babel-preset-stage-1依賴babel-preset-stage-2,後者又依賴babel-preset-stage-3。
最終,在.babelrc檔案這樣寫。
{ "presets": [ "es2015", "stage-0" ], "plugins": [] }
D、babel-preset-env
這是最新的預設外掛,包含es2015,es2016,es2017和latest等,不包含react,polyfill。
$ npm install --save-dev babel-preset-env
最終,在.babelrc檔案這樣寫,是不是比上面簡潔了很多?
{ "presets": ["env"], "plugins": [] }
如果與webpack一起使用,需要再配置下webpack.config.js檔案。
module: { rules: [ { test: /\.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader' } } ] }
2、外掛
A、babel-plugin-transform-runtime
babel-polyfill可以控制你自己寫的檔案,但是不能很好的處理第三方library,因為polyfill會汙染原來的全域性環境,容易發生衝突,這時候babel-runtime閃電登場。
babel-runtime是一個提供了regenerator、core-js和helpers的執行時庫。
babel-plugin-transform-runtime外掛依賴babel-runtime,babel-runtime是真正提供runtime環境的包,也就是說transform-runtime外掛是把js程式碼中使用到的新原生物件和靜態方法轉換成對runtime實現包的引用。
a.1、利弊
可以用babel-runtime/core-js匯出的物件和方法替代ES6引入的新原生物件和靜態方法,避免全域性汙染;
可以用babel-runtime/regenerator匯出的函式取代generators或async函式;
可以把多餘程式碼提取到babel-runtime/helps中,減少體積。
由於runtime不會汙染全域性空間,所以例項方法是無法工作的,這就需要polyfill啦。
a.2、安裝
# babel-plugin-transform-runtime用於開發環境 $ npm install --save-dev babel-plugin-transform-runtime # babel-runtime用於生產環境 $ npm install --save babel-runtime
a.3、配置.babelrc檔案
{ "plugins": [ ["transform-runtime", { "helpers": false, "polyfill": false, "regenerator": true, "moduleName": "babel-runtime" }] ] }
a.4、使用
// ES6程式碼 var sym = Symbol(); // 通過transform-runtime轉換後的ES5+runtime程式碼 var _symbol = require("babel-runtime/core-js/symbol"); var sym = (0, _symbol.default)();
五、babel-core,新語法轉碼
1、安裝
$ npm install --save-dev babel-core
2、使用
var babel = require("babel-core"); A、如果是字串形式的JavaScript程式碼,可以使用transform編譯。 babel.transform("code();", options); // { code, map, ast } B、如果是檔案,非同步編譯使用transformFile。 babel.transformFile("filename.js", options, function(err, result) { result; // { code, map, ast } }); C、如果是檔案,同步編譯使用transformFileSync。 babel.transformFileSync("filename.js", options); // { code, map, ast } D、要是已經有一個babel AST(抽象語法樹)了就可以直接從AST進行轉換。 babel.transformFromAst(ast, code, options); // { code, map, ast }
六、babel-polyfill,新Api轉碼
babel預設只轉換新的JavaScript句法,而不轉換新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全域性物件,以及一些定義在全域性物件上的方法(比如Object.assign)都不會轉碼。babel-polyfill正好可以轉碼新的API,polyfill和runtime都是對core-js和regenerator的再封裝,方便使用。
插播一段,一直好奇polyfill是個啥東西,簡單查了下,程式碼填充,也可譯作相容性補丁,類似軟墊片。
1、安裝
$ npm install --save-dev babel-polyfill
2、使用
//方案A,在your.js中引入 import 'babel-polyfill'; //方案B,在node.js中引入 require('babel-polyfill'); //方案C,設定webpack.config.js module.exports = { entry: ["babel-polyfill", "./app/js"] };
七、babel-cli,命令列轉碼
1、安裝
$ npm install --save-dev babel-cli
2、命令列
# 轉碼結果輸出到標準輸出 $ babel example.js # 轉碼結果寫入一個檔案 # --out-file 或 -o 引數指定輸出檔案 $ babel example.js --out-file compiled.js # 或者 $ babel example.js -o compiled.js # 整個目錄轉碼 # --out-dir 或 -d 引數指定輸出目錄 $ babel src --out-dir lib # 或者 $ babel src -d lib # -s 引數生成source map檔案 $ babel src -d lib -s
3、命令列優化
可以像上面一樣直接輸入命令完成轉碼,也可以修改package.json檔案,然後執行npm run build。
"scripts": { "build": "babel src -d lib" }
八、babel-register,require實時轉碼
通過繫結node.js的require來自動轉譯require引用的js程式碼檔案。
babel-register模組改寫require命令,為它加上一個鉤子。此後,每當使用require載入.js、.jsx、.es和.es6字尾名的檔案,就會先用babel進行轉碼。
1、安裝
$ npm install --save-dev babel-register
2、使用
//必須先載入babel-register require("babel-register"); require("./index.js");
3、利弊
這種方式不需要手動對index.js轉碼。
babel-register只會對require命令載入的檔案轉碼,而不會對當前檔案轉碼。另外,由於它是實時轉碼,所以只適合在開發環境使用。
九、sublime外掛轉碼es6
1、安裝外掛
在sublime中,安裝babel外掛。
2、全域性安裝babel-cli包
cnpm install -g babel-cli
3、配置sublime中的babel外掛
preferences -> Package Settings -> Babel -> Settings-Default,開啟檔案,按照下面的引數配置。
{ "debug": false, "use_local_babel": true, "node_modules": { "windows": "C:/Users/Administrator/AppData/Roaming/npm/node_modules/babel-cli/node_modules", "linux": "/usr/lib/node_modules", "osx": "/usr/local/lib/node_modules" }, "options": {} }
4、配置.babelrc檔案
在當前目錄配置好.babelrc檔案,定義轉碼規則,在當前目錄放置好依賴包。
{ "presets": ["es2015"], "plugins": [] }
5、執行轉換
寫一個es6.js檔案,按下快捷鍵Ctrl+Shift+P,輸入babel,選擇babel transform執行,會自動生成一個匿名檔案。很詭異的是,這個檔案自動儲存了,但不是在當前目錄,也不是在sublime的包目錄裡,具體在哪,我也不清楚,我只知道這不是我想要的結果。我希望能夠在儲存es6.js檔案時,自動生成期望目錄的es5.js檔案,而且名稱一致,檔案實際存在,不是無名檔案。後面找到優化方案,再回來補充。
tools -> Babel -> Babel Transform,也可以執行轉換。
十、線上轉碼
babel提供一個repl線上編譯器,可以線上將ES6程式碼轉為ES5程式碼。轉換後的程式碼,可以直接作為ES5程式碼插入網頁執行。
十一、總結
看了上面的介紹,你會用babel了嗎?如果還有點迷惑,搞不清東南西北,看這裡就對啦。
1、宣告轉換規則
babel的配置檔案.babelrc列舉了一些要用到的東西。比如babel要用env,就把env寫進去,宣告要處理哪些語法。
2、進行轉換操作
幕後高人則是babel-core,babel-polyfill或者babel-cli,他們負責把原始檔轉換成目標檔案。
安裝了babel-core和babel-polyfill,並在你的原始檔中引用,頁面載入的時候,就會自動實現轉換。或者安裝babel-cli,在命令列輸入命令,手動轉換。