В последние годы, если вы являетесь фронтенд-разработчиком, если вы не использовали или даже не слышали о Babel, вас можно считать путешественником, верно?
Когда дело доходит до babel, всплывает ряд существительных:
- babel-cli
- babel-core
- babel-runtime
- babel-node
- babel-polyfill
- ...
Это все бейблы? Что они делают? Есть ли разница?
что именно сделал Бабель? как?
Проще говоря, новый синтаксис es2015/2016/2017/2046 в JavaScript преобразуется в es5, чтобы недорогие среды выполнения (такие как браузеры и узлы) могли распознавать и выполнять. Эта статья основана на Babel 6.x для обсуждения. Недавно компания babel выпустила версию 7.x, о которой я расскажу в конце.
Строго говоря, babel также можно преобразовать в более низкую спецификацию. Но в нынешнем виде спецификации es5 достаточно для охвата большинства браузеров, поэтому переход на es5, как правило, безопасен и популярен.
Если вы мало что знаете о es5/es2015 и т. д., вам может понадобиться сначала составить класс.
инструкции
Всего есть три способа:
- Использовать один файл (автономный скрипт)
- командная строка (cli)
- Плагины для инструментов сборки (babel-loader для webpack, rollup-plugin-babel для rollup).
Последние два встречаются чаще. Второй чаще встречается в package.json.scripts
Команда в абзаце, третья напрямую интегрирована в инструмент сборки.
Единственная разница между этими тремя методами — это вход.Вызываемое ядро Babel обрабатывается одинаково, так что не будем беспокоиться о входе.
Время выполнения и плагины
Babel делится на три этапа: анализ, преобразование и генерация.
Сам Babel не имеет никакой функции преобразования, он разбивает функции преобразования на плагины один за другим. Поэтому, когда мы не настраиваем никаких плагинов, код и входные данные, проходящие через Babel, остаются теми же.
Существует два типа плагинов:
- когда мы добавляемплагин синтаксисаПосле этого этап синтаксического анализа позволяет babel анализировать больше грамматик. (Кстати, библиотека синтаксического анализа, используемая для внутреннего использования Babel, называется babylon и не разработана Babel.)
Для простого примера, когда мы определяем или вызываем метод, запятые не допускаются после последнего параметра, напримерcallFoo(param1, param2,)
является незаконным. Если исходный код написан таким образом, после babel будет выдана синтаксическая ошибка.
Но недавние предложения JS позволили этот новый способ написания (чтобы сделать различия в коде более понятными). Чтобы избежать ошибок в сообщениях Babel, вам необходимо добавить синтаксические плагины.babel-plugin-syntax-trailing-function-commas
- когда мы добавляемПлагин переводаПосле этого на этапе преобразования исходный код преобразуется и выводится. Это также самое важное требование для использования Babel.
По сравнению с плагинами синтаксиса плагины перевода на самом деле лучше понятны, например, функции стрелок.(a) => a
будет преобразован вfunction (a) {return a}
. Плагин, который выполняет эту работу, называетсяbabel-plugin-transform-es2015-arrow-functions
.
Версия плагина грамматики и версия плагина перевода могут существовать для одного и того же вида грамматики.Если мы используем плагин транспилера, нам больше не нужно использовать плагин синтаксиса.
конфигурационный файл
Поскольку плагины являются основой Babel, как их использовать? Всего 2 шага:
- Добавьте название плагина в конфигурационный файл (создайте .babelrc или package.json в корневом каталоге
babel
внутри тот же формат) - использовать
npm install babel-plugin-xxx
установить
Конкретный формат записи подробно не описывается.
preset
Например, es2015 — это набор спецификаций, включающий около дюжины или 20 плагинов для перевода. Если вы хотите, чтобы разработчики добавляли и устанавливали по одному каждый раз, файл конфигурации будет очень длинным.npm install
Это также будет долго, не говоря уже о том, что нам, возможно, придется использовать другие спецификации одновременно.
Для решения этой проблемы Babel также предоставляет набор плагинов. Поскольку он широко используется, нет необходимости повторять определение и установку. (Разница между одной точкой и пакетом в том, что пакет экономит много времени и усилий по настройке)
Пресеты делятся на следующие категории:
-
Официальный контент, в настоящее время включающий env, react, flow, minify и т. д. Самое главное здесь — это env, о котором будет подробно рассказано позже.
-
stage-x, который содержит проект последней спецификации года, обновляется каждый год.
Он также подразделяется на
- Этап 0 — Пугало: Просто идея, которую придумали участники TC39.
- Этап 1 - Предложение: первоначальная попытка.
- Этап 2 - Первый проект: Заполните предварительную спецификацию.
- Этап 3 — Кандидат: Полная спецификация и предварительная реализация браузера.
- Этап 4 — Завершение: будет добавлено в следующий ежегодный выпуск.
Например
syntax-dynamic-import
является содержанием этапа-2,transform-object-rest-spread
Это содержание стадии-3.Кроме того, нижний этап будет содержать все содержимое более высокого уровня, например этап-1 будет содержать все содержимое этапа-2, этапа-3.
Обновление Stage-4 будет помещено непосредственно в env в следующем году, поэтому нет необходимости использовать отдельный Stage-4.
-
es201x, latest
Это грамматики, включенные в стандартную спецификацию. Например, es2015 содержит
arrow-functions
, es2017 содержитsyntax-trailing-function-commas
. Но из-за появления env и es2016, и es2017 были заброшены. Таким образом, мы часто видим es2015 в списке отдельно, но редко два других.Последний является прототипом env, это предустановка, обновляемая каждый год, цель состоит в том, чтобы включить все es201x. Но также из-за появления более гибких env от него отказались.
исполнительный лист
Несколько простых принципов:
- Плагин запустится перед пресетом.
- Плагины выполняются последовательно спереди назад.
- Порядок предустановкикак раз наоборот(сзади на перед).
Обратный порядок пресетов в первую очередь для обратной совместимости, так как большинство пользователей пишут в порядке['es2015', 'stage-0']
. Это должно быть выполнено в первую очередьstage-0
Чтобы убедиться, что babel не сообщает об ошибке. Поэтому, когда мы расставляем пресеты, мы также должны обращать внимание на порядок.На самом деле, пока они перечислены в хронологическом порядке.
Элементы конфигурации для плагинов и пресетов
Короче говоря, плагины и пресеты просто должны перечислить свои имена в строковом формате. Но если предустановке или плагину нужны какие-то элементы конфигурации (или параметры), вам нужно сначала превратить себя в массив. Первый элемент по-прежнему является строкой, представляющей собственное имя, а второй элемент является объектом, объектом конфигурации.
Наиболее важная вещь для настройки — это env, а именно:
"presets": [
// 带了配置项,自己变成数组
[
// 第一个元素依然是名字
"env",
// 第二个元素是对象,列出配置项
{
"module": false
}
],
// 不带配置项,直接列出名字
"stage-2"
]
окружение (выделение)
Поскольку env является наиболее часто используемым и наиболее важным, нам нужно сосредоточиться на нем.
Основная цель env — узнать характеристики целевой среды посредством конфигурации, а затем выполнить только необходимые преобразования. Например, если целевой браузер поддерживает es2015, то пресет es2015 фактически не нужен, поэтому код может быть меньше (как правило, преобразованный код всегда длиннее), а время сборки может быть сокращено.
Если вы не прописываете какие-либо элементы конфигурации, env эквивалентен last, что также эквивалентно добавлению es2015 + es2016 + es2017 (исключая плагины в stage-x). Список плагинов, включенных в env, хранится по адресуздесь
Вот некоторые из наиболее часто используемых методов настройки:
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "safari >= 7"]
}
}]
]
}
Приведенная выше конфигурация будет учитывать характеристики последних 2-х версий всех браузеров (версия сафари выше или равна 7.0) и конвертировать необходимый код. Существующие функции этих версий не будут преобразованы. Синтаксис здесь может относиться кbrowserslist
{
"presets": [
["env", {
"targets": {
"node": "6.10"
}
}]
]
}
Приведенная выше конфигурация устанавливает цель для nodejs и поддерживает версии 6.10 и выше. также можно использоватьnode: 'current'
для поддержки последней стабильной версии. Например, стрелочные функции не будут преобразованы в nodejs 6 и выше, но будут преобразованы в nodejs 0.12.
Еще один полезный элемент конфигурацииmodules
. Его значение может бытьamd
, umd
, systemjs
, commonjs
иfalse
. Это позволяет babel выводить код в определенном модульном формате. Если вы выберетеfalse
Модульная обработка не выполняется.
Другие вспомогательные инструменты
Основной механизм обработки и методы конфигурации babel обсуждались выше, и это способ вызова babel независимо от какой-либо записи. А вот упомянутая в начале статьи кучаbabel-*
Все еще сбивает с толку. На самом деле этиbabel-*
Большинство из них представляют собой разные входы (методы) использования babel, давайте кратко представим их ниже.
babel-cli
Как следует из названия, cli — это инструмент командной строки. установленbabel-cli
можно использовать в командной строкеbabel
команда для компиляции файла.
Следующие шаблоны часто используются при разработке пакетов npm:
- Пучок
babel-cli
установить какdevDependencies
- добавить в package.json
scripts
(Напримерprepublish
),использоватьbabel
команда для компиляции файла npm publish
Это позволяет писать исходный код с использованием более нового канонического синтаксиса JS, сохраняя при этом поддержку устаревших сред. Поскольку проект может быть не слишком большим и не нуждаться в инструменте сборки (веб-пакет или накопительный пакет), перед выпуском используйте его.babel-cli
для обработки.
babel-node
babel-node
даbabel-cli
часть, его не нужно устанавливать отдельно.
Его роль заключается в запуске кода es2015 непосредственно в среде узла без дополнительного перекодирования. Например, у нас есть файл js, написанный с синтаксисом es2015 (например, с использованием стрелочных функций). мы можем напрямую использоватьbabel-node es2015.js
Выполнить вместо транскодирования.
Можно сказать:babel-node
= babel-polyfill
+ babel-register
. Кто эти двое?
babel-register
переписать модуль babel-registerrequire
команду, добавьте к ней хук. После этого при каждом использованииrequire
нагрузка.js
,.jsx
,.es
и.es6
Файл с суффиксным именем будет сначала перекодирован с помощью babel.
При использовании он должен быть загружен первымrequire('babel-register')
.
Следует отметить, что babel-register будет толькоrequire
загружаемые командой файлы перекодируются, ане будет перекодировать текущий файл.
Кроме того, поскольку он перекодирует в реальном времени,Подходит только для использования в среде разработки.
babel-polyfill
Babel по умолчанию преобразует только синтаксис js, а не новые API, такие как глобальные объекты, такие как Iterator, Generator, Set, Maps, Proxy, Reflect, Symbol, Promise, и некоторые методы, определенные для глобальных объектов (например,Object.assign
) не будет перекодирован.
Например, es2015 добавлен к объектам массива.Array.from
метод. Babel не будет перекодировать этот метод. Если вы хотите, чтобы этот метод работал, вы должны использоватьbabel-polyfill
. (интегрировано внутриcore-js
иregenerator
)
При использовании увеличивать перед запуском всего кодаrequire('babel-polyfill')
. или более обычная операция находится вwebpack.config.js
генерал-лейтенантbabel-polyfill
как первая запись. Поэтому должно бытьbabel-polyfill
в видеdependencies
вместоdevDependencies
babel-polyfill
Есть два основных недостатка:
-
использовать
babel-polyfill
Это приведет к тому, что пакет будет очень большим, потому чтоbabel-polyfill
представляет собой единое целое, добавляя все методы в цепочку прототипов. Например, мы использовали толькоArray.from
, но ставитObject.defineProperty
Его также добавляют, что является расточительством. Эту проблему можно решить, используяcore-js
Определенная библиотека классов для решения,core-js
все отдельно. -
babel-polyfill
Это загрязнит глобальные переменные и изменит цепочку прототипов многих классов.Если мы разработаем библиотеку классов для использования другими разработчиками, эта ситуация станет очень неконтролируемой.
Таким образом, в реальном использовании, если мы не можем вынести эти два недостатка (особенно второй), мы обычно склонны использоватьbabel-plugin-transform-runtime
.
Но если код содержит методы экземпляров типов в более высокой версии js (например,[1,2,3].includes(1)
), для которого по-прежнему требуется полифилл.
babel-runtime и babel-plugin-transform-runtime (выделено)
Мы часто видим, что .babelrc используется в проектах.babel-plugin-transform-runtime
,иpackage.json
серединаdependencies
(Обратите внимание, что это неdevDependencies
) также включаетbabel-runtime
, эти два используются в наборах? Что они делают?
во-первыхbabel-plugin-transform-runtime
.
babel преобразует синтаксис js, о котором упоминалось ранее. отasync/await
Например, без этого плагина (то есть по умолчанию) преобразованный код был бы таким:
// babel 添加一个方法,把 async 转化为 generator
function _asyncToGenerator(fn) { return function () {....}} // 很长很长一段
// 具体使用处
var _ref = _asyncToGenerator(function* (arg1, arg2) {
yield (0, something)(arg1, arg2);
});
Не зацикливайтесь на конкретном синтаксисе, просто посмотрите, это_asyncToGenerator
определяется в текущем файле и затем используется для замены исходного кодаawait
. Но каждый преобразованный файл будет вставлять абзац_asyncToGenerator
Это приводит к дублированию и потерям.
в использованииbabel-plugin-transform-runtime
, преобразованный код станет
// 从直接定义改为引用,这样就不会重复定义了。
var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
// 具体使用处是一样的
var _ref = _asyncToGenerator3(function* (arg1, arg2) {
yield (0, something)(arg1, arg2);
});
Переход от метода определения к ссылке, тогда повторяющееся определение становится повторяющейся ссылкой, и нет проблемы дублирования кода.
Но и здесь мы находимbabel-runtime
выходит, это совокупность этих методов, а значит,в настоящее время используетbabel-plugin-transform-runtime
когда вы должны поставитьbabel-runtime
как зависимость.
скажи большеbabel-runtime
, который внутренне объединяет
-
core-js
: преобразовать некоторые встроенные классы (Promise
,Symbols
и т. д.) и статические методы (Array.from
Ждать). Большая часть преобразования выполняется здесь. Импортируется автоматически. -
regenerator
: в видеcore-js
пропуски, в основномgenerator/yield
иasync/await
Две группы поддержки. при использовании в кодеgenerators/async
автоматически импортируется. -
помощники, как указано выше
asyncToGenerator
является одним из них, и другие, такие какjsx
,classCallCheck
подожди, ты увидишьbabel-helpers. Когда в коде есть встроенные помощники (как в первом фрагменте кода выше), удалите определение и вставьте ссылку (становясь таким образом вторым фрагментом кода).
babel-plugin-transform-runtime
не поддерживаетсяметоды экземпляра (например,[1,2,3].includes(1)
)
Кроме того, существует плагин, который также может выполнять работу по разделению и объединению помощников, чтобы избежать дублирования кода, который называетсяbabel-plugin-external-helpers
. Но поскольку мы используемtransform-runtime
Эта функция уже включена, поэтому ее не нужно использовать повторно. И авторы babel начали обсуждать, что эти два плагина слишком похожи, и обсуждают добавлениеexternal-helpers
удалить, обсуждается вissue#5699середина.
babel-loader
Три способа использования babel были упомянуты ранее и представленыbabel-cli
. Но некоторые крупные проекты будут иметь инструменты сборки (такие как webpack или rollup) для сборки и минимизации кода (uglify). Теоретически мы могли бы также использовать минимизированный код, но это было бы очень медленно. Так не было бы идеально добавить обработку Babel перед uglify?
Поэтому необходимо вставить Babel в инструмент сборки. Возьмем (я хорошо знаком) веб-пакет в качестве примера, в веб-пакете есть концепция загрузчика, поэтому он выглядитbabel-loader
.
иbabel-cli
Такой же,babel-loader
Также читается в .babelrc или package.jsonbabel
Сегмент настраивается как собственный, и последующая обработка ядра такая же. единственное соотношениеbabel-cli
Сложность заключается в том, что он должен взаимодействовать с веб-пакетом, поэтому его необходимо настроить на стороне веб-пакета. Наиболее распространенные из них следующие:
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader'
}
]
}
Если вы хотите передать здесь элементы конфигурации babel, вы также можете изменить его на:
// loader: 'babel-loader' 改成如下:
use: {
loader: 'babel-loader',
options: {
// 配置项在这里
}
}
Элементы конфигурации здесь имеют наивысший приоритет. Но я думаю, что понятнее и читабельнее вынести его в отдельный конфигурационный файл.
Резюме
название | эффект | Примечание |
---|---|---|
babel-cli | Разрешить командной строке использовать команду babel для переноса файлов | |
babel-node | Разрешить командной строке использовать babel-node для прямого перевода + выполнения файлов узлов | следитьbabel-cli Установить вместеbabel-node = babel-polyfill + babel-register
|
babel-register | переписатьrequire команда для перекодирования загружаемого файла без перекодирования текущего файла |
Только для среды разработки |
babel-polyfill | Добавьте совместимые методы ко всем API | нужно перед всем кодомrequire , и объем относительно большой |
babel-plugin-transform-runtime & babel-runtime | Методы вспомогательного класса изменены с определенных перед каждым использованием на единые.require , чтобы упростить код |
babel-runtime Необходимо установить как зависимость, а не зависимость разработки |
babel-loader | При использовании webpack в качестве загрузчика для перекодирования перед обфускацией кода |
Babel 7.x
Недавно Babel выпустил версию 7.0. Поскольку все вышеперечисленные части написаны для 6.x, поэтому давайте обратим внимание на изменения, внесенные 7.0 (без изменений в основном механизме, без изменений в плагинах, пресетах, парсинге и переводе).
Я выбираю только некоторые из них, которые больше связаны с разработчиками, и перечисляю их здесь, а большинство пропущенных относятся к определенному плагину. Полный список можно посмотретьОфициальный сайт.
Изменения в пресетах: уберите es201x, удалите stage-x и принудительно включите env (выделено)
Цель поэтапного отказа от es201x — позволить env автоматически выбирать среду, не требуя от разработчиков дополнительных усилий.Любой разработчик, использующий es201x, должен вместо этого использовать env.. А вот удаление (original deprecated) здесь не удаление, а использовать его не рекомендуется.Трудно сказать, что бабел 8 действительно удален.
Напротив, этапу-х не так повезло, их просто удалили. Это связано с тем, что команда babel считает расточительным тратить энергию на обновление пресетов для этих «нестабильных черновиков». Хотя stage-x был удален, плагины, которые он содержит, не были удалены (просто переименованы, см. следующий раздел), и мы все еще можем явно объявить эти плагины для достижения эквивалентных эффектов.Полный список
Чтобы сократить механическую работу разработчиков по замене конфигурационных файлов, Babel разработалbabel-upgrade
изинструмент, который обнаружит stage-x в конфигурации babel и заменит его соответствующими плагинами. Помимо этого у него есть и другие функции, их мы подробно рассмотрим позже. (Короче говоря, цель состоит в том, чтобы сделать ваш переход на Babel 7 более плавным)
Изменения в именах пакетов npm (выделено)
Это кардинальное изменение в Babel 7, в результате которого всеbabel-*
переименован в@babel/*
,Например:
-
babel-cli
стал@babel/cli
. -
babel-preset-env
стал@babel/preset-env
. Кроме того, можно также опуститьpreset
и сокращенно как@babel/env
. -
babel-plugin-transform-arrow-functions
стал@babel/plugin-transform-arrow-functions
. иpreset
Такой же,plugin
также может быть опущен, поэтому сокращенно@babel/transform-arrow-functions
.
Это изменение относится не только к зависимостям package.json, но и к конфигурации .babelrc (plugins
, presets
), а также для согласованности. Например
{
"presets": [
- "env"
+ "@babel/preset-env"
]
}
Кстати, ядро упомянутой выше грамматики парсинга babelbabylon
теперь переименован в@babel/parser
, который, по-видимому, кодифицирован.
Упомянутый выше stage-x был удален, а содержащиеся в нем плагины были переименованы, хотя и остались. Команда babel хочет более четко различать плагины, которые уже есть в спецификации (например, es2015).babel-plugin-transform-arrow-functions
) и плагины, которые находятся только в черновике (например, stage-0's@babel/plugin-proposal-function-bind
). Способ заключается в том, чтобы добавить к имениproposal
, все подключаемые модули перевода, включенные в stage-x, используют этот префикс, кроме подключаемых модулей синтаксиса.
Наконец, если имя плагина содержит каноническое имя (-es2015-
, -es3-
д.), будут удалены. Напримерbabel-plugin-transform-es2015-classes
стал@babel/plugin-transform-classes
. (Я не использовал этот плагин один, стыдно)
Нижние версии узла больше не поддерживаются
Babel 7.0 больше не поддерживает четыре версии nodejs 0.10, 0.12, 4, 5, что эквивалентно требованию nodejs >= 6 (текущий LTS nodejs равен 8, поэтому требование не слишком велико).
Больше не поддерживается здесь означает, что babel нельзя использовать для перевода кода в этих средах узлов с низкой версией, но код, переведенный babel, все еще может работать в этих средах, так что не путайте.
Изменения только и игнорирования правил сопоставления
В вавилоне 6,ignore
опция, если включена*.foo.js
, что на самом деле означает (в переводе на glob)./**/*.foo.js
, который является текущим каталогомвключить подкаталогивсеfoo.js
конечный файл. Это может противоречить общепринятому пониманию разработчиков.
Итак, в Вавилоне 7 то же выражение*.foo.js
Применяется только к текущему каталогу, а не к подкаталогам. Если вы все еще хотите работать с подкаталогами, вы должны написать это в соответствии с полной спецификацией glob как./**/*.foo.js
Только тогда может.only
Тоже самое.
Это изменение правила применяется только к подстановочным знакам, а не к путям. такnode_modules
По-прежнему содержит все свои подкаталоги, а не только один уровень. (Иначе девелоперы по всему миру взорвутся)
@babel/node стал независимым от @babel/cli
В отличие от babel 6, если вы хотите использовать@babel/node
, его нужно установить отдельно и добавить в зависимости.
babel-upgrade
Я упомянул об этом, когда упомянул об удалении stage-xинструмент, его цель — помочь пользователям автоматизировать переход с babel 6 на 7.
Функции этого инструмента обновления включают в себя: (Это не полный список, только наиболее важный и часто используемый контент)
- package.json
- поместите все зависимости (и зависимости разработки) в
babel-*
заменить@babel/*
- положить эти
@babel/*
Зависимые версии обновляются до последней версии (например,^7.0.0
) - если
scripts
в использованииbabel-node
, автоматически добавлено@babel/node
для зависимостей разработки - Если есть
babel
элементы конфигурации, проверьтеplugins
иpresets
, введите короткое имя (env
) с полным именем (@babel/preset-env
)
- .babelrc
- проверить, какой
plugins
иpresets
, введите короткое имя (env
) с полным именем (@babel/preset-env
) - Проверьте, содержит ли он
preset-stage-x
, если есть, замените его соответствующим плагином и добавьте вplugins
Он используется следующим образом:
# 不安装到本地而是直接运行命令,npm 的新功能
npx babel-upgrade --write
# 或者常规方式
npm i babel-upgrade -g
babel-upgrade --write
babel-upgrade
Сам инструмент все еще находится в стадии разработки, и многие TODO не выполнены, поэтому функции в будущем могут быть богаче, например, упомянутые выше.ignore
преобразование подстановочных знаков и т. д.