предисловие
[Серия практических занятий] в основном позволяет нам углубить наше понимание некоторых принципов посредством практики.
[Практическая серия] Маршрутизация внешнего интерфейса
Заинтересованные студенты могут подписаться[Серия упражнений]. Попросите звездочку и попросите подписаться~
Что такое Вавилон? Почему мы должны это понимать?
1. Что такое вавилон?
Babel — это компилятор JavaScript. Он компилирует последнюю версию javascript в версию, которую можно выполнить в данный момент.Короче говоря, использование babel позволяет нам свободно использовать этот новый и последний синтаксис es6 и даже es7 в текущем проекте.
Чтобы иметь возможность программировать с помощью прекрасного ES678910, мы должны его понять!
2. Надежные инструменты требуют огромных усилий
August 27, 2018 by Henry Zhu
Спустя 2 года, 4000 коммитов, более 50 предварительных релизов и большой помощи сообщества, мы рады объявить о выпуске Babel 7. Прошло почти три года с момента выхода Babel 6! Во время релиза предстоит выполнить много работы по миграции, поэтому, пожалуйста, свяжитесь с нами в течение первой недели релиза. Babel 7 — это огромное обновление: мы ускорили его компиляцию и создали инструменты обновления, поддержку конфигурации JS, поддержку «переопределений» конфигурации, дополнительные параметры размера/минификации, поддержку фрагментов JSX, поддержку TypeScript, поддержку новых предложений. и т.д. ждите!
Команда разработчиков Babel так много работала, чтобы внести свой вклад в открытый исходный код и предоставить нам, разработчикам, более совершенные инструменты, почему бы нам не узнать об этом?
(ОС: Пожалуйста, не меняйтесь. Я не могу научиться~)
3. Роль Бабеля
August 27, 2018 by Henry Zhu
Я хотел бы начать эту статью с пересмотра роли Babel в экосистеме JavaScript за последние несколько лет.
Изначально JavaScript отличался от серверного языка и не мог гарантировать одинаковую поддержку для каждого пользователя, поскольку пользователи могли использовать браузеры с разной степенью поддержки (особенно более старые версии Internet Explorer). Если бы разработчик захотел использовать новый синтаксис (например, class A {}), пользователи старых браузеров просто получили бы пустой экран из-за SyntaxError.
Babel предоставляет разработчикам возможность использовать новейший синтаксис JavaScript, не беспокоясь об обратной совместимости, например (class A {} переводится как var A = function A() {}).
Поскольку он может транспилировать код JavaScript, его также можно использовать для реализации новых функций: таким образом, он стал мостом, помогающим TC39 (комитету, разработавшему грамматику JavaScript) получать отзывы о предложениях JavaScript и позволять сообществу высказывать свое мнение о будущем. развитие языка.
Сегодня Babel является основой разработки JavaScript. В настоящее время GitHub имеет более 1,3 миллиона репозиториев, использующих Babel, 17 миллионов загрузок npm в месяц и сотни пользователей, включая многие основные фреймворки (React, Vue, Ember, Polymer) и известные компании (Facebook, Netflix, Airbnb) и т. д. . Он стал основой разработки JavaScript, и многие люди даже не подозревают, что он используется. Даже если вы не используете его сами, скорее всего, ваши зависимости используют Babel.
Даже если вы не используете его сами, скорее всего, ваши зависимости используют Babel. Ты боишься? Ты понимаешь?
Как работает Вавилон
1. Анализ
Этап синтаксического анализа получает код и выводит AST. Этот шаг делится на два этапа: лексический анализ и синтаксический анализ.
1. Лексический анализ
На этапе лексического анализа код в виде строки преобразуется в поток токенов.
Вы можете думать о токене как о плоском массиве фрагментов синтаксиса:
n * n;
[
{ type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
{ type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
{ type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
...
]
Каждый тип имеет набор свойств, описывающих токен:
{
type: {
label: 'name',
keyword: undefined,
beforeExpr: false,
startsExpr: true,
rightAssociative: false,
isLoop: false,
isAssign: false,
prefix: false,
postfix: false,
binop: null,
updateContext: null
},
...
}
Как и узлы AST, они также имеют свойства start, end и loc.
2. Синтаксический анализ
На этапе синтаксического анализа поток токенов преобразуется в форму AST. На этом этапе информация в токенах используется для преобразования их в структуру представления AST, что упрощает последующие операции.
Проще говоря, фаза синтаксического анализа
code(字符串形式代码) -> tokens(令牌流) -> AST(抽象语法树)
Babel использует @babel/parser для анализа кода, а входная строка кода js генерирует AST (абстрактное синтаксическое дерево) в соответствии со спецификацией ESTree. Парсер, используемый Babel,babylon.
2. Конвертировать
Шаг преобразования берет AST и пересекает его, добавляя, обновляя и удаляя узлы по пути. Это самый сложный процесс в Babel или любом другом компиляторе.
Babel предоставляет метод @babel/traverse (обход) для поддержания общего состояния дерева AST и может завершить его замену, удаление или добавление узлов.Параметры этого метода — исходный AST и пользовательские правила преобразования, а возвращаемые результатом является трансформация после AST.
3. Сгенерировать
На этапе генерации кода окончательный (после ряда преобразований) AST преобразуется в код в строковой форме, а также создаются исходные карты.
Генерация кода на самом деле довольно проста: сначала пройдите весь AST в глубину и создайте строку, которая может представлять преобразованный код.
Babel использует @babel/generator для преобразования модифицированного AST в код Процесс генерации можно настроить на сжатие и удаление комментариев, а также поддерживает sourceMap.
Практические предпосылки
Прежде чем это сделать, вы должны иметь базовое представление о Babel.Давайте кратко разберемся в некоторых вещах о Babel, чтобы облегчить разработку плагинов позже.
babel-core
Babel-core — это основной пакет Babel, в котором хранится множество основных API, здесь мы говорим о преобразовании.
transform : используется для транскодирования строк для получения AST.
//安装
npm install babel-core -D;
import babel from 'babel-core';
/*
* @param {string} code 要转译的代码字符串
* @param {object} options 可选,配置项
* @return {object}
*/
babel.transform(code:String,options?: Object)
//返回一个对象(主要包括三个部分):
{
generated code, //生成码
sources map, //源映射
AST //即abstract syntax tree,抽象语法树
}
babel-types
Модуль Babel Types — это библиотека инструментов в стиле Lodash для узлов AST, включающая методы построения, проверки и преобразования узлов AST. Библиотека инструментов содержит продуманные методы инструментов, которые полезны для написания логики для обработки AST.портал
npm install babel-types -D;
import traverse from "babel-traverse";
import * as t from "babel-types";
traverse(ast, {
enter(path) {
if (t.isIdentifier(path.node, { name: "n" })) {
path.node.name = "x";
}
}
});
JS CODE -> AST
Просмотрите древовидную структуру AST, соответствующую коду
Посетители
Когда мы говорим о «входе» в узел, мы на самом деле подразумеваем, что мы посещаем их, и мы используем этот термин из-за концепции шаблона посещения.
Посетитель — это межъязыковая схема для обхода AST. Проще говоря, они представляют собой объект, определяющий методы извлечения определенных узлов в древовидной структуре. Это немного абстрактно, поэтому давайте рассмотрим пример.
const MyVisitor = {
Identifier() {
console.log("Called!");
}
};
// 你也可以先创建一个访问者对象,并在稍后给它添加方法。
let visitor = {};
visitor.MemberExpression = function() {};
visitor.FunctionDeclaration = function() {}
Примечание: Identifier() { ... } является сокращением от Identifier: { enter() { ... } }
Это простой посетитель, который при обходе вызывает метод Identifier() всякий раз, когда в дереве встречается идентификатор.
Пути
AST обычно имеют много узлов, так как же узлы напрямую связаны друг с другом? Мы можем использовать огромный изменяемый объект, которым можно манипулировать и получать к нему доступ, чтобы представлять отношения между узлами, или мы можем использовать Paths (пути), чтобы упростить это дело.
Путь — это объект, представляющий соединение между двумя узлами.
В некотором смысле путь — это реактивное реактивное представление позиции узла в дереве и различной информации об этом узле. Когда вы вызываете метод, изменяющий дерево, информация о пути также обновляется. Babel управляет всем этим за вас, делая операции с узлами максимально простыми и без сохранения состояния.
Пути в посетителях
Когда у вас есть посетитель метода-члена Identifier(), вы фактически посещаете путь, а не узел. Таким образом, вы манипулируете реактивным представлением узла, а не самим узлом.
const MyVisitor = {
Identifier(path) {
console.log("Visiting: " + path.node.name);
}
};
Правила плагина Babel
Подключаемый модуль Babel требует, чтобы мы предоставили функцию, которая возвращает объект посетителя.
//函数参数接受整个Babel对象,这里将它进行解构获取babel-types模块,用来操作AST。
module.exports = function({types:t}){
return {
visitor:{
}
}
}
Получить Babel ... плагин !!!
做一个简单的ES6转ES3插件:
1. let,const 声明 -> var 声明
2. 箭头函数 -> 普通函数
файловая структура
|-- index.js 程序入口
|-- plugin.js 插件实现
|-- before.js 转化前代码
|-- after.js 转化后代码
|-- package.json
Во-первых, давайте создадим package.json.
npm init
package.json
{
"name": "babelplugin",
"version": "1.0.0",
"description": "create babel plugin",
"main": "index.js",
"scripts": {
"babel": "node ./index.js"
},
"author": "webfansplz",
"license": "MIT",
"devDependencies": {
"@babel/core": "^7.2.2"
}
}
Как видите, сначала мы загрузили @babel/core в качестве нашей зависимости для разработки, а затем настроили npm run babel в качестве команды разработки.
index.js
const { transform } = require('@babel/core');
const fs = require('fs');
//读取需要转换的js字符串
const before = fs.readFileSync('./before.js', 'utf8');
//使用babel-core的transform API 和插件进行字符串->AST转化。
const res = transform(`${before}`, {
plugins: [require('./plugin')]
});
// 存在after.js删除
fs.existsSync('./after.js') && fs.unlinkSync('./after.js');
// 写入转化后的结果到after.js
fs.writeFileSync('./after.js', res.code, 'utf8');
Давайте сначала реализуем функцию 1. let, объявление const -> объявление var
let code = 1;
мы проходимпорталУбедитесь, что структура AST, соответствующая приведенному выше коду,
Мы видим, что этот оператор находится в узле VariableDeclaration. Далее нам нужно работать только с атрибутом kind, соответствующим узлу VariableDeclaration~
before.js
const a = 123;
let b = 456;
plugin.js
module.exports = function({ types: t }) {
return {
//访问者
visitor: {
//我们需要操作的访问者方法(节点)
VariableDeclaration(path) {
//该路径对应的节点
const node = path.node;
//判断节点kind属性是let或者const,转化为var
['let', 'const'].includes(node.kind) && (node.kind = 'var');
}
}
};
};
хорошо~ посмотрим на эффект!
npm run babel
after.js
var a = 123;
var b = 456;
Правильно, все!!!Выполняется функция 1, затем реализуется функция 2. Стрелочная функция -> обычная функция (этот пункт пока рассматривать не будем~)
Давайте сначала посмотрим, какой узел соответствует стрелочной функции?
let add = (x, y) => {
return x + y;
};
мы проходимпорталУбедитесь, что структура AST, соответствующая приведенному выше коду,
Мы видим, что узлом, соответствующим стрелочной функции, является ArrowFunctionExpression.
Далее давайте посмотрим, какие узлы соответствуют обычным функциям?
let add = function(x, y){
return x + y;
};
мы проходимпорталУбедитесь, что структура AST, соответствующая приведенному выше коду,
Мы видим, что узел, соответствующий нормальной функции, — это FunctionExpression.
Таким образом, наша идея реализации требует только замены узла (ArrowFunctionExpression->FunctionExpression).
plugin.js
module.exports = function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
const node = path.node;
['let', 'const'].includes(node.kind) && (node.kind = 'var');
},
//箭头函数对应的访问者方法(节点)
ArrowFunctionExpression(path) {
//该路径对应的节点信息
let { id, params, body, generator, async } = path.node;
//进行节点替换 (arrowFunctionExpression->functionExpression)
path.replaceWith(t.functionExpression(id, params, body, generator, async));
}
}
};
};
полный волнения
npm run babel
after.js
var add = function (x, y) {
return x + y;
};
Удивлен или нет?Удивлен или нет?Думаете, это конец?Значит, вы слишком молоды.
Мы часто пишем такие стрелочные функции, чтобы опустить возврат.
let add = (x,y) =>x + y;
Давайте попробуем это, чтобы увидеть, можно ли его избежать
npm run babel
GG: Консоль красная~
Ниже я непосредственно вставлю окончательную реализацию.По определенным причинам, я думаю, что читателям будет интереснее изучить их самостоятельно~
plugin.js
module.exports = function({ types: t }) {
return {
visitor: {
VariableDeclaration(path) {
const node = path.node;
['let', 'const'].includes(node.kind) && (node.kind = 'var');
},
ArrowFunctionExpression(path) {
let { id, params, body, generator, async } = path.node;
//箭头函数我们会简写{return a+b} 为 a+b
if (!t.isBlockStatement(body)) {
const node = t.returnStatement(body);
body = t.blockStatement([node]);
}
path.replaceWith(t.functionExpression(id, params, body, generator, async));
}
}
};
};
ты закончил
Если вы считаете, что это вам поможет, поставьте звездочку или подпишитесь, чтобы поддержать автора~ В будущем будет много галантерейных товаров!!!