Babel
Babel — это компилятор JavaScript.
// Babel 输入: ES2015 箭头函数
[1, 2, 3].map((n) => n + 1);
// Babel 输出: ES5 语法实现的同等功能
[1, 2, 3].map(function(n) {
return n + 1;
});
Babel позволяет нам написать новую версию грамматики, преобразовать ее в младшую версию, чтобы она могла работать в браузерах, которые поддерживают только младшую версию грамматики.
Babel великолепен, он «знает» код и меняет его. Этот Babel — это код для управления кодом, круто.
Изучение Babel очень полезно для улучшения наших способностей.Обычно мы используем код для управления различными вещами.На этот раз объект, которым мы оперируем, становится самим кодом.
Этот процесс понимания кода и манипулирования им называется статическим анализом кода.
Статический анализ кода
Давайте сначала рассмотрим проблему, подсветка кода в редакторе выглядит следующим образом:
Редактор может помечать разные элементы кода разными цветами. Очевидно, что редактору необходимо «распознать» код, проанализировать и обработать код, чтобы добиться этого эффекта. Это называется статическим анализом кода.
Статический анализ против динамического анализа
Статический анализ — это процесс анализа кода без необходимости выполнения кода.
Динамический анализ предназначен для анализа и обработки кода во время его выполнения. Приведенная выше подсветка кода относится к статическому анализу, который анализируется и обрабатывается, когда код не выполняется.
Полезность статического анализа
Статический анализ может не только выделять код, но и преобразовывать код, а также оптимизировать и сжимать наш исходный код.
AST (абстрактное синтаксическое дерево)
В процессе статического анализа кода исходный код преобразуется в AST (Abstract Syntax Tree).
Почему существует АСТ
Исходный код для Бабеля — это строка. Babel хочет разобрать эту строку. Обычно мы работаем со строками, используя строковые методы или регулярные выражения, но этого далеко недостаточно для выполнения сложных операций со строками (исходный код).
Для работы необходимо преобразовать строку (исходный код) в древовидную структуру данных. Эта древовидная структура называется AST (абстрактное синтаксическое дерево).
Здесь я думаю о программе = структуре данных + алгоритме. Бизнес-требования, которые мы обычно пишем, не требуют высоких структур данных, а простые объекты и списки могут быть решены, однако, если нам нужны какие-то конкретные сложные задачи, такие как код операции, который мы сейчас изучаем, нам нужно сначала подумать: что следует Я оперирую Какую структуру данных мне поставить, мне будет проще написать алгоритм/логику.
Разобрать исходный код в AST
Что касается исходного кода, то в настоящее время мы можем видеть его как строку, и первым шагом в его анализе должно быть сначала преобразование исходного кода в AST, чтобы облегчить последующие операции.
есть одинОнлайн-конвертер AST, мы можем ставить на нем эксперименты, писать код, он поможет нам перевести его в AST:
Я ничего не пишу, а у AST есть корневая нода:
// AST
{
"type": "Program",
"start": 0,
"end": 0,
"body": [],
"sourceType": "module"
} // 可以看成是一个对象,有一些字段,这代码树的根结点。
Затем я пишу строку кода:
// 源码
const text = 'Hello World';
// AST
{
"type": "Program",
"start": 0,
"end": 27,
"body": [
{
"type": "VariableDeclaration",
"start": 0,
"end": 27,
"declarations": [
{
"type": "VariableDeclarator",
"start": 6,
"end": 26,
"id": {
"type": "Identifier",
"start": 6,
"end": 10,
"name": "text"
},
"init": {
"type": "Literal",
"start": 13,
"end": 26,
"value": "Hello World",
"raw": "'Hello World'"
}
}
],
"kind": "const"
}
],
"sourceType": "module"
}
Глупый, Const Text = «Hello World»; генерирует так много вещей. Понимание этого и понимания AST - первый порог для обучения Бабела.
понимать АСТ
Посмотрите на рисунок ниже, это интерфейс AST Explorer, напишите код слева и помогите нам перевести его в AST справа. AST имеет два выражения, Tree и JSON. Все вышеперечисленное выражено в формате JSON. Позже , я обнаружил, что дерево все еще используется.На форму проще смотреть, потому что форма дерева выделяет тип узла:
Я рисую дерево, выраженное AST, следующим образом:
Обобщите характеристики дерева AST:
- Узлы типизированы. Когда мы изучаем структуру данных дерева, узлы самые простые, здесь сложные и имеют типы.
- Отношения между узлом и его дочерними узлами связаны через атрибуты узла. Древовидная структура, которую мы изучаем, состоит из всех левых и правых дочерних элементов левых и правых дочерних элементов. Однако в дереве AST разные типы узлов имеют разные атрибуты: дочерним узлом узла типа Program является его атрибут body, а дочерним узлом типа VariableDeclaration — его объявления и атрибуты вида. То есть атрибуты узла рассматриваются как дочерние узлы узла, а дочерние узлы также могут иметь типы, образующие дерево.
- Родительский узел представляет собой комбинацию всех дочерних узлов.Мы видим, что const text = «Hello World», представленный VariableDeclaration, разделен на следующие два дочерних узла, и дочерние узлы продолжают разделяться.
Я надеюсь, что из вышеприведенного анализа у каждого может быть самое интуитивное понимание AST, то есть узел имеет тип дерева.
Тогда систему типов узлов очень нужно понимать, вотОбъяснение системы типов Babel AST. Как видите, можно сказать, что система типов абстрагирует различные члены кода, идентификаторы, литералы, объявления, выражения. Таким образом, древовидная структура этих типов узлов может использоваться для выражения нашего кода.
Что касается системы типов, то после дополнительных экспериментов мы получим общее представление и понимание структуры AST.
Бонус: двигатель V8 также использует AST
Кстати, AST также используется в V8. Двигатель V8 имеет четыре основных модуля:
- Translator Paser: Преобразование исходного кода в AST.
- Интерпретатор: преобразовать AST в байт-код.
- Компилятор: Преобразование байт-кода в ассемблерный код.
- Модуль сборки мусора: отвечает за управление высвобождением памяти.
Видно, что AST также является ключевой частью исполнения V8. Давайте взглянем на использование Babel AST и шаги выполнения.
Шаги обработки Babel
Оглядываясь назад на процесс Бабела
- Разбирать (разбирать). Превратите исходный код в AST.
- Трансформировать. Манипулировать AST, который также является той частью, которой мы можем манипулировать, чтобы изменить код.
- Генерировать (генерировать). Преобразуйте измененный AST обратно в код.
Парсер вавилон
Первый шаг: парсинг, парсер в Бабеле — это вавилон. Давайте испытаем это:
// 安装
npm install --save babylon
// 实验代码
import * as babylon from "babylon";
const code = `const text = 'Hello World';`;
const ast = babylon.parse(code);
console.log('ast', ast);
Переменная code — это наш исходный код, а переменная ast — это AST Давайте посмотрим на результат печати:
Как мы и ожидали, мы получили AST. Здесь я заметил, что есть также поля информации о местоположении, такие как начало, конец и местоположение, которые должны быть полезными для создания исходной карты.
Преобразователь бабел-траверс
Шаг 2: конвертировать. Получил аст, пора эксплуатировать, для этого используется бабел-траверс в Бабеле.
// 安装
npm install --save babel-traverse
// 实验代码
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `const text = 'Hello World';`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
console.log('path', path);
}
})
console.log('ast', ast);
Библиотека babel-traverse предоставляет метод traverse. Первый параметр — ast, второй параметр — объект. Мы написали метод ввода. Параметр метода — путь. Почему не узел? Посмотрим на вывод:
Путь печатается 5 раз, и на асте действительно есть 5 узлов, которые соответствуют. Метод обхода — это метод обхода.Путь инкапсулирует каждый узел, а также предоставляет такие поля, как контейнер и область действия. Предоставьте больше информации об узле, чтобы мы могли лучше управлять узлом.
Выполним операцию переименования переменных:
import * as babylon from "babylon";
import traverse from "babel-traverse";
const code = `const text = 'Hello World';`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (path.node.type === "Identifier"
&& path.node.name === 'text') {
path.node.name = 'alteredText';
}
}
})
console.log('ast', ast);
Посмотрите результат:
Действительно, наш ast был изменен, при этом сгенерированный код ast будет const alteredText = 'Hello World';
вавилон-траверс Лодаша: вавилон-типы
При использовании babel-traverse для работы с AST вы также можете использовать библиотеки инструментов, которые помогут нам писать более лаконичный и эффективный код, вы можете использовать типы babel.
npm install --save babel-types
import * as babylon from "babylon";
import traverse from "babel-traverse";
import * as t from "babel-types";
const code = `const text = 'Hello World';`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
// 使用babel-types
if (t.isIdentifier(path.node, { name: "text" })) {
path.node.name = 'alteredText';
}
}
})
console.log('ast', ast);
Используйте типы babel для достижения той же функциональности, что и в приведенном выше примере, с меньшим количеством кода.
Генератор вавилон-генератор
Шаг 3: Генерация. С манипулируемым ast пришло время сгенерировать новый код. Для этого используется babel-генератор в Babel.
npm install --save babel-generator
// 加入babel-generator
import * as babylon from "babylon";
import traverse from "babel-traverse";
import * as t from "babel-types";
import generate from "babel-generator";
const code = `const text = 'Hello World';`;
const ast = babylon.parse(code);
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "text" })) {
path.node.name = 'alteredText';
}
}
})
const genCode = generate(ast, {}, code);
console.log('genCode', genCode);
Посмотрите распечатку:
хорошо!В поле кода мы видим только что сгенерированный код.
Конечно, есть более подробная информация о четырех упомянутых выше библиотеках, и те, кто заинтересован, могут изучить ее дальше.
Коллекция этих библиотек и есть наш Вавилон.
плагин
Мы заглянули внутрь Babel, и теперь, когда мы выпрыгнули наружу, нам нужно манипулировать AST извне, вместо того, чтобы заходить внутрь Babel для изменения хода.
Для внешнего управления AST требуются плагины. Давайте посмотрим на плагинbabel-plugin-transform-member-expression-literals
После использования вторичного плагина:
// 转换前
obj.const = "isKeyword";
// 转换后
obj["const"] = "isKeyword";
Давайте еще раз взглянем на этот плагинисходный код:
{
name: "transform-member-expression-literals",
visitor: {
MemberExpression: {
exit({ node }) {
const prop = node.property;
if (
!node.computed &&
t.isIdentifier(prop) &&
!t.isValidES3Identifier(prop.name)
) {
// foo.default -> foo["default"]
node.property = t.stringLiteral(prop.name);
node.computed = true;
}
},
},
},
};
}
Здесь я пытаюсь поместить это в наш экспериментальный код следующим образом:
import * as babylon from "babylon";
import traverse from "babel-traverse";
import * as t from "babel-types";
import generate from "babel-generator";
const code = `const obj = {};obj.const = "isKeyword";`;
const ast = babylon.parse(code);
const plugin = {
MemberExpression: {
exit({ node }) {
const prop = node.property;
console.log('node', node);
if (
!node.computed &&
t.isIdentifier(prop)
// !t.isValidES3Identifier(prop.name) 这里注释掉,我们的t里没这个方法
) {
// foo.default -> foo["default"]
node.property = t.stringLiteral(prop.name);
node.computed = true;
}
},
},
};
traverse(ast, plugin)
const genCode = generate(ast, {}, code);
console.log('genCode', genCode);
Выходной код: "const obj = {};obj["const"] = "isKeyword";". Как и ожидалось, плагин Babel будет передан во внутренний метод traverse. И совместим с шаблоном посетителя, что позволяет нам работать с типами узлов (такими как посетитель.MemberExpression здесь). Здесь мы используем выход вместо входа. Чтобы объяснить, обход — это глубокий обход дерева. Когда мы перемещаемся по дереву вниз, мы входим (входим) в каждый узел, а когда мы возвращаемся назад, мы выходим (выходим) из каждого узла. Для AST ход проходится дважды, и мы можем манипулировать узлами при входе или выходе.
заключительные замечания
Моя ссылка:
Вот и все для сегодняшнего исследования, понимания внутреннего устройства Babel и того, как работают основные плагины.