введение
«Это первый день моего участия в ноябрьском испытании обновлений, ознакомьтесь с подробностями события:Вызов последнего обновления 2021 г.".
Babel
В нынешнем фронтенде она существует как гора, и в любом проекте она присутствует в большей или меньшей степени.
может быть дляBabel
Подавляющее большинство фронтенд-разработчиков находятся в состоянии невежества, но они умеют как в реальном развитии бизнеса, так и в личном совершенствовании.Babel
Должно быть, это необходимый способ продвинуться до старшего фронтенд-инженера.
В статье мы говорим только о «галантерее», начиная с принципа и объединяя углубленную практику, чтобы показать вамBabel
красота.
мы начнемBabel
основное содержание и, таким образом, постепенноBabel
Мир разработчиков плагинов, отныне позволяет вамBabel
Удобно.
Общее содержание текста следующее:
-
Babel
Руководство по ежедневному использованию-
Начиная с основного содержания, вы освоите общие
Plugin
а такжеPreset
. -
в передовых инфраструктурных проектах
Babel
Объяснение конфигурации. -
Babel
Связанныйpolyfill
содержание.
-
-
Babel
Руководство по разработке плагинов-
взять тебя в
Babel
мир компиляции, опытBabel
Знание лежащих в его основе принципов. -
Возьмите вас за руку, чтобы разработать свой собственный
Babel
плагин.
-
🚀 Без лишних слов, давайте перейдем к делуBabel
Мир.
Babel
ежедневное использование
Сначала мы начнем с базовой конфигурацииBabel
и сопутствующий контент.
общийplugin
а такжеPreset
Сначала скажемPlugin
а такжеPreset
различия и связи.
так называемыйPreset
лишь некоторыеPlugin
коллекция, вы можете поставитьPreset
Понимание называется чем-тоPlugin
Интеграция называется пакетом.
общийPreset
В статье перечислены три наиболее часто используемыхPreset
,болееPrest
Вы можете проверить это здесь.
babel-preset-env
@babel/preset-env
умный пресет, который преобразует наши высокиеJavaScript
Код переводится в более низкую версию по встроенным правиламjavascript
код.
preset-env
Большая часть внутренней интеграцииplugin
(State > 3
) плагина перевода, он выполнит перевод кода в соответствии с соответствующими параметрами.
Вы можете настроить определенные параметры впосмотреть здесь.
@babel/preset-env
Не будет включать какие-либо предложения по синтаксису JavaScript ниже этапа 3. Если ниже требуется совместимостьStage 3
Синтаксис этапа требует дополнительного введения соответствующихPlugin
быть совместимым.
Важно отметить, что
babel-preset-env
Только для перевода этапа грамматики, такого как перевод стрелочных функций,const/let
грамматика. для некоторыхApi
илиEs 6
встроенный модульpolyfill
,preset-env
нельзя перевести. Мы опубликуем это позжеpolyfill
Объясните подробно для вас.
babel-preset-react
Обычно мы используемReact
серединаjsx
Я считаю, что всем понятна сутьjsx
в конечном итоге будет скомпилирован какReact.createElement()
метод.
babel-preset-react
Это предположение будетjsx
Роль перевода.
babel-preset-typescript
дляTypeScript
код, у нас есть два способа компиляцииTypeScript
код становитсяJavaScript
код.
-
использовать
tsc
командный, комбинированныйcli
аргументы командной строки илиtsconfig
конфигурационный файл для компиляцииts
код. -
использовать
babel
,пройти черезbabel-preset-typescript
код для компиляцииts
код.
Общий плагин
Babel
На официальном сайте очень подробноPlugin List.
об общемPlugin
На самом деле, большинство из них интегрировано вbabel-preset-env
, когда вы обнаружите, что ваш проект не поддерживает последнююjs
грамматике, мы можем обратиться к соответствующемуBabel Plugin List
Найдите соответствующий плагин грамматики и добавьте егоbabel
конфигурация.
Есть также некоторые менее часто используемые
packages
,Например@babel/register
: он будет переписанrequire
команду, добавьте к ней хук. После этого при каждом использованииrequire
нагрузка.js
,.jsx
,.es
а также.es6
Файл с суффиксным именем будет сначала перекодирован с помощью Babel.
Эти пакеты не особенно часто используются в повседневной жизни.Если есть студенты, у которых есть соответствующие требования к компиляции, они могут перейти к
babel
Проверьте официальный сайт. Если официального сайта нет в готовом видеplugin/package
,не переживай! Мы также научим вас практическим навыкам в будущем.babel
Разработка плагинов.
Среди наиболее распространенных@babel/plugin-transform-runtime
мы будем нижеPolyfill
Объясните подробно.
интерфейсная инфраструктураBabel
Подробная конфигурация
Далее поговорим о построении front-end проектовbabel
соответствующая конфигурация.
Что касается инструментов сборки интерфейса, вы никоим образом не используетеwebapack
ещеrollup
Или любой инструмент сборки и упаковки, который неотделим от внутренней частиBabel
соответствующая конфигурация.
Здесь мы используем наиболее часто используемые в бизнесе
webpack
Например, другие инструменты сборки представляют только разные пакеты с точки зрения использования,Babel
Принцип настройки тот же.
оWebPack
в нашем повседневном использованииbabel
Связанная конфигурация в основном включает следующие три связанных плагина:
-
babel-loader
-
babel-core
-
babel-preset-env
Возможно, вы часто видите их в процессе построения проекта, здесь мы будем использовать псевдокод шаг за шагом, чтобы объяснить разницу и связь между ними.
Сначала нам нужно знать, чтоwebpack
серединаloader
Суть заключается в функции, которая принимает на вход наш исходный код и возвращает новый контент.
babel-loader
такbabel-loader
Суть в функции, мы сопоставляем с соответствующейjsx?/tsx?
файл вbabel-loader
:
/**
*
* @param sourceCode 源代码内容
* @param options babel-loader相关参数
* @returns 处理后的代码
*/
function babelLoader (sourceCode,options) {
// ..
return targetCode
}
о
options
,babel-loader
Поддержка напрямую черезloader
инъекция формы параметра, а такжеloader
Внутри функции, прочитав.babelrc/babel.config.js/
В конфигурацию внедряются файлы babel.config.json и другие.
о
babel
В методах инициализации различных инфраструктурных проектов вы можетеПроверьте это здесь.
babel-core
мы говорили оbabel-loader
является только функцией, которая идентифицирует совпадающие файлы и принимает соответствующие параметры, тоbabel
Основная библиотека в процессе компиляции кода@babel/core
эта библиотека.
babel-core
даbabel
Ядро библиотеки компиляции, он может сделать наш кодЛексический анализ--Синтаксический анализ--Семантический анализпроцесс созданияAST
Абстрактное синтаксическое дерево, так что операции над «этим деревом» затем компилируются как новый код.
babel-core
На самом деле эквивалентно@babel/parse
а также@babel/generator
Сочетание этих двух сумок тронулоjs
Скомпилированные студенты могут знатьesprima
а такжеescodegen
С этими двумя библиотеками вы можете использоватьbabel-core
Роль понимания называется сочетанием этих двух библиотек.
babel-core
пройти черезtransform
метод для компиляции нашего кода.
оbabel-core
На самом деле существует множество методов компиляции вtransform
метод или принятьjs
Путь к файлуtransformFile
метод для компиляции файла в целом.
В то же время он также поддерживает синхронные и асинхронные методы.Вы можете найти конкретные методы впосмотреть здесь.
оbabel-core
Внутренние правила компиляции и использования будут подробно обсуждаться в следующих главах плагинов.
Далее, давайте улучшим соответствующийbabel-loader
функция:
const core = require('@babel/core')
/**
*
* @param sourceCode 源代码内容
* @param options babel-loader相关参数
* @returns 处理后的代码
*/
function babelLoader (sourceCode,options) {
// 通过transform方法编译传入的源代码
core.transform(sourceCode)
return targetCode
}
мы здесьbabel-loader
называется вbabel-core
В этой библиотеке был скомпилирован код.
babel-preset-env
Выше мы сказалиbabel-loader
по сути является функцией, которая внутренне передаетbabel/core
Этот основной пакет выполняетJavaScript
Перевод кода.
Но для перевода кодаНам нужно сказатьbabel
Каковы правила конвертации, как я должен сказатьbabel
: "Эй, Babel. Конвертируй мой код во что-то, называемое версией EcmaScript 5!".
В настоящее времяbabel-preset-env
Вот что он делает здесь:Рассказыватьbabel
Какие правила нужны для передачи кода.
const core = require('@babel/core');
/**
*
* @param sourceCode 源代码内容
* @param options babel-loader相关参数
* @returns 处理后的代码
*/
function babelLoader(sourceCode, options) {
// 通过transform方法编译传入的源代码
core.transform(sourceCode, {
presets: ['babel-preset-env'],
plugins: [...]
});
return targetCode;
}
здесь
plugin
а такжеprest
на самом деле то же самое, так что я будуplugin
прямо в коде. Аналогично некоторые другиеpreset
илиplugin
Тоже играет такую роль.
оbabel
Я считаю, что все поняли свои соответствующие обязанности и основные принципы.Если у вас есть другие проблемы с конфигурацией, вы можете обратиться к ним.babel
Документация или ознакомьтесь с этой моей статьейReact-Webpack5-TypeScript для создания спроектированного многостраничного приложения..
Babel
Связанныйpolyfill
содержание
чтоpolyfill
оpolyfill
, давайте сначала объясним, что имеется в виду подpolyfill
.
Во-первых, давайте проясним эти три понятия:
- до настоящего времени
ES
синтаксис, такой как: стрелочные функции,let/const
. - до настоящего времени
ES Api
,НапримерPromise
- до настоящего времени
ES
Экземпляр/статические методы, такие какString.prototype.include
babel-prest-env
будет конвертировать только последниеes
синтаксис, не преобразует соответствующийApi
и методы экземпляра, скажемES 6
серединаArray.from
статический метод.babel
Этот метод не будет переведен, если вы хотите распознать и запустить в браузерах более ранних версий.Array.from
Если метод соответствует нашим ожиданиям, нам необходимо ввести дополнительныеpolyfill
продолжаетсяArray
Добавьте вышеуказанный метод, чтобы реализовать это.
На самом деле, можно кратко резюмировать,грамматическая трансформацияpreset-env
Полностью дееспособен. Но некоторые встроенные модули методов, простоpreset-env
Грамматическую трансформацию невозможно распознать и преобразовать, поэтому необходим ряд инструментов, похожих на «прокладки», чтобы дополнить реализацию кода низкой версии этой части контента. Это называетсяpolyfill
роль,
Таргетинг наpolyfill
содержание метода,babel
Он включает в себя два аспекта для решения:
-
@babel/polyfill
-
@babel/runtime
-
@babel/plugin-transform-runtime
мы выяснили, чтоpolyfill
так же какpolyfill
После роли и значения давайте разберем эти два по одному.babel
Как использовать пакет и разница.
@babel/polyfill
Сначала давайте посмотрим на первую реализациюpolyfill
Путь:
@babel/polyfill
представлять
пройти черезbabelPolyfillДобавляя свойства к глобальным объектам и напрямую изменяя встроенные объектыPrototype
Добавить реализацию метода наpolyfill
.
сказать, что нам нужна поддержкаString.prototype.include
, после импортаbabelPolyfill
После этого пакета он будет глобальноString
добавить объект-прототипinclude
способ поддержать нашуJs Api
.
Мы сказали, что этот метод по существу монтирует свойства глобального объекта/встроенного объекта, поэтому этот метод неизбежно приведет к глобальному загрязнению.
заявление@babel/polyfill
существуетbabel-preset-env
EстьuseBuiltIns
параметр, который определяет, какpreset-env
используется в@babel/polyfill
.
{
"presets": [
["@babel/preset-env", {
"useBuiltIns": false
}]
]
}
-
useBuiltIns
--"usage"
|"entry"
|false
false
когда мы используемpreset-env
входящийuseBuiltIns
параметр, значение по умолчаниюfalse
. Это означает, что только последниеES
синтаксис, ничего не переводитApi
и метод.
entry
при входящемentry
Когда нам нужно вручную ввести его один раз в файл входа в проектcore-js
Это будет основано на конфигурации нашего списка совместимости браузера (browserList
)ПотомПолная суммаВвести несовместимоеpolyfill
.
Советы: в
Babel7.4。0
Позже,@babel/polyfill
Устаревший, он стал интеграцией двух других пакетов."core-js/stable"; "regenerator-runtime/runtime";
. вы можете увидеть здесьРазнообразие, но они используются одинаково, за исключением того, что пакеты, представленные в файле входа, отличаются.
Введение в настройку списка совместимости браузера можно найти здесь..
// 项目入口文件中需要额外引入polyfill
// core-js 2.0中是使用"@babel/polyfill" core-js3.0版本中变化成为了上边两个包
import "@babel/polyfill"
// babel
{
"presets": [
["@babel/preset-env", {
"useBuiltIns": "entry"
}]
]
}
Следует также отметить, что когда мы используем
useBuiltIns:entry/usage
, требуется дополнительная спецификацияcore-js
этот параметр. По умолчанию используетсяcore-js 2.0
,Так называемыйcore-js
Это реализация «шима», о котором мы упоминали выше. Он реализует ряд встроенных методов илиPromise
ЖдатьApi
.
core-js 2.0
версия должна следоватьpreset-env
Устанавливаются вместе, нет необходимости устанавливать отдельно
usage
Выше мы сказали, что конфигурацияentry
час,perset-env
Будет полностью импортирован на основе нашего списка совместимости браузеров.polyfill
. Так называемый полный импорт, например, используется только в нашем кодеArray.from
Сюда. ноpolyfill
не только ввестиArray.from
, а также знакомитPromise
,Array.prototype.include
и другие методы, которые не используются. Это приводит к введению слишком большого объема в упаковку.
В этот момент нашuseBuintIns:usage
конфигурация.
когда мы настраиваемuseBuintIns:usage
, он будет совместим с настроенным браузером, а в кодеиспользовалApi
импортироватьpolyfill
Добавляйте по мере необходимости.
когда используешьusage
, нам не нужно вводить дополнительно в запись проектаpolyfill
, он будет импортирован по запросу на основе того, что используется в нашем проекте.
{
"presets": [
["@babel/preset-env", {
"useBuiltIns": "usage",
"core-js": 3
}]
]
}
оusage
а такжеentry
Есть одно существенное отличие, о котором следует знать.
Вводим в проектPromise
Например.
когда мы настраиваемuseBuintInts:entry
, он будет импортирован только один раз в полный файл записиpolyfill
. Вы можете понять это так:
// 当使用entry配置时
...
// 一系列实现polyfill的方法
global.Promise = promise
// 其他文件使用时
const a = new Promise()
и когда мы используемuseBuintIns:usage
час,preset-env
Только на основе каждого модуля для анализа того, что они используютpolyfill
во вступление.
preset-env
Это поможет нам разумно внедрить там, где это необходимо, например:
// a. js 中
import "core-js/modules/es.promise";
...
// b.js中
import "core-js/modules/es.promise";
...
-
существует
usage
В этом случае, если у нас будет много модулей, несомненно будет много избыточного кода (import
грамматика). -
также используя
usage
потому что он импортируется локально внутри модуляpolyfill
Поэтому импортируйте его в модуль по мере необходимости иentry
Он будет введен один раз при вводе кода.
usageBuintIns
Различные параметры имеют разные адаптации для разных сценариев, и вам необходимо найти лучший способ в соответствии с реальной ситуацией вашего собственного проекта для конкретных сценариев использования параметров.
@babel/runtime
Выше мы упоминали@babel/polyfill
Существует побочный эффект загрязнения глобальных переменных в реализацииpolyfill
ВремяBabel
Это также предоставляет нам другой способ достижения этой функции, т.@babel/runtime
.
просто говоря,@babel/runtime
больше похоже наРешения по запросу, например, где использоватьPromise
,@babel/runtime
добавит вверху своего файлаimport promise from 'babel-runtime/core-js/promise'
.
В то же время мы упоминали выше оpreset-env
изuseBuintIns
элементы конфигурации, нашиpolyfill
даpreset-env
Помогите нам ввести смарт.
а такжеbabel-runtime
Тогда метод внедрения будет полностью передан нам интеллектом, и мы будем внедрять то, что нам нужно.
Его использование очень простое, пока мы идем на установкуnpm install --save @babel/runtime
После этого при необходимости использовать соответствующийpolyfill
Его можно ввозить отдельно от места. Например:
// a.js 中需要使用Promise 我们需要手动引入对应的运行时polyfill
import Promise from 'babel-runtime/core-js/promise'
const promsies = new Promise()
в общем,babel/runtime
Вы можете понимать это как библиотеку инструментов времени выполнения, на которую можно ссылаться.
против
babel/runtime
Мы будем сотрудничать в большинстве случаев@babel/plugin-transfrom-runtime
Выполните использование для достижения интеллектаruntime
изpolyfill
Представлять.
@babel/plugin-transform-runtime
babel-runtime
существующие проблемы
babel-runtime
Прежде чем мы вручную введем некоторыеpolyfill
, он внедрит в наш код что-то вроде_extend(), classCallCheck()
Инструментальные функции, такие как следующие, код этих инструментальных функций будет включен в каждый скомпилированный файл, например:
class Circle {}
// babel-runtime 编译Class需要借助_classCallCheck这个工具函数
function _classCallCheck(instance, Constructor) { //... }
var Circle = function Circle() { _classCallCheck(this, Circle); };
Если в нашем проекте есть несколько файлов, использующихclass
, то внедрить такую избыточную и повторяющуюся служебную функцию в каждый файл, несомненно, будет катастрофой.
Итак, по двум упомянутым выше вопросам:
-
babel-runtime
Интеллектуальный анализ недостижим, и нам нужно вводить его вручную. -
babel-runtime
Во время компиляции многократно генерируется избыточный код.
Мы собираемся представить нашего главного героя@babel/plugin-transform-runtime
.
@babel/plugin-transform-runtime
эффект
@babel/plugin-transform-runtime
Роль плагина как раз и заключается в решении вышеупомянутойrun-time
Плагины, поднятые для существующих проблем.
-
babel-runtime
Интеллектуальный анализ недостижим, и нам нужно вводить его вручную.
@babel/plugin-transform-runtime
Плагин грамотно проанализирует, что нужно перевести и использовать в нашем проекте.js
код, тем самым достигая модульности отbabel-runtime
импортировать необходимыеpolyfill
выполнить.
-
babel-runtime
Во время компиляции многократно генерируется избыточный код.
@babel/plugin-transform-runtime
Плагин предоставляетhelpers
параметр. В частности, вы можетеПроверьте все его параметры конфигурации здесь.
этоhelpers
После того, как параметр включен, вы можете обратиться к вышеупомянутым функциям инструмента, которые повторяются на этапе компиляции, таким какclassCallCheck, extends
Равное преобразование кода называетсяrequire
утверждение. На данный момент эти служебные функции не будут повторно появляться в используемых модулях. Например:
// @babel/plugin-transform-runtime会将工具函数转化为require语句进行引入
// 而非runtime那样直接将工具模块代码注入到模块中
var _classCallCheck = require("@babel/runtime/helpers/classCallCheck");
var Circle = function Circle() { _classCallCheck(this, Circle); };
настроить@babel/plugin-transform-runtime
На самом деле принцип использования подробно разобран выше, студенты, у которых остались вопросы по настройке, могут оставить сообщение или перейти в область комментариев.babel
Официальный вид сайта.
Вот список его текущей конфигурации по умолчанию:
{
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": false,
"helpers": true,
"regenerator": true,
"version": "7.0.0-beta.0"
}
]
]
}
Суммироватьpolyfill
Мы можем видеть, что дляpolyfill
На самом деле, я потратил много времени, пытаясь понять различия и связи между ними, так что давайте немного их подытожим.
существуетbabel
реализовано вpolyfill
Есть в основном два пути:
-
один через
@babel/polyfill
Сотрудничатьpreset-env
Использование таким способом может загрязнить глобальную область видимости. -
один через
@babel/runtime
Сотрудничать@babel/plugin-transform-runtime
При использовании этот способ не загрязняет область применения. -
Глобальный импорт загрязняет глобальную область, но в отличие от локального импорта. Это добавит много дополнительных операторов импорта и увеличит размер пакета.
существуетuseBuintIns:usage
На самом деле и@babel/plugin-transform-runtime
В случае аналогичного эффекта
Обычно мой личный выбор заключается в том, чтобы придерживаться первого использования, не загрязняя глобальную среду при разработке библиотеки классов.@babel/plugin-transform-runtime
пока в развитии бизнеса@babel/polyfill
.
babel-runtime 是为了减少重复代码而生的。 babel生成的代码,可能会用到一些_extend(), classCallCheck() 之类的工具函数,默认情况下,这些工具函数的代码会包含在编译后的文件中。如果存在多个文件,那每个文件都有可能含有一份重复的代码。
babel-runtime插件能够将这些工具函数的代码转换成require语句,指向为对babel-runtime的引用,如 require('babel-runtime/helpers/classCallCheck'). 这样, classCallCheck的代码就不需要在每个文件中都存在了。
Babel
Разработка плагина
Выше мы говорили о повседневных делахbabel
Принцип использования, поговорим оbabel
Содержимое разработки, связанное с подключаемым модулем.
Вам может быть интересноBabel
Что могут плагины? Проще говоря, поbabel
Плагины могут помочь вам глубже погрузиться в знания о принципах компиляции внешнего интерфейса на базовом уровне.
Конечно, если это не только для улучшения личных способностей, если вы разрабатываете собственную библиотеку компонентов, вы хотите добиться чего-то вродеelement-plus
Введение по запросу в , или, возможно, дляlint
У вас есть свои особые правила. В противном случае для некоторыхjs
Поддержка специальной записи в.
В общем, знание принципа компиляции может сделать что угодно!
взять тебя вbabel
мир компиляции
Для знания компиляции основное внимание в статье уделяется не этому. Но, пожалуйста, будьте уверены, я буду использовать самый популярный способ, чтобы вы началиbabel
Разработка плагинов.
webpack
,lint
,babel
И так много инструментов и основной библиотеки черезабстрактное синтаксическое дерево(Абстрактное синтаксическое дерево, AST) это понятие для реализации обработки кода.
AST
Так называемое абстрактное синтаксическое деревоJavaScript Parser
Преобразуйте код в абстрактное синтаксическое дерево, которое определяет структуру кода. Затем проанализируйте, измените и оптимизируйте код, манипулируя добавлением, удалением, модификацией и проверкой этого дерева.
Для преобразования кода в разныеAST
ты сможешьздесьastexplorer
Любой текущий основной парсерAST
трансформировать.
Здесь мы сначала перечислим некоторые справочные веб-сайты:
-
astexplorer: это онлайн-переводчик кода, который может преобразовать любой код в
AST
. -
babel-handbook:
babel
Руководство по разработке плагинов на китайском языке. -
the-super-tiny-compiler-cn:Один
github
с открытым исходным кодом маленькийlistp
передача стиляjs
Compiler, студентам, интересующимся принципом компиляции, настоятельно рекомендуется ознакомиться с его кодом.
babel
Руководство по основам разработки плагинов
Когда нам нужно разработать собственныйbabel
При подключении мы обычно используемbabel
Некоторые библиотеки для кодированияparser
так же какtransform ast
,generator code
, не требует от нас ручного выполнения процесса лексического/синтаксического анализа кода.
В разработке плагинов обычно участвуют следующие библиотеки:
-
@babel/core
: Мы сказали вышеbabel/core
даbabel
основная библиотека, ядроapi
все здесь. Как то, что мы упоминали вышеtransform
,parse
метод. -
@babel/parser
:babel
парсер. -
@babel/types
: Этот модуль содержит методы для ручного построения AST и проверки типа узлов AST (например, через соответствующийapi
создать соответствующий узел). -
@babel/traverse
: Этот модуль используется дляAst
обход, который поддерживает состояние всего дерева (обратите внимание, чтоtraverse
дляast
глубокий обход). -
@babel/generator
: Этот модуль используется для генерации кода черезAST
Создайте новый код для возврата.
babel
рабочий процесс
В ежедневных фронтенд-проектах большую часть времени мы используемbabel
провестиjs
преобразование кода.
Его рабочий процесс можно грубо свести к следующим трем аспектам:
-
Parse
фаза (анализ): на этой фазе нашjs
Код (строка) лексически анализируется для создания серииtokens
, а затем грамматический анализ будетtokens
комбинация, называемаяAST
Абстрактное синтаксическое дерево. (Напримерbabel-parser
Его роль - этот шаг) -
Transform
(преобразование) этап: этот этапbabel
Пересекая это дерево, для старогоAST
Добавляйте, удаляйте, изменяйте и проверяйте, а такжеjs
Преобразования синтаксических узлов называются синтаксическими узлами, совместимыми с браузером. (babel/traverse
Именно на этом шаге нужно пройти по дереву) -
Generator
(создать) этап: этот этапbabel
будет новыйAST
Преобразования также выполняют глубокий обход для создания нового кода. (@babel/generator
)
Мы используем картинку, чтобы описать этот процесс:
babel
серединаAST
процесс обхода
-
AST
Это так называемый обход в глубину.Студенты, которые не знают, что такое обход в глубину, могут сами проверить соответствующую информацию~ -
babel
серединаAST
Обход узлов основан на шаблоне посетителя (Visitor
), разные посетители будут выполнять разные действия и получать разные результаты. -
visitor
Метод, названный в честь каждого узла, монтируется наAST
При обходе соответствующее имя метода будет запущено для выполнения соответствующего метода для работы.
Рука с вашим развитиемbabel
плагин
Здесь мы используем простойES6
Функция стрелки преобразуется вES5
Способ начать, чтобы привести всех в реальностьbabel
Разработка плагина.
Я полагаю, что у некоторых студентов могут быть сомнения,babel
Уже присутствует в соответствующем@babel/plugin-transform-arrow-functions
Преобразование стрелочной функции, зачем нам ее реализовывать.
Вот так,babel
Этот плагин уже существует, и он идеален. Здесь я хочу подчеркнуть, что я выбрал этот пример, чтобы вы началиbabel
Процесс разработки плагина, простой пример на самом деле может лучше дать вам представление о разработке плагина, чтобы вы могли сделать выводы из одного случая.
Давайте начнем~
Сначала давайте посмотрим на результат, который нам нужно достичь:
Цель
// input
const arrowFunc = () => {
console.log(this)
}
// output
var _this = this
funciton arrowFunc() {
console.log(_this)
}
babel
оригинальный метод преобразования
/**
* babel插件
* 主要还是@babel/core中的transform、parse 对于ast的处理
* 以及babel/types 中各种转化规则
*
* Ast是一种深度优先遍历
* 内部使用访问者(visitor)模式
*
* babel主要也是做的AST的转化
*
* 1. 词法分析 tokens : var a = 1 ["var","a","=","1"]
* 2. 语法分析 将tokens按照固定规则生成AST语法树
* 3. 语法树转化 在旧的语法树基础上进行增删改查 生成新的语法书
* 4. 生成代码 根据新的Tree生成新的代码
*/
// babel核心转化库 包含core -》 AST -》 code的转化实现
/*
babel/core 其实就可以相当于 esprima+Estraverse+Escodegen
它会将原本的sourceCode转化为AST语法树
遍历老的语法树
遍历老的语法树时候 会检查传入的插件/或者第三个参数中传入的`visitor`
修改对应匹配的节点
生成新的语法树
之后生成新的代码地址
*/
const babel = require('@babel/core');
// babel/types 工具库 该模块包含手动构建TS的方法,并检查AST节点的类型。(根据不同节点类型进行转化实现)
const babelTypes = require('@babel/types');
// 转化箭头函数的插件
const arrowFunction = require('@babel/plugin-transform-arrow-functions');
const sourceCode = `const arrowFunc = () => {
console.log(this)
}`;
const targetCode = babel.transform(sourceCode, {
plugins: [arrowFunction],
});
console.log(targetCode.code)
Здесь мы используемbabel/core
,этоtransform
метод преобразует наш код вAST
войти одновременноplugins
обработка становится новойAST
и, наконец, сгенерируйте соответствующий код.
Студенты, которые не уверены в принципе работы плагина, могут написать его самостоятельно по комментариям к коду, здесь всего десяток строчек кода.
реализовать это самостоятельно@babel/plugin-transform-arrow-functions
плагин
Здесь мы пытаемся реализовать такую функцию самостоятельно.
Во-первых, давайте сначала напишем базовую структуру:
const babel = require('@babel/core');
// babel/types 工具库 该模块包含手动构建TS的方法,并检查AST节点的类型。(根据不同节点类型进行转化实现)
const babelTypes = require('@babel/types');
// 我们自己实现的转化插件
const { arrowFunctionPlugin } = require('./plugin-transform-arrow-functions');
const sourceCode = `const arrowFunc = () => {
console.log(this)
}`;
const targetCode = babel.transform(sourceCode, {
plugins: [arrowFunctionPlugin],
});
// 打印编译后代码
console.log(targetCode.code)
// plugin-transform-arrow-functions.js
const arrowFunctionPlugin = () => {
// ...
}
module.exports = {
arrowFunctionPlugin
}
Здесь мы построилиplugin-transform-arrow-functions
файл для реализации нашего собственного плагина:
Мы сказали вышеbabel
Плагин — это, по сути, объект со свойством в нем.visitor
. этоvisitor
У объекта есть много методов, и каждый метод назван на основе имени узла.
когдаbabel/core
серединаtransform
методAST
войдет при обходеvisitor
match в объекте, если тип соответствующего узла совпадаетvisitor
Затем свойства на нем будут выполнять соответствующий метод.
Например этот кусок кода:
const arrowFunctionPlugin = {
visitor: {
ArrowFunctionExpression(nodePath) {
// do something
}
},
}
когда проводитсяAST
При обходе, если встречается тип узлаArrowFunctionExpression
войдетvisitor
в объектеArrowFunctionExpression
Таким образом, метод выполняет соответствующую логику для работы с текущим деревом.
вот дваtip
Нужно немного объяснить вам.
- Как узнать тип каждого узла? Например
ArrowFunctionExpression
является типом стрелочной функции.
первый,babel/typesВсе типы узлов описаны в . Мы можем проверить поbabel/types
Проверьте соответствующий тип узла.
Конечно, есть и другой, более удобный способ, о котором мы упоминали выше.astexplorer, вы можете проверить соответствующую генерацию кода здесьAST
Тем самым получив соответствующий узел.
- что
nodePath
Параметр, что это делает?
Каждый метод здесь имеетnodePath
параметры, так называемыеnodePath
Параметр вы можете понимать как путь к узлу. Он содержит всю информацию о развилке этого узла в этом дереве и соответствующемapi
.Обратите внимание, что путь может быть выделен здесь, ты сможешьПроверьте это здесьсмысл и всеAPI
.
После того, как мы написали базовую структуру, приступим к реализации внутренней логики плагина.
Мы явно хотим поговорить о коде для компиляции, это неизбежно.AST
Модификация узлов. По сути, мы все еще проходимAST
Модификация операции узлаAST
Это дает результат кода, который мы хотим.
-
Прежде всего, мы можем пройтиastexplorerВведите наш исходный код и ожидаемый скомпилированный код, чтобы получить соответствующий
AST
структура. -
После этого мы сравниваем структуру двух деревьев, чтобы можно было сравнить оригинал.
AST
Измените на основе, чтобы получить наш окончательныйAST
. -
Шагов больше быть не должно.
babel transform
Метод будет пересмотрен в соответствии с нашей редакциейAST
Создайте соответствующий исходный код.
Студентам настоятельно рекомендуется входить самостоятельноastexplorerВведите код для перевода и сравните его с переведенным кодом.
Скриншот части функции стрелки узла, который необходимо скомпилировать:
Несколько скриншотов узлов скомпилированного кода:
Здесь мы находим контрастinput
а такжеoutput
:
-
output
Функциональный узел генерал-лейтенанта СтрелкаArrowFunctionExpression
заменяетсяFunctionDeclaration
. -
output
для стрелочных функций вbody
, объявление вызывающего выраженияExpressionStatement
когда входящийarguments
отThisExpression
заменяетсяIdentifier
. - в то же время
output
В ту же область видимости, что и стрелочная функция, добавлено объявление дополнительной переменной.const _this = this
.
Это просто, нам просто нужноarrowFunctionPlugin
Реализация этих трех функций может удовлетворить требования, давайте попробуем это вместе.
const babelTypes = require('@babel/types');
function ArrowFunctionExpression(path) {
const node = path.node;
hoistFunctionEnvironment(path);
node.type = 'FunctionDeclaration';
}
/**
*
*
* @param {*} nodePath 当前节点路径
*/
function hoistFunctionEnvironment(nodePath) {
// 往上查找 直到找到最近顶部非箭头函数的this p.isFunction() && !p.isArrowFunctionExpression()
// 或者找到跟节点 p.isProgram()
const thisEnvFn = nodePath.findParent((p) => {
return (p.isFunction() && !p.isArrowFunctionExpression()) || p.isProgram();
});
// 接下来查找当前作用域中那些地方用到了this的节点路径
const thisPaths = getScopeInfoInformation(thisEnvFn);
const thisBindingsName = generateBindName(thisEnvFn);
// thisEnvFn中添加一个变量 变量名为 thisBindingsName 变量值为 this
// 相当于 const _this = this
thisEnvFn.scope.push({
// 调用babelTypes中生成对应节点
// 详细你可以在这里查阅到 https://babeljs.io/docs/en/babel-types
id: babelTypes.Identifier(thisBindingsName),
init: babelTypes.thisExpression(),
});
thisPaths.forEach((thisPath) => {
// 将this替换称为_this
const replaceNode = babelTypes.Identifier(thisBindingsName);
thisPath.replaceWith(replaceNode);
});
}
/**
*
* 查找当前作用域内this使用的地方
* @param {*} nodePath 节点路径
*/
function getScopeInfoInformation(nodePath) {
const thisPaths = [];
// 调用nodePath中的traverse方法进行便利
// 你可以在这里查阅到 https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md
nodePath.traverse({
// 深度遍历节点路径 找到内部this语句
ThisExpression(thisPath) {
thisPaths.push(thisPath);
},
});
return thisPaths;
}
/**
* 判断之前是否存在 _this 这里简单处理下
* 直接返回固定的值
* @param {*} path 节点路径
* @returns
*/
function generateBindName(path, name = '_this', n = '') {
if (path.scope.hasBinding(name)) {
generateBindName(path, '_this' + n, parseInt(n) + 1);
}
return name;
}
module.exports = {
hoistFunctionEnvironment,
arrowFunctionPlugin: {
visitor: {
ArrowFunctionExpression,
},
},
};
Далее давайте воспользуемся плагином, который мы написали в коде, чтобыrun
Момент.
Подведем итоги процесса разработки плагина.
Хотя выше это простой плагинDemo
Например, а вот воробей маленький и полный. полныйbabel
Процесс плагина примерно такой, подытожим немного оbabel
Процесс разработки плагина.
-
Из исходного кода и транспилированного кода
AST
Сравните узлы, найдите соответствующие разные узлы и максимально повторно используйте предыдущие узлы. -
Есть измененные/добавленные/удаленные узлы, через
nodePath
серединаApi
вызвать соответствующий методAST
обработка.
Макроскопически, процесс разработки плагинов в основном разделен на эти два шага, а остальноеast
«Бизнес-логика» в разделе Среднего преобразования.
babel
В части разработки плагинов могут участвовать некоторые люди, которые раньше не сталкивались сAPI
, здесь я решил напрямую использовать код для объяснения разработки плагина и не объяснял это подробноAPI
. Если вы не понимаете некоторые части, вы можете оставить мне сообщение в области комментариев, соответствующееAPI
Я лично рекомендую вам делать большеруководство по разработке плагина babel-handbookЗапрос, понимание здесь будет более глубоким.
Плагин в тексте просто маленький
Demo
уровне, цель состоит в том, чтобы привести всех вbabel
Шлюз для разработки плагинов. Вы можете найти код в статье наздесьПроверить. этоrepo
содержит не толькоdemo
, также включает в себя несколько более сложных плагинов для обучения и имитации, а также плагин загрузки по требованию, который реализует библиотеку компонентов, упомянутую в начале статьи (Загружаю плагины по запросу. Я все еще пишу, простите мою лень...).
11.6 Обновление
Здесь мы добавляемbabel-register
простые очки знаний.
Давайте посмотрим на официальный сайт дляbabel-register
описание:
One of the ways you can use Babel is through the require hook. The require hook will bind itself to node's
require
and automatically compile files on the fly. This is equivalent to CoffeeScript's coffee-script/register.
Короткий ответ - представитьbabel/register
назадbabel
Файлы с определенным суффиксом будут обработаны.
All subsequent files required by node with the extensions
.es6, .es, .jsx, .mjs,
and.js
will be transformed by Babel.
Сначала я вообще-то не совсем понял, в чем его функция.Для файлов с вышеуказанными суффиксами дайтеBabel
Для конвертации мы используем различные инструменты компиляции переднего плана, такие какwebpack
серединаbabel-loader
а такжеrollup
середина@rollup/plugin-babel
Помогите нам сделать что-то?
здесь@babel/regiser
Функция не более чем одним махом.
пока яstackoverflow
увидеть это вотвечать.
Небольшое простое резюме, то есть обычно в нашей области фронтенда мы комбинируем различные инструменты сборки иbabel
Его использование может лучше передавать файлы, которые необходимо обработать,babel
Компилировать где.
пока вnodejs
серединаbabel
не его ядроAPI
часть, если мы хотимnodejs
используется вbabel
Чтобы перевести наши файлы, мы можем передатьbabel/regiseter
Таргетинг наrequire
Файл, представленный оператором, передаетсяbabel
идтиtransform
. чтобы достичьnodejs
Переводитjs
документ.
Важно отметить, что
babel/register
это своевременная компиляция.
// index.js
require('@babel/register')
const data = require('./register');
console.log(`${data}`, 'data');
// register.js
const arrowFunction = () => {
console.log('Hello,My name is wang.haoyu');
};
module.exports = arrowFunction
// babelrc
{
"presets": ["@babel/preset-env"]
}
когда мы используемnode
бегатьindex.js
Время:
Вы обнаружите, что наш результат печати стал нормальной функцией.
На этом этапе давайте закомментируем соответствующий@babel/register
// require('@babel/register')
const data = require('./register');
console.log(`${data}`, 'data');
Мы обнаружили, что в этот момент наша стрелочная функция неpreset-env
эффективный.
11.16 Обновление
Дополнение оuseBuiltIns:usage
а такжеplugin-runtime
Отличия и лучшие практики.
напиши в конце
На этом этапе я хотел бы поблагодарить всех, кто видел здесь маленького партнера.
в статьеbabel
Объяснение — это только вершина айсберга, надеюсь, эта статья станет исследованием для всех.Babel
отправная точка.