Babel-loader: настройка транспиляции

babel-loader — загрузчик для Webpack, предназначенный для интеграции Babel в процесс сборки проекта. Его основная задача — преобразование современного JavaScript-кода в версию, совместимую с более старыми браузерами и окружениями.

Babel выполняет транспиляцию:

  • преобразует синтаксис ES6+;
  • добавляет поддержку экспериментальных возможностей;
  • компилирует JSX;
  • обрабатывает TypeScript;
  • внедряет полифилы;
  • удаляет несовместимые конструкции.

Webpack сам по себе не умеет преобразовывать современный JavaScript. Он только собирает модули. Именно babel-loader связывает систему сборки с Babel и делает транспиляцию частью пайплайна обработки файлов.


Установка babel-loader

Для базовой конфигурации требуется установить:

npm install --save-dev babel-loader @babel/core @babel/preset-env

Описание пакетов:

Пакет Назначение
babel-loader интеграция Babel с Webpack
@babel/core ядро Babel
@babel/preset-env автоматическая транспиляция под нужные браузеры

Базовая настройка Webpack

Простейшая конфигурация:

const path = require('path');

module.exports = {
    mode: 'development',

    entry: './src/index.js',

    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    },

    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader'
                }
            }
        ]
    }
};

Здесь:

  • test определяет тип файлов;
  • exclude исключает обработку зависимостей;
  • babel-loader запускает Babel для каждого JS-файла.

Конфигурация Babel

Настройки Babel могут храниться:

  • в .babelrc;
  • в babel.config.js;
  • внутри package.json;
  • непосредственно в параметрах загрузчика.

Наиболее распространённый вариант:

{
    "presets": ["@babel/preset-env"]
}

Файл .babelrc:

{
    "presets": [
        "@babel/preset-env"
    ]
}

Как работает preset-env

@babel/preset-env анализирует:

  • целевые браузеры;
  • используемый синтаксис;
  • уровень поддержки возможностей JavaScript.

После этого Babel преобразует только действительно несовместимые конструкции.

Пример исходного кода:

const sum = (a, b) => a + b;

После транспиляции:

"use strict";

var sum = function sum(a, b) {
    return a + b;
};

Настройка browserslist

Babel ориентируется на список поддерживаемых браузеров.

Конфигурация в package.json:

{
    "browserslist": [
        "> 0.25%",
        "not dead"
    ]
}

Пример более строгой поддержки:

{
    "browserslist": [
        "last 2 versions",
        "ie 11"
    ]
}

Чем старее браузеры в списке, тем больше преобразований выполняет Babel.


Использование targets

Альтернативный способ — настройка внутри Babel:

{
    "presets": [
        [
            "@babel/preset-env",
            {
                "targets": {
                    "chrome": "90",
                    "firefox": "88"
                }
            }
        ]
    ]
}

Пример для Node.js:

{
    "presets": [
        [
            "@babel/preset-env",
            {
                "targets": {
                    "node": "18"
                }
            }
        ]
    ]
}

Автоматическое подключение полифилов

Транспиляция синтаксиса не добавляет отсутствующие API.

Например:

Promise
Map
Set
Array.from

Для поддержки таких возможностей используются полифилы.

Установка:

npm install core-js regenerator-runtime

Конфигурация:

{
    "presets": [
        [
            "@babel/preset-env",
            {
                "useBuiltIns": "usage",
                "corejs": 3
            }
        ]
    ]
}

Режимы useBuiltIns

Режим Описание
false полифилы не подключаются
entry подключаются вручную
usage Babel добавляет только используемые полифилы

Режим entry

Входной файл:

import "core-js/stable";
import "regenerator-runtime/runtime";

Конфигурация:

{
    "presets": [
        [
            "@babel/preset-env",
            {
                "useBuiltIns": "entry",
                "corejs": 3
            }
        ]
    ]
}

Режим usage

Babel анализирует код и автоматически внедряет только необходимые полифилы.

Исходный код:

const arr = Array.from(document.querySelectorAll('div'));

После сборки Babel может автоматически подключить:

import "core-js/modules/es.array.from.js";

Это уменьшает размер итогового бандла.


Исключение node_modules

Обычно директория зависимостей не транспилируется:

exclude: /node_modules/

Причины:

  • высокая нагрузка на сборку;
  • увеличение времени компиляции;
  • большинство библиотек уже поставляются в совместимом виде.

Однако некоторые пакеты публикуются в ES6-формате. Тогда требуется частичная транспиляция.


Транспиляция отдельных пакетов

Пример:

{
    test: /\.js$/,
    exclude: /node_modules\/(?!modern-lib)/,
    use: 'babel-loader'
}

Здесь:

  • все зависимости исключаются;
  • modern-lib всё же проходит через Babel.

Использование include

Иногда include удобнее:

const path = require('path');

module.exports = {
    module: {
        rules: [
            {
                test: /\.js$/,
                include: [
                    path.resolve(__dirname, 'src')
                ],
                use: 'babel-loader'
            }
        ]
    }
};

Настройка cacheDirectory

Транспиляция может быть медленной. babel-loader поддерживает файловый кэш.

Пример:

{
    loader: 'babel-loader',
    options: {
        cacheDirectory: true
    }
}

Преимущества:

  • ускорение повторных сборок;
  • снижение нагрузки на CPU;
  • ускорение HMR.

Настройка source maps

Для корректной отладки:

module.exports = {
    devtool: 'source-map'
};

Babel автоматически сохраняет информацию об исходных строках.


Использование Babel plugins

Плагины расширяют возможности Babel.

Установка:

npm install --save-dev @babel/plugin-proposal-optional-chaining

