因babel的版本從5升級到6有不少改動,好比babel自己再也不提供任何transform的工做,都須要藉助插件來完成,本文的全部討論都是創建在babel 6之上的。若是隻想看結論,直接跳到文章最後。 ---寫在前面javascript
ES6即ECMAScript 6,是前端開發的JS最新規範,如今你們的開發都在使用ES6,對此並不陌生了。只是瀏覽器對ES6的支持並不完整,想要更好讓ES6在各個平臺完美運行還需一番折騰,特此一敘。下圖是ES6的瀏覽器兼容性一覽表(已ES6的Number爲例):前端
另外國內瀏覽器都號稱是「雙核」,實際上瀏覽器的版本號較新,打包的Webkit內核確很低,好比市場份額很高的360安全瀏覽器,最新版本是9.1,Webkit內核版本纔是55.0.2883,要知道Chrome官方最新版本已經是62.0.3202,相差之遠不甚理解。java
固然,搜狗瀏覽器市場份額也不低,官方最新版本是7.1,內核版本是49.0.2623,爲之一驚。node
所以,咱們想要在全部的瀏覽器上平穩、完美的運行ES6代碼,必需要了解它的忠實伴侶 Babel。webpack
你們可能對此也並不陌生,可是對babel的內部機制、插件的做用、兼容的配置還缺少一些認識,本文就是特地嘗試來補全這些內容。git
使用babel無非要用到.babelrc文件或者在package.json增長babel字段。咱們以.babelrc文件爲例:github
{ "presets":["es2015","stage-0"], "plugins": ["transform-runtime"] }
這是最多見的babel配置,而後結合webpack下的babel-loader完成對JS代碼的babel編譯。web
上面代碼的presets和plugins分別是什麼含義呢?若是是下面的配置有何不可呢?shell
{ "presets":["es2015","stage-0"] }
首先來明確一個概念: presets是一系列plugin的集合。好比上述配置中es2015表示babel-preset-es2015,它包含如下plugin:npm
使用presets的好處就是不用再plugins配置裏一個一個的寫了。
而後,咱們經過對代碼的編譯來看下上面兩個配置的區別。源碼以下:
let a=1; let b=(item)=>{return item+1}; let c='1'.padStart(2,'0'); let d=Object.assign({k:2},{t:4}); let e=new Set();
咱們使用第二種配置,獲得的編譯結果以下:
"use strict"; var a = 1; var b = function b(item) { return item + 1; }; var c = '1'.padStart(2, '0'); var d = Object.assign({ k: 2 }, { t: 4 }); var e = new Set();
從編譯結果來看,let、箭頭函數都被編譯了,然而padStart和Object.assign原樣輸出了。緣由很簡單,let被編譯是使用了es2015中的transform-es2015-block-scoping,箭頭函數編譯是使用了es2015的transform-es2015-arrow-functions。padStart、Object.assign和Set並未在es2015和state-0中找到對應plugin。咱們再使用第一種配置編譯,結果以下:
'use strict'; var _set = require('babel-runtime/core-js/set'); var _set2 = _interopRequireDefault(_set); var _assign = require('babel-runtime/core-js/object/assign'); var _assign2 = _interopRequireDefault(_assign); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var a = 1; var b = function b(item) { return item + 1; }; var c = '1'.padStart(2, '0'); var d = (0, _assign2.default)({ k: 2 }, { t: 4 }); var e = new _set2.default();
從編譯結果看,let、箭頭函數、Object.assign、Set都被正確編譯,padStart仍巋然不動。在這友情提醒一下,babel-plugin-transform-runtime是依賴babel-runtime的。那麼如何讓padStart方法也能被成功編譯呢,這麼大的開場白終於聊到了咱們今天的主題:babel的polyfill方案。
官方推薦的方式是使用babel-polyfill。
This will emulate a full ES2015+ environment and is intended to be used in an application rather than a library/tool. This polyfill is automatically loaded when using babel-node.
This means you can use new built-ins like Promise or WeakMap, static methods like Array.from or Object.assign, instance methods like Array.prototype.includes, and generator functions (provided you use the regenerator plugin). The polyfill adds to the global scope as well as native prototypes like String in order to do this.
用最簡單的方式歸納官方的說法就是:只要引入了babel-polyfill你能夠大膽的用ES6。基本方法以下:
先安裝
npm install --save babel-polyfill
後使用
// 在代碼中顯示調用 require("babel-polyfill"); // or import "babel-polyfill";
或者在webpack中配置
module.exports = { entry: ["babel-polyfill", "./index.js"] };
那麼問題來了,既然使用這麼簡單,有啥弊端沒?這個問題的答案也很簡單:它無疑大大增長了代碼的體積,即便你只有1k的代碼,也會打包出幾百k出來。若是不在乎這個代碼的體積,肆意大膽的去用吧。若是想作到代碼清爽、合理的利用babel的功能還請繼續閱讀。
使用babel-polyfill能夠不使用presets和transform-runtime,可是不意味着presets和transform-runtime沒有用武之地。在此總結了幾個原則:
純業務開發
第2、放棄使用preset-es201五、preset-state-0,請使用preset-env。
babel-polyfill的引入會自動加入不少代碼,有時候咱們並不徹底須要。好比:作移動端開發不須要考慮IE之類的、B端產品線只考慮指定的瀏覽器等。這個時候使用prest-env就能夠了。配置以下:
{ "presets": [ ["env", { "targets": { "browsers": ["last 2 versions", "safari >= 7"] } }] ] }
browsers的可選值參考 browserslist;有幾個點要提醒一下:
類庫和工具開發
第1、儘可能避免使用babel-polyfill而使用tranform-runtime
If you are looking for something that won't modify globals to be used in a tool/library, checkout the transform-runtime plugin. This means you won't be able to use the instance methods mentioned above like Array.prototype.includes.
官方這句話換個角度講就是類庫的開發建議使用transform-runtime,它的原則是不改變原型鏈上的方法,可是經過babel-runtime或者core-js手動引入,這樣不只代碼優雅編譯的包體積會小不少。
第2、選擇性的使用ES6語法
ES6雖然很強大,可是不少方法使用ES5仍可輕鬆實現。看下剛纔說起的 Array.prototype.includes方法:
// ES6寫法 let a=[1,2,3]; if(a.includes(1)){ console.log('1 is finded'); }
// ES5寫法 let a=[1,2,3]; if(a.some((item)=>{return item===1})){ console.log('1 is finded'); }
這兩個寫法區別並不大,可是若是使用ES6的寫法再加上polyfill的引入,代碼要多很多。
請老老實實使用babel-polyfill。