babel: 把符合最新標准的js代碼向下編譯成現在隨處可用的版本,還支持語法擴展,如支持React用的JSX語法,還支持用於靜態類型檢查的流式語法(Flow Syntax)。
babel的一切都是簡單的插件,你也可以利用babel創建自己的插件。
babel自身被分解成了數個核心模塊,任何人都可以利用它們來創建下一代的js工具(圍繞着babel已涌現出了大規模和多樣化的生態系統)。
Babel 幾乎可以編譯所有時新的 JavaScript 語法,但對於 APIs 來說卻並非如此。如它能編譯箭頭函數語法,卻無法編譯Array.from()這個新的api。為了解決這個問題,我們使用一種叫做 Polyfill(代碼填充,也可譯作兼容性補丁) 的技術。 簡單地說,polyfill 即是在當前運行環境中用來復制尚不存在的原生 api 的代碼。 能讓你提前使用還不可用的 APIs,Array.from 就是一個例子。
js發展史:
1996年,網景瀏覽器,網景把javascript提交給 ECMA International(歐洲計算機制造商協會) 進行標准化,並最終確定出新的語言標准,它就是 ECMAScript。自此,ECMAScript 成為所有 JavaScript 實現的基礎,不過,現實中我們只用 ECMAScript 稱呼標准,平時都還是使用 JavaScript 來稱呼這個語言。
標准(Standard): 用於定義與其他事物區別的一套規則。
實現(Implementation): 某個標准的具體實施/真實實踐。
1996--2011年,相繼發布了ECMAScript3,(ECMAScipt4由於太過激進而被拋棄),ECMAScript5,但由於現實中的很多實現和標准大相徑庭,大部分開發者依然寫着ECMAScript3風格的代碼。
2012年,由於開始停止對舊版瀏覽器的支持,用ECMAScript5(ES5)編寫代碼變得可行。並且新的ES6標准開始啟動。
2015 年,負責制定 ECMAScript 規范草案的委員會 TC39 決定將定義新標准的制度改為一年一次(所以開始以年份來命名ECMAScript標准了),這意味着每個新特性一旦被批准就可以添加,而不像以往一樣,規范只有在整個草案完成,所有特性都沒問題后才能被定稿。因此,ES6在2015年6月份公布之前又被重命名為了 ECMAScript 2015(ES2015)
2016年,提議中的新的js語法ES2016,或稱為ES7。
2017年,提議中的新的js語法ES2017,或稱為ES8,如async, await。
js提案要被正式采納必須經過5(0-4)個階段,相繼通過各個階段,最終在階段4被標准正式采納。
.babelrc:
babel的配置文件,用來讓babel做你要它做的事情的配置文件,存放在項目的根目錄下,用來設置轉碼規則和插件。在 babel 6 里,執行 babel test.js,只會輸出原樣的文本,因為 babel 不再包含任何 transform 功能,babel 6 里把它們作為插件(plugin)分割出去,需要我們自己配置插件,.babelrc文件內容為:
{ "presets": [], // 轉碼規則 "plugins": [] // 插件 }
比如要使用es6的箭頭函數,必須配置"plugins": ["transform-es2015-arrow-functions"]。基本上 ES6/ES7 的各種功能,babel 都提供了相應的插件用於轉換,但如果我們要一個一個配置 – 那就太惱人了。所以 babel 還提供了一個方法:presets。我們不妨把 presets 理解為套餐,不同套餐有不同的插件組合,比如 ES2015 preset 里打包了所有用於轉換 ES2015 代碼的插件,React preset 則打包了轉換 react.js jsx 語法的插件。
轉碼規則可以根據需要按照:
# ES2015轉碼規則 $ npm install --save-dev babel-preset-es2015 //包含所有es2015的特性 # react轉碼規則 $ npm install --save-dev babel-preset-react # ES7不同階段語法提案的轉碼規則(共有4個階段),選裝一個 $ npm install --save-dev babel-preset-stage-0 //包含了async,await等 $ npm install --save-dev babel-preset-stage-1 $ npm install --save-dev babel-preset-stage-2 $ npm install --save-dev babel-preset-stage-3
然后,將這些規則加入.babelrc:
{ "presets": [ "es2015", "react", "stage-2" ], "plugins": [] }
以下所有Babel工具和模塊的使用,都必須先寫好.babelrc。也可以把內容寫進package.json文件里:
"babel": { "presets": [ "react", "node5", "stage-0" ], "plugins": [] },
babel-cli,babel-node:
全局按照babel-cli,就可以在命令行下使用Babel編譯文件了,如babel src -d lib (把src目錄整個編譯成一個新的目錄)或babel example.js -o compiled.js (把example.js編譯后的結果寫入到compiled.js)。但是不推薦全局安裝,導致項目產生了對環境的依賴,另一方面,這樣做也無法支持不同項目使用不同版本的Babel。一般在項目中安裝babel: npm install babel-cli --save-dev ,把命令寫在npm scripts里,如"scripts": {"build": "babel src -d lib"},然后就可以在終端里運行npm run build。
babel-node不用單獨安裝,而是隨babel-cli一起安裝。babel-node命令,它近似於 node,只不過它在運行代碼前會預先編譯 ES2015 的代碼。提供一個支持ES6的REPL環境。它支持Node的REPL環境的所有功能,而且可以直接運行ES6代碼或腳本文件。
把npm scripts改寫成"scripts": {"build": "babel-node scripts.js"},這樣script.js本身就不用做任何轉碼處理。
babel-register:
運行babel的另一個方法,只需要引入文件就可以運行 Babel,或許能更好地融入你的項目設置。babel-register模塊改寫require命令,為它加上一個鈎子。此后,每當使用require加載.js、.jsx、.es和.es6后綴名的文件,就會先用Babel進行轉碼。如創建一個register.js文件:
require("babel-register")
require("./index.js")
這樣做就可以把 Babel注冊到 Node 的模塊系統中並開始編譯其中 require 的所有文件。這樣執行命令node register.js就可以運行的就是編譯后的index.js文件了。需要注意的是,babel-register只會對require命令加載的文件轉碼,而不會對當前文件轉碼。另外,由於它是實時轉碼,所以只適合在開發環境使用。
babel-core:
以編程的方式來使用 Babel,如果某些代碼需要調用Babel的API進行轉碼,就要使用babel-core模塊。
var babel = require('babel-core'); // 字符串轉碼 babel.transform('code();', options); // => { code, map, ast } // 文件轉碼(異步) babel.transformFile('filename.js', options, function(err, result) { result; // => { code, map, ast } }); // 文件轉碼(同步) babel.transformFileSync('filename.js', options); // => { code, map, ast }
babel-polyfill:
Babel默認只轉換新的JavaScript句法(syntax),而不轉換新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局對象,以及一些定義在全局對象上的方法(比如Object.assign)都不會轉碼。如果想讓這些方法運行,必須使用babel-polyfill,為當前環境提供一個墊片。
要使用 Babel polyfill,首先用 npm 安裝它:npm install --save babel-polyfill,然后只需要在文件頂部導入 polyfill 就可以了:import 'babel-polyfill'或是在webpack.config.js中加入babel-polyfill到你的入口數組:entry:["babel-polyfill","./app/js"]
babel-runtime:
與 babel-polyfill 一樣,babel-runtime 的作用也是模擬 ES2015 環境。只不過,babel-polyfill 是針對全局環境的引入它的,babel-runtime 更像是分散的 polyfill 模塊,我們可以在自己的模塊里單獨引入,比如 require(‘babel-runtime/core-js/promise’) ,它們不會在全局環境添加未實現的方法,只是,這樣手動引用每個 polyfill 會非常低效。我們借助 Runtime transform 插件來自動化處理這一切。安裝:npm install --save-dev babel-plugin-transform-runtime npm install --save babel-runtime; 配置.babelrc: "plugins":["transform-runtime"],這樣Babel會把這樣的代碼:
class Foo {
method() {}
}
編譯成:
import _classCallCheck from "babel-runtime/helpers/classCallCheck"; import _createClass from "babel-runtime/helpers/createClass"; let Foo = function () { function Foo() { _classCallCheck(this, Foo); } _createClass(Foo, [{ key: "method", value: function method() {} }]); return Foo; }();
至於要用 babel-polyfill 還是 babel-runtime,則需要根據具體需求。舉個例子,如果一個庫里引用了 babel-polyfill,別人的庫也引用了 babel-polyfill,我們很可能會跑兩個 babel-polyfill 實例,這里,使用 babel-runtime 會更合適。
手動指定插件:
你可以安裝一些別的插件,配置.babelrc的plugins,這樣能讓你對正在使用的轉換器進行更細致的控制。如babel-plugin-transform-es2015-classes插件。插件列表可參考babel官方網址。
插件選項:很多插件也有選項用於配置他們自身的行為。 例如,很多轉換器都有“寬松”模式,通過放棄一些標准中的行為來生成更簡化且性能更好的代碼。如"plugins":[ ["transform-es2015-classed", {"loose":true}] ]。
基於環境自定義babel:
Babel 插件解決許多不同的問題。 其中大多數是開發工具,可以幫助你調試代碼或是與工具集成。 也有大量的插件用於在生產環境中優化你的代碼。因此,想要基於環境來配置 Babel 是很常見的。你可以輕松的使用 .babelrc 文件來達成目的。
{ "presets": [...], "plugins": [...], "env": { "development": { "plugins": [...] }, "production": { "plugins": [...] } } }
Babel 將根據當前環境來開啟 env 下的配置。當前環境可以使用 process.env.BABEL_ENV 來獲得。 如果 BABEL_ENV 不可用,將會替換成 NODE_ENV,並且如果后者也沒有設置,那么缺省值是"development"。
手動指定插件?插件選項?環境特定設置?所有這些配置都會在你的項目里產生大量的重復工作。為此可以創建自己的預設並發布,使用時直接安裝就可以了,比如 babel-preset-airbnb,
Babel和其他工具:
以eslint為例,eslint是最流行的語法檢查工具之一。首先安裝 eslint 和 babel-eslint,然后創建或使用項目現有的 .eslintrc 文件並設置 parser 為 babel-eslint,現在添加一個 lint 任務到 npm 的 package.json 腳本中就可以了,"scripts": {"lint": "eslint my-files.js"}。
webpack中定義babel-loader:
webpack.config.js中的loaders配置參考:
loaders: [ { test: /\.jsx?$/, loader: 'babel-loader', include: [ path.resolve(__dirname, '../src'), ], query: { babelrc: false, presets: [ 'react', 'es2015', 'stage-0', ], plugins: [ 'transform-runtime' ] } } ]