Конфигурация:

{
    "plugins": [
        "@babel/plugin-proposal-optional-chaining"
    ]
}

Optional chaining

Исходный код:

const city = user?.address?.city;

После транспиляции:

var city = user === null || user === void 0
    ? void 0
    : user.address;

Nullish coalescing

Установка:

npm install --save-dev @babel/plugin-proposal-nullish-coalescing-operator

Конфигурация:

{
    "plugins": [
        "@babel/plugin-proposal-nullish-coalescing-operator"
    ]
}

Пример:

const name = value ?? 'default';

Использование preset-react

Для React используется JSX-транспиляция.

Установка:

npm install --save-dev @babel/preset-react

Конфигурация:

{
    "presets": [
        "@babel/preset-env",
        "@babel/preset-react"
    ]
}

Пример JSX:

const element = <h1>Hello</h1>;

После Babel:

const element = React.createElement("h1", null, "Hello");

Использование preset-typescript

Babel умеет удалять TypeScript-аннотации.

Установка:

npm install --save-dev @babel/preset-typescript

Конфигурация:

{
    "presets": [
        "@babel/preset-typescript"
    ]
}

Webpack:

{
    test: /\.ts$/,
    use: 'babel-loader'
}

Отличие Babel от TypeScript Compiler

Babel:

  • удаляет типы;
  • выполняет транспиляцию;
  • не проверяет типизацию.

TypeScript Compiler (tsc):

  • проверяет типы;
  • генерирует JS;
  • поддерживает дополнительные возможности TS.

Часто используется связка:

TypeScript -> Babel -> Webpack

Передача options напрямую в loader

Конфигурация:

{
    test: /\.js$/,
    use: {
        loader: 'babel-loader',
        options: {
            presets: ['@babel/preset-env']
        }
    }
}

Такой подход удобен:

  • для небольших проектов;
  • при создании изолированных сборок;
  • для монорепозиториев.

Использование env-конфигураций

Babel поддерживает разные настройки для окружений.

Пример:

{
    "env": {
        "development": {
            "sourceMaps": true
        },
        "production": {
            "comments": false
        }
    }
}

Запуск:

NODE_ENV=production webpack

Настройка debug

Режим диагностики:

{
    "presets": [
        [
            "@babel/preset-env",
            {
                "debug": true
            }
        ]
    ]
}

Babel покажет:

  • какие трансформации применяются;
  • какие полифилы подключаются;
  • почему выполняется конкретная транспиляция.

Использование exclude в preset-env

Можно отключить отдельные трансформации:

{
    "presets": [
        [
            "@babel/preset-env",
            {
                "exclude": [
                    "@babel/plugin-transform-arrow-functions"
                ]
            }
        ]
    ]
}

Использование include в preset-env

Принудительное включение:

{
    "presets": [
        [
            "@babel/preset-env",
            {
                "include": [
                    "@babel/plugin-transform-classes"
                ]
            }
        ]
    ]
}

Режим loose

Некоторые преобразования могут выполняться в упрощённом виде.

Пример:

{
    "presets": [
        [
            "@babel/preset-env",
            {
                "loose": true
            }
        ]
    ]
}

Преимущества:

  • меньший размер кода;
  • более быстрая работа;
  • упрощённая генерация.

Недостаток — возможное отклонение от спецификации ECMAScript.


Совместная работа babel-loader и thread-loader

Для больших проектов:

npm install --save-dev thread-loader

Конфигурация:

{
    test: /\.js$/,
    use: [
        'thread-loader',
        'babel-loader'
    ]
}

thread-loader запускает обработку в отдельных потоках.


Совместимость с Webpack 5

Современная конфигурация обычно включает:

npm install --save-dev webpack webpack-cli babel-loader

Webpack 5:

  • полностью совместим с Babel 7;
  • поддерживает persistent cache;
  • ускоряет rebuild;
  • уменьшает объём служебного кода.

Типичная production-конфигурация

const path = require('path');

module.exports = {
    mode: 'production',

    entry: './src/index.js',

    output: {
        filename: 'bundle.[contenthash].js',
        path: path.resolve(__dirname, 'dist'),
        clean: true
    },

    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        cacheDirectory: true
                    }
                }
            }
        ]
    }
};

.babelrc:

{
    "presets": [
        [
            "@babel/preset-env",
            {
                "targets": "> 0.25%, not dead",
                "useBuiltIns": "usage",
                "corejs": 3
            }
        ]
    ]
}

Распространённые ошибки

Babel не транспилирует код

Причины:

  • отсутствует .babelrc;
  • неверный test;
  • файл исключён через exclude;
  • отсутствует @babel/preset-env.

Unexpected token

Ошибка:

Unexpected token <

Причина — Babel не умеет обрабатывать JSX.

Решение:

npm install --save-dev @babel/preset-react

Cannot find module ‘@babel/core

Причина:

@babel/core

не установлен.

Решение:

npm install --save-dev @babel/core

Полифилы не работают

Причины:

  • не установлен core-js;
  • отсутствует regenerator-runtime;
  • не настроен useBuiltIns.

Производительность babel-loader

Основные способы ускорения:

Метод Эффект
cacheDirectory ускорение повторной сборки
exclude: /node_modules/ уменьшение объёма обработки
thread-loader параллельная компиляция
уменьшение числа плагинов снижение нагрузки
точные targets меньше трансформаций

Архитектура обработки файлов

Последовательность обработки:

Исходный файл
    ↓
babel-loader
    ↓
Babel presets/plugins
    ↓
Транспилированный код
    ↓
Webpack bundle

babel-loader выступает связующим звеном между системой модульной сборки и системой транспиляции JavaScript-кода.