github: babel概括總結javascript
在前端的發展過程當中,javascript的兼容性,一直是前端頭痛的問題,在之前的一些有些項目中,爲解決瀏覽器兼容而花費的時間甚至還要多餘實際的業務邏輯開發時間,babel就是其中處理兼容的轉譯工具(或者叫平臺)。css
javascript在不斷髮展,新的提案標準每一年都會有,在獲得普遍普及以前,Babel 把用最新標準編寫的 JavaScript 代碼向下編譯成能夠在今天隨處可用的版本html
babel的編譯過程分爲3步,解析(parse),轉換(transform),生成(generate),對應的三個插件分別是Babylon
、babel-traverse
、babel-generator
。
babylon將源碼轉換爲抽象語法樹(AST);babel-traverse經過AST生成一個便於操做、轉換的path對象,供咱們的babel插件處理;babel-generator讀取AST並將其轉換爲代碼和源碼映射。這些過程不是本文的關注點,咱們關注的是結果,哪些插件與咱們的生產息息相關,咱們如何去使用babel的插件。前端
vue腳手架生成的項目在.babelrc
文件中的配置:vue
{ "presets": [ ["env", { "modules": false, "targets": { "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] } }], "stage-2" ], "plugins": ["transform-vue-jsx", "transform-runtime"] }
babel插件推崇的是功能的單一性,就是每一個插件的功能儘量的單一,好比我要使用es6的箭頭函數,那就能夠裝一個轉換插件npm i -D @babel/plugin-transform-arrow-functions
,將其寫進.babelrc
文件裏就好了:詳情java
{ "presets": [], "plugins": ["@babel/plugin-transform-arrow-functions"] }
這樣,咱們寫的:node
(a) => [...a]
會被該插件轉換爲:react
function (a) { return [...a] }
這個插件也只解決箭頭函數的轉換,就算函數內部用了其它新語法也無論,這個好處是很明顯的,就跟咱寫項目推崇組件的細膩度一個道理webpack
然而呢,js發展有點快,想一下那個es2015(es6)一下加了多少東西,咱們要使用還得一個一個的npm i -D xxx
,這個有點小麻煩,因此就能夠採用presets
配置項。npm i -D babel-preset-es2015
,而後配置.babelrc
。詳情git
爲了承接上文,這裏暫時先用babel6的寫法,babel7裏也能夠用babel-preset-es2015,可是文檔裏去掉了,es201五、es201六、es2017(2018年的東西直接寫在env裏了,7月份2019年的新標準就要來羅@_@)等都被放在env裏面了,之後這幾個preset會不會砍掉就不知道咯
{ "presets": ["es2015"], "plugins": [] }
這樣咱們就可使用包括箭頭函數在內的es6的新語法
而不用去擔憂兼容問題。這下這兩個的關係也就清晰了,presets
裏面配置的是一些plugins
集合
在babel 7.3.0
裏面,presets
-- 對應插件
有這些:
@babel/preset-env
@babel/preset-stage-0
@babel/preset-stage-1
@babel/preset-stage-2
@babel/preset-stage-3
@babel/preset-flow
@babel/preset-react
babel-preset-minify
@babel/preset-typescript
在presets配置裏面,咱們看到了:
["env", { "modules": false, "targets": { "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] } }]
這個env是@babel/preset-env
這個集合插件配置項,這裏的配置項:
modules
:"amd" | "umd" | "systemjs" | "commonjs" | "cjs" | "auto" | false, defaults to "auto".
target
:就是告訴babel你的js要兼容哪些環境,它會幫你將你寫的js轉譯成目標環境兼容的js語法,這個具體配置能夠看browserslist 那就是說,js不管用什麼新玩意,@babel/preset-env
都能跟我兼容到我想要的環境?帶着問題,咱們再看看官網的介紹,What is Babel:
再看看經過env轉換的幾個demo:
a => a // 轉爲 function (a) {return a} function func (b = false) {return false} // 轉爲 function func (b) { b = b || false return b }
比較明顯,上面的都屬於轉換語法
,因此應該說「js不管用什麼新語法
,@babel/preset-env
都能幫你兼容到目標環境」。
@babel/preset-env
具體能夠幫咱們轉化哪些呢?看這兒JavaScript新特性和Babel插件的映射關係,這個是@babel/preset-env集合插件所包含的插件列表,每一個插件對應轉換一個新特性,至於沒有的,好比promise、Array.from等,請往下看。
在上面的配置中,咱們看到env下面有個stage-2。stage-x,這裏麪包含的都是當年最新規範的草案,每一年更新。細分爲以下幾步
官網裏有一句話It is important to note that @babel/preset-env does not support stage-x plugins.
,就是說@babel/preset-env中不包含在草案階段的新屬性的轉換插件
其實咱們經過 plugin-features,以及 proposals/finished-proposals(其中2019就是今年的stage-4),能夠發現@babel/preset-env是包含了stage-4階段的plugins的。
好比寫react的同窗比較熟悉的decorators目前就處於stage-2階段,咱們要用這些處於草案階段的新屬性,能夠安裝npm i -D @babel/preset-stage-2
,而後在presets裏寫上stage-2,babel就會經過那些處於草案階段的新屬性的插件將咱們代碼中的用到的新屬性轉譯成爲es5
此外,低一級的 stage 會包含全部高級 stage 的內容,例如 stage-2 會包含 stage-2, stage-3 的全部內容。
Babel 幾乎能夠編譯全部時新的 JavaScript 語法,但對於 APIs 來講卻並不是如此。好比說:Promise
、WeakMap
、Array.from
、Object.assign
、Array.prototype.includes
、generator
等。爲了解決這個問題,咱們使用一種叫作 Polyfill(代碼填充,也可譯做兼容性補丁) 的技術。能讓你提早使用還不可用的 APIs。
引入它很簡單,咱們npm i -S @babel/polyfill
,
在vue中的入口文件main.js文件的最上面:
import "@babel/polyfill";
或者在webpack入口裏引入:
module.exports = { entry: ["@babel/polyfill", "./main.js"], };
二者任選其一
上面這兩種方式是將整個polyfill都引入了,不少代碼其實對咱們是沒有用的,好比,咱們的env配置的是不須要兼容ie9如下的瀏覽器,這種引入方式把全部兼容ie的代碼都引入了,包含ie8如下,因此,通常咱們會在.babelrc
文件裏的env裏配置下useBuiltIns
參數,這樣babel在引入的時候會根據咱們env環境去加載相應的polyfill:詳細
有以下三種方式
// .babelrc { ["env", { "modules": false, "targets": { "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] }, // 是否自動引入polyfill,開啓此選項必須保證已經安裝了babel-polyfill // 在這裏設置自動引入後,babel會根據你配置的兼容的環境,去按需加載polyfill的代碼,這樣能保證代碼量最少 // 參數:Boolean,默認爲false. "useBuiltIns": false }] } // webpack.base.conf.js module.exports = { entry: ["@babel/polyfill", "./main.js"], };
咱們看到上面的配置中有個transform-runtime
,這個是配置@babel/plugin-transform-runtime,它是作什麼的呢?官網說:一個插件,經過重複使用babel注入的助手(helper)代碼,來減小代碼體積
,咱們看看它是如何工做的。
npm i -D @babel/plugin-transform-runtime // .babelrc { "plugins": [ "@babel/plugin-transform-runtime", // 默認配置 { "absoluteRuntime": false, "corejs": false, "helpers": true, "regenerator": true, "useESModules": false } ] }
好比這個es6的class類:
class Person { }
在沒有使用transform-runtime時,每一個使用class
函數處,Babel 會生成class
的helper函數放置在文件頂部,就是說在多個文件裏使用了class
, babel就會在每一個文件裏面都生成相同的helper:
"use strict"; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) {throw new TypeError("Cannot call a class as a function"); } } var Person = function Person() { _classCallCheck(this, Person); };
這樣沒必要要的重複會使咱們的代碼體積很是雍腫,transform-runtime就是來解決這個重複生成helper的問題的,它會將這個es6的class
語法的helper
函數放在babel-runtime/helpers
裏,而後在使用處經過require引入,這樣就不必在每一個使用處重複定義helper
了,達到了減小代碼體積的效果。
"use strict"; var _classCallCheck2 = require("babel-runtime/helpers/classCallCheck"); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var Person = function Person() { (0, _classCallCheck3.default)(this, Person); };
@babel/runtime和@babel/polyfill這兩個模塊功能幾乎相同,就是轉碼新增 api
根據它們兩的特色,@babel/polyfil通常用於前端項目,@babel/runtime通常用來寫插件
Babel 的 CLI 是一種在命令行下使用 Babel 編譯文件的簡單方法。有時候咱們只是寫一個插件,須要用babel轉一下咱們代碼中的高階語法,由於項目可能不太大,用不到構建工具,就能夠用babel-cil。轉換依據咱們的.babelrc
文件或者package.json
中babel選項
編譯一個文件
babel my-file.js
若是咱們想要把一個目錄整個編譯成一個新的目錄,可使用 --out-dir 或者 -d。.
$ babel src --out-dir lib # 或 $ babel src -d lib
babel-loader是什麼呢?前面說了,咱們能夠經過babel-cil在命令行裏告訴babel轉譯哪些js,也能夠經過babel-register,在代碼裏經過require來轉,可是,如今前端開發是一個工程化過程,依賴關係比較複雜,在一個稍微大點兒的項目中還真無法手動告訴babel要處理哪些文件,好比一個.vue
文件,裏面還包含html、css,還有一些不是js的鬼語法,這時候就要藉助其它插件先提早處理下,因此,webpack根據依賴關係,加載文件的時候遇到js文件後,會將文件內容的js字符串根據loader配置的前後順序,挨個兒傳遞給它們處理,babel-loader就是其中之一
babel就是將目標環境(瀏覽器)經過打補丁升級成支持最新javascript語法的環境的工具。
// .babelrc { "presets": [ ["env", { // 這裏默認是false,不用再寫一遍 - // "modules": false, // 通常不單獨寫出來,babel/preset-env會自個讀取package裏面的browserslist,與css兼容環境保持一致 // https://github.com/browserslist/browserslist - // "targets": { - // "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] - // }, }], "stage-2" ], "plugins": ["transform-vue-jsx", "transform-runtime"] } // webpack.base.conf.js module.exports = { entry: ["@babel/polyfill", "./main.js"], };
名稱 | 做用 | 備註 |
---|---|---|
babel/cli | 容許命令行使用 babel 命令轉譯文件 | 通常在寫插件時使用 |
babel/polyfill | 爲全部 API 增長兼容方法 | 須要在全部代碼以前 require,且體積比較大 |
babel/plugin-transform-runtime | 把幫助類方法從每次使用前定義改成統一 require,精簡代碼 | --- |
babel/runtime | helper庫 | 須要安裝爲依賴,而不是開發依賴,node環境使用,web環境不須要 |
babel/loader | babel插件在webpack項目中的一個入口 | --- |
babel/core | babel的polyfill庫 | --- |
babel/preset-env | babel預製環境的集合插件,經過配置目標環境,轉換標準上的新特性 | 只轉新特性,不轉api |
babel/preset-stage-2 | 轉換草案stage-2以及stage-3階段的的新屬性 | --- |
參考: