Babel 是一個 JavaScript 編譯器,它能夠將ES6+語法編譯爲瀏覽器支持的ES5語法。要學好babel
必須先理解相關的概念,可是你剛起步就去扣這些細節的話,極可能由於babel
一些複雜而模糊的概念打擊你的信息。因此咱們先從最簡單的開始,而後深刻。git
接下來咱們嘗試按照官方文檔作一個最簡單的例子github
安裝babel-cli
npm
npm install --save-dev babel-cli
添加babel編譯命令api
"build": "babel src -d lib"
添加.babelrc
配置文件瀏覽器
{ "presets": [], "plugins": [] }
添加JS文件babel
// /src/main.js const fetch = args => args; console.log(Promise);
執行 npm run build
,咱們發現編譯後的文件沒有發生任何的改變,就是簡單的原樣輸出app
// /lib/main.js const fetch = args => args; console.log(Promise);
爲何呢?這是由於在.babelrc
文件沒有作任何的配置。咱們一共使用了三個新特性,分別是常量申明const
,箭頭函數=>
,新全局變量Promise
。接下來我門嘗試將他們編譯爲ES5代碼。async
如上圖所示,藉助plugin
能夠實現咱們的目標。接下來咱們分別實現它:函數
const
原文連接 文檔中說明:工具
babel-plugin-check-es2015-constants
這個插件僅僅是驗證const
變量的規則,好比不能重複申明,不可變等特性。
把它編譯爲ES5代碼須要藉助babel-plugin-transform-es2015-block-scoping
咱們設置須要編譯的代碼爲:
const fetch = args => args; fetch = 55; // 故意寫錯 console.log(Promise);
安裝上面所需的兩個plugin
並改寫.babelrc
配置:
{ "presets": [], "plugins": [ "check-es2015-constants", "transform-es2015-block-scoping" ] }
編譯過程報錯驗證check-es2015-constants
生效
咱們修改須要編譯的代碼爲:
const fetch = args => args; console.log(Promise);
再次編譯成功
var fetch = args => args; console.log(Promise);
到這裏咱們已經成功的使用插件對const
的語法和轉譯(Syntax
和 transform
)ES5化。是否是很開心呢? 接下來咱們處理箭頭函數=>
。
arrow functions
一樣安裝對應的plugin
:
npm install --save-dev babel-plugin-transform-es2015-arrow-functions
這裏延用上面須要compile的源代碼:
var fetch = args => args; console.log(Promise);
改寫.babelrc
配置文件:
{ "presets": [], "plugins": [ "check-es2015-constants", "transform-es2015-block-scoping", "transform-es2015-arrow-functions" ] }
編譯結果:
var fetch = function (args) { return args; }; console.log(Promise);
編譯成功,unbelievable, we did it!!!
,是否是真的很簡單?先別得意???,還有一個Promise
沒有解決呢。有驚喜喲!!!
Promise
我有一個問題:我特地將上面的兩個例子放在一塊兒,是由於他們都屬於對新語法Syntax
進行編譯,而Promise
是做爲一個全局api
的存在,在大部分瀏覽器中是不存在這個全局api
的,若是讓你來解決這個問題,你會怎麼作?
你的答案:是的,沒錯!就是在不支持Promise
的環境中實現Promise
,在babel
中被稱爲polyfill
,注意它和shim
的區別哦~
babel-polyfill
文檔最簡單的使用:
npm install --save-dev babel-polyfill
Use it by requiring it at the top of the entry point to your application or in your bundler config. ---在入口文件的最頂層做用域直接引入
到這裏咱們已經完成既定的3個目標,可是你有沒有想過,隨着咱們ES6新特性的增長,plugin
的長度也逐漸增長,能夠碰見會有多長?,並且本身去找這些對應的plugin
也是比較麻煩的。有沒有傻瓜式的集成方法呢?您接着往下看。
爲了方便,Babel團隊將一些Plugins
集合在一塊兒,並稱之爲preset
。因此一個preset
是一系列plugin
的總和。按照年份劃分:
ES2015
/ ES-2016
/ES2017
等等,還延伸了stage
的概念,詳情請查閱官網。
babel-preset-env
A Babel preset that compiles ES2015+ down to ES5 by automatically determining the Babel plugins and polyfills you need based on your targeted browser or runtime environments.
不須要指定任何的plugin
和polyfill
,Babel preset 能夠將ES6+的新語法向下編譯爲ES5代碼,按照你指定的運行環境。具體的配置項請看官網。Without any configuration options, babel-preset-env behaves exactly the same as babel-preset-latest (or babel-preset-es2015, babel-preset-es2016, and babel-preset-es2017 together).
不須要作任何配置選項,它和babel-preset-latest
表現一致,或者說和babel-preset-es2015
,babel-preset-es2016
,babel-preset-es2017
4個preset總和一致。
感受是否是很牛逼!下面咱們來嘗試一個例子。爲防止有任何的代碼污染,我卸載全部npm package
最初的樣子:
{ "name": "babelrc", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "babel src -d lib" }, "author": "", "license": "ISC", "devDependencies": {} }
我安裝時發現@babel/preset-env
還處於bate
版本,使用時還有一些問題,因此決定仍是使用老版本。
(1)安裝babel-preset-env
上圖爲babel-preset-env
的一些依賴,主要爲一些helper
和plugin
。
爲了驗證出babel-preset-env
的是否知足要求,我新增了幾行包括ES6的代碼:
const fetch = args => args; console.log(Promise); // 新增一些代碼 class G { } let [a, b, c] = [1, 2, 3]; (async () => { await console.log(1) })();
(2)配置.babelrc
{ "presets": ["env"], "plugins": [] }
(3)編譯結果
"use strict"; function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var fetch = function fetch(args) { return args; }; console.log(Promise); // 新增一些代碼 var G = function G() { _classCallCheck(this, G); }; var a = 1, b = 2, c = 3; _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() { return regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: _context.next = 2; return console.log(1); case 2: case "end": return _context.stop(); } } }, _callee, undefined); }))();
我就問還有誰???全部的新語法特性都被成功處理了。
babel爲源代碼非實例方法(好比Object.assign
)和 babel-runtime/helps
下的工具函數自動引用了polyfill。這樣能夠避免全局空間的污染,很是適合用於JS庫和工具包的實現。可是實例方法(好比someString.includes("target")
)仍是須要使用babel-polyfill
。
若是你是新手,你可能沒有注意到,babel編譯時會在每一個文件生成一些須要幫助函數,若是文件比較多,那麼這些重複的代碼會增長編譯後的代碼體積。下面是一個例子:
源代碼
class G { }
.babelrc
配置
{ "presets": ["env"], "plugins": [] }
編譯代碼
"use strict"; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var G = function G() { _classCallCheck(this, G); }; // _classCallCheck就是一個內部生成的幫助函數
爲了優化編譯體積,babel
團隊推出了 babel-plugin-transform-runtime
+ babel-runtime
來解決這個問題。先看一下直觀體驗:
源代碼
class G { }
.babelrc
配置
{ "presets": ["env"], "plugins": ["transform-runtime"] }
編譯後的代碼:
"use strict"; var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck"); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var G = function G() { (0, _classCallCheck3.default)(this, G); };
經過對比能夠看出,第二種方案直接從babel-runtime
引入babel-runtime/helpers/classCallCheck
,避免本身定義,從而減小代碼的體積。
除了這個優勢意外
babel-runtime
三個主要部分:core.js
+ helpers
+ regenerator
固然除了上面的方法,經過按需引入 polyfills
和 transforms
更能帶來更多的體積優化。
減小對沒必要要瀏覽器的兼容來減小 polyfills
和 transforms
的引入:
{ "presets": [ ["env", { "targets": { // The % refers to the global coverage of users from browserslist "browsers": [ ">0.25%", "not ie 11", "not op_mini all"] } }] ] }
(1)可能你發現了我並無安裝babel-cli
,爲何我能使用babel
命令?
由於我在全局安裝了babel
。
(2) browserslistrc 配置更改以後 babel編譯後的代碼怎麼沒有改變。或者說 browserslistrc 影響babel編譯的具體表現是怎麼樣的?
暫時我也不知道,我正在研究,後面更新。
[1] browserslist
[2] browserslist-example
[3] browserslist-queries
因爲本人表達能力真的不好,表達不夠清楚還望你們多多包涵。