史上最簡單的Babel入門教程
在學習 Babel
之前,只知道它是一個 JS
編譯器,大概的功能就是幫助我們在舊的瀏覽器環境中將 ES6+
的代碼轉換爲兼容低版本的 JS
代碼。但是,它是通過什麼實現的,具體是怎樣實現的就沒有深入瞭解了。
廢話不多說,直接進入主題。本文通過一個案例來打開 Babel
這個技能,所以先要準備一個小項目。
mkdir babel-basic && cd babel-basic
npm init -y
mkdir src && cd src
touch index.js
一頓操作以後,我們新建的項目目錄爲:
/babel-basic
|- /src
|- index.js
|- package.json
然而,package.json
是最原始的配置,而 index.js
暫時沒有代碼:
{
"name": "babel-basic",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {}
}
@babel/core
學習 Babel
,首先要了解一個叫 @babel/core
的東西,它是 Babel
的核心模塊,所以首先要安裝:
npm i --save-dev @babel/core
安裝成功之後就可以在我們的代碼中使用了, 你可以採用CommonJS
的引用方式:
const babel = require('@babel/core');
babel.transform("code", options);
這裏的知識點有很多, 不過你不用急於的掌握它, 只需要知道它是Babel
的核心, 讓我們接着往下看。
@babel/cli
這是一個終端運行工具,內置的插件,運行你從終端使用 babel
的工具。同樣也需要安裝:
npm i --save-dev @babel/cli
接着,在 src/index.js
中寫一段簡單代碼:
const fn = () => 1; // 箭頭函數, 返回值爲1
console.log(fn());
用法一:命令行的形式
babel src --out-dir lib
這行代碼的意思是:它使用我們設置的解析方式來解釋 src
目錄下的所有 js
文件,並將轉換後的每個文件都輸出到 lib
目錄下。
但是,到目前爲止我們並沒有設置任何的解析方式,所以在執行這行代碼後,能看到項目中多了一個 lib
目錄,裏面的代碼和 src
目錄中的是一致的。至於如何設置解析方式,就涉及到後面講的 plugins
和 presets
了。
另外,還可以通過 npx babel src --out-dir lib
來代替上面的命令,兩者的結果是一致的。
用法二:給 package.json
中配置命令:
{
"name": "babel-basic",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
+ "build": "babel src -d lib"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
+ "@babel/cli": "^7.8.4",
+ "@babel/core": "^7.8.4"
}
}
運行 npm run build
效果也是一致的。上面的 -d
是 --out--dir
的縮寫。
plugins
plugins
的本質是一個 js
程序,指示着 Babel
如何轉換代碼,如何解析代碼。
案例:
在上面的例子中,我們在 index.js
中寫了一個箭頭函數,然而箭頭函數在低版本瀏覽器是不兼容的,所以得進行代碼轉換,將箭頭函數轉換爲普通的函數,這裏就涉及到怎麼設置 babel
的代碼解析方式了。
官方提供了一個插件,用於將箭頭函數轉換爲普通的函數:
npm i --save-dev @babel/plugin-transform-arrow-functions
安裝完成後就可以通過命令行的方式來運行:
npx babel src --out-dir lib --plugins=@babel/plugin-transform-arrow-functions
結果在 lib
目錄中的代碼被轉換了:
const fn = function () {
return 1;
}; // 箭頭函數, 返回值爲1
console.log(fn());
雖然已經實現了箭頭函數的轉換功能,但是其他的 ES6+
語法還是不能進行轉換的,這是因爲我們使用的這個插件僅僅支持函數轉換。那麼問題來了,ES6+
有那麼多語法,如果一個瀏覽器不支持 ES6+
,那麼我們不就需要安裝很多這樣的插件嗎??
Presets
上面問題的答案顯然是否定的,Presets
正是用於解決這種問題的。它是一組插件的集合,可以使用官方的,也可以自己自定義。
@babel/preset-env
這是一個將 ES6+
轉換爲低版本代碼的插件集合。
安裝:
npm i --save-dev @babel/preset-env
比如:我們使用 ES7
的乘方運算符和函數參數支持尾部逗號:
const fn = () => 1; // ES6箭頭函數, 返回值爲1
let num = 3 ** 2; // ES7求冪運算符
let foo = function(a, b, c, ) { // ES7參數支持尾部逗號
console.log('a:', a)
console.log('b:', b)
console.log('c:', c)
}
foo(1, 3, 4)
console.log(fn());
console.log(num);
命令行運行:
npx babel src --out-dir lib --presets=@babel/preset-env
結果,在 lib
目錄下的代碼進行了轉換:
"use strict";
var fn = function fn() {
return 1;
}; // 箭頭函數, 返回值爲1
var num = Math.pow(3, 2);
var foo = function foo(a, b, c) {
console.log('a:', a);
console.log('b:', b);
console.log('c:', c);
};
foo(1, 3, 4);
console.log(fn());
console.log(num);
好了,到目前爲止我們瞭解了 @babel/core
、@babel/cli
、plugins
、presets
,但是你會發現一個問題,我們一直使用命令行的方式來執行代碼,這顯然不是簡潔的方式,所以,接下來我們需要將這些複雜的命令做成配置項。
配置
在項目中創建一個 babel.config.js
文件:
const presets = [
[
"@babel/env",
{
targets: {
edge: "17",
chrome: "64",
firefox: "60",
safari: "11.1"
}
}
]
]
module.exports = { presets };
加上這個配置的作用是:
- 使用了
env preset
env preset
只會爲目標瀏覽器中沒有的功能加載轉換插件
現在,我們只需要使用命令 npm run build
這個命令就可以進行代碼轉換了。然而,你會好奇 Babel
是如何找到這個配置文件的呢?答案是默認情況下它會去項目的根目錄找一個名爲 babel.config.js
的文件(或者 babelrc.js
)作爲配置文件。
如何非得改這個配置文件的名稱,那麼我們的命令就需要帶上參數了:
{
"scripts": {
"build": "babel src -d lib --config-file ./babel.config.js"
}
}
需要注意的還有一句話:只會爲目標瀏覽器中沒有的功能加載轉換插件。比如,上面的配置中,edge:"17"
,意思就是轉換之後的代碼支持到 edge17
,如果代碼在 edge17
中本已支持,則不進行代碼轉換。
所以,你會發現執行 npm run build
命令後,lib
中的代碼沒有進行轉換。這是因爲那些目標瀏覽器本來就支持這些語法,如果將 edge:"17"
改爲 edge:"10"
,重新運行,結果就會轉換了。
Polyfill
在上面提到,plugins
是插件,比如將箭頭函數轉換爲普通函數的插件:@babel/plugin-transform-arrow-functions
。
presets
是一組插件的集合,比如上面使用的 env preset
。
然而,polyfill
是對執行環境或其他功能的補充。
比如,在 edge10
中使用 ES7
的語法 includes()
,但是我們知道這個版本的瀏覽器環境是不支持這個方法的,如果強行使用並不能達到預期效果。
polyfill
這是解決這個問題的,如果環境不支持,那麼就幫你引用一個支持這種語法的環境!!
示例:
// 原來的代碼
var hasTwo = [1, 2, 3].includes(2);
// 加了polyfill之後的代碼
require("core-js/modules/es7.array.includes");
require("core-js/modules/es6.string.includes");
var hasTwo = [1, 2, 3].includes(2);
嘿嘿,到這裏知道 polyfill
的用處了吧。
@babel/polyfill
這是一個用於模擬 ES6+
環境的 polyfill
,也就是說,它支持 ES6+
的語法。
安裝:
npm i --save @babel/polyfill
但是,由於我們使用的是 env preset
,這個配置中有一個 useBuiltIns
的選項,將此設置爲 "usage"
就只包括你需要的 polyfill
:
const presets = [
[
"@babel/env",
{
targets: {
edge: "17",
chrome: "64",
firefox: "67",
safari: '11.1'
},
+ useBuiltIns: "usage"
}
]
]
module.exports = { presets }
安裝配置完成後,Babel
將檢查你的所有代碼,然後查詢目標環境中缺少的功能,並引入僅包含所需的 polyfill
示例:
在前面的例子中,我們使用一下 edge17
沒有的 Promise.prototype.finally
:
const fn = () => 1; // ES6箭頭函數, 返回值爲1
let num = 3 ** 2; // ES7求冪運算符
let hasTwo = [1, 2, 3].includes(2)
let foo = function(a, b, c, ) { // ES7參數支持尾部逗號
console.log('a:', a)
console.log('b:', b)
console.log('c:', c)
}
foo(1, 3, 4)
Promise.resolve().finally();//edge17不支持
console.log(fn());
console.log(num);
console.log(hasTwo);
執行 npm run build
,結果代碼沒有被轉換,而是 polyfill
幫我們引入了一個環境:
"use strict";
require("core-js/modules/es7.promise.finally");
const fn = () => 1; // ES6箭頭函數, 返回值爲1
let num = 3 ** 2; // ES7求冪運算符
let hasTwo = [1, 2, 3].includes(2);
let foo = function foo(a, b, c) {
// ES7參數支持尾部逗號
console.log('a:', a);
console.log('b:', b);
console.log('c:', c);
};
foo(1, 3, 4);
Promise.resolve().finally();
console.log(fn());
console.log(num);
console.log(hasTwo);
最後,你會發現這是一個被遺棄的 @babel/polyfill
,而是推薦你使用 core-js@3
+ @babel/preset-env
。
解決辦法:
安裝 core-js@3
:
npm i --save core-js@3
添加 corejs
選項:
const presets = [
[
"@babel/env",
{
targets: {
edge: "17",
chrome: "64",
firefox: "67",
safari: '11.1'
},
useBuiltIns: "usage",
+ corejs: 3
}
]
]
module.exports = { presets }
最後,npm run build
你就會發現警告沒有了,代碼也是正確的。