Babel 是 Web 開發者工具箱中令人驚艷的一員。它是一個很棒的工具,已經存在很長一段時間,但現在幾乎每個 JavaScript 開發者都依賴它,這種情況還會持續,因為 Babel 現在不可或缺,並為所有人解決了一個大問題。

本文介紹的是 Babel 7,目前的穩定版本。

Babel 簡介

Babel 是一個很棒的工具,已經存在很長一段時間,但現在幾乎每個 JavaScript 開發者都依賴它,這種情況還會持續,因為 Babel 現在不可或缺,並為所有人解決了一個大問題。

哪個問題?

這是每個 Web 開發者肯定都遇到過的問題:JavaScript 的某個特性在最新版本的瀏覽器中可用,但在舊版本中卻不行。或者 Chrome 或 Firefox 實現了它,但 Safari iOS 和 Edge 卻沒有。

例如,ES6 引入了箭頭函數

[1, 2, 3].map((n) => n + 1)

這在現代瀏覽器中都有支持。但 IE11 不支持它,也不支持 Opera Mini(怎么知道的?通過檢查 ES6 兼容性表)。

那麼你應該如何解決這個問題呢?你是應該放棄那些舊的/不兼容的瀏覽器,還是應該編寫舊的 JavaScript 代碼,讓所有用戶都開心?

這時候,Babel 登場了。Babel 是一個編譯器:它接收一種標準的代碼,將其轉換為另一種標準的代碼。

你可以配置 Babel 將現代 ES2017 JavaScript 轉換為 JavaScript ES5 語法:

[1, 2, 3].map(function(n) {
    return n + 1
})

這必須在構建時進行,所以你必須設置一個能夠處理這一過程的工作流。Webpack 是一個常見的解決方案。

(註:如果你對於這個 ES 的東西感到困惑,可以在 ECMAScript 指南 中了解更多有關 ES 版本的信息)

安裝 Babel

使用 npm,在項目中可以輕鬆安裝 Babel:

npm install --save-dev @babel/core @babel/cli

過去我推薦安裝全局的 babel-cli,但這現在已被 Babel 的維護者不鼓勵使用,因為在每個項目中使用此工具可以在每個項目中擁有不同的 Babel 版本,並且將 Babel 持久化到你的代碼庫更有利於團隊合作。

由於 npm 現在配備了 npx,在項目文件夾中可以直接輸入命令來運行本地安裝的 CLI 包:

現在我們只需這樣運行 Babel:

npx babel script.js

一個示例 Babel 配置

Babel 默認情況下不會達到任何有用的效果,需要對其進行配置並添加插件。

這裡是 Babel 插件列表

為了解決我們在介紹中提到的問題(在每個瀏覽器中使用箭頭函數),我們可以運行

npm install --save-dev \
 @babel/plugin-transform-arrow-functions

將該包下載到我們應用程序的 node_modules 文件夾中,然後我們需要將

{
    "plugins": ["@babel/plugin-transform-arrow-functions"]
}

添加到應用程序根文件夾中的 .babelrc 文件。如果你還沒有該文件,只需創建一個空文件,將內容添加進去。

提示:如果你從未見過以點開頭的文件(以點開頭的文件表示隱藏文件),一開始可能會感到奇怪,因為在你的文件管理器中可能看不到該文件。

現在,如果我們有一個 script.js 文件,其內容如下:

var a = () => {};
var a = (b) => b;

const double = [1,2,3].map((num) => num * 2);
console.log(double); // [2,4,6]

var bob = {
    _name: "Bob",
    _friends: ["Sally", "Tom"],
    printFriends() {
        this._friends.forEach(f =>
            console.log(this._name + " knows " + f));
    }
};
console.log(bob.printFriends());

運行 babel script.js 將輸出以下代碼:

var a = function () {};var a = function (b) {
    return b;
};

const double = [1, 2, 3].map(function (num) {
    return num * 2;
});console.log(double); // [2,4,6]

var bob = {
    _name: "Bob",
    _friends: ["Sally", "Tom"],
    printFriends() {
        var _this = this;

        this._friends.forEach(function (f) {
            return console.log(_this._name + " knows " + f);
        });
    }
};
console.log(bob.printFriends());

你可以看到,箭頭函數都已經被轉換為 JavaScript ES5 函數。

Babel 預設

如前一節所述,Babel 可以配置來轉譯特定的 JavaScript 功能。

你可以添加更多插件,但無法逐個功能地添加到配置中,這是不實際的。

這就是 Babel 提供預設的原因。

最受歡迎的預設是 envreact

提示:Babel 7 廢棄(並移除了)類似 preset-es2017 和 stage 預設,使用 @babel/preset-env 替代。

env 預設

env 預設非常好用:你告訴它你要支持的環境,它會為你做好一切,支持所有的現代 JavaScript 功能

例如:“支持每個瀏覽器的最新的 2 個版本,但是對於 Safari,讓我們支持從 Safari 7 開始的所有版本”。

{
    "presets": [
        ["env", {
            "targets": {
                "browsers": ["last 2 versions", "safari >= 7"]
            }
        }]
    ]
}

或者,“我不需要瀏覽器支持,只需讓我使用 Node.js 6.10”。

{
    "presets": [
        ["env", {
            "targets": {
                "node": "6.10"
            }
        }]
    ]
}

react 預設

react 預設非常方便,可以用於編寫 React 應用:添加 preset-flowsyntax-jsxtransform-react-jsxtransform-react-display-name

通過添加這個預設,你可以立即開始開發 React 應用,帶有 JSX 轉換和 Flow 支持。

更多關於預設的資訊

https://babeljs.io/docs/plugins/

使用 Babel 與 webpack

如果你想在瀏覽器中運行現代 JavaScript,僅僅使用 Babel 是不夠的,你還需要將代碼打包。Webpack 是這方面的完美工具。

提示:如果你對 webpack 不熟悉,請閱讀 webpack 指南

現代 JS 需要兩個不同的階段:編譯階段和運行階段。這是因為一些 ES6+ 功能需要填充或運行時助手。

要安裝 Babel 的填充運行時功能,運行以下命令:

npm install @babel/polyfill \
 @babel/runtime \
 @babel/plugin-transform-runtime

現在,在你的 webpack.config.js 文件中添加以下內容:

entry: [
    'babel-polyfill',
    // 你的應用腳本應該放在這裡
],

module: {
    loaders: [
        // Babel loader 將 ES2015 編譯成 ES5,以實現完全的跨瀏覽器支持
        {
            loader: 'babel-loader',
            test: /\.js$/,
            // 只包括在 `src` 子目錄中存在的文件
            include: [path.resolve(__dirname, "src")],
            // 排除 node_modules,與上一行代碼等效
            exclude: /node_modules/,
            query: {
                // 使用默認的 ES2015 預設,包含所有 ES2015 功能
                presets: ['es2015'],
                plugins: ['transform-runtime']
            }
        }
    ]
}

通過將預設和插件信息放入 webpack.config.js 文件中,我們可以避免使用 .babelrc 文件。