Как началась история
Каждая история начинается с ошибки ...
В то время как я наслаждался тем, что mangle-props дает нам почти 40% -ную степень сжатия, используя способ, о котором я упоминал в своем последнем сообщении в блоге, все идет хорошо.
Однажды мы решили переместить наши новые компоненты пользовательского интерфейса в React.js, и я понял, что мне нужно скомпилировать синтаксис JSX, прежде чем загружать их в компрессор Terser. Terser не справится с минимизацией синтаксиса JSX, это невозможно. Даже если вы используете плагин babel-minify + syntax-jsx, он имеет ошибку.
Я устал применять этот процесс сборки к нашим веб-приложениям. Странно делиться процессом сборки как инструментом или пакетом, потому что это слишком необычно для людей.
Babel (специальные синтаксисы, например, JSX) → Compress (terser-js) → Babel (остальные плагины)
Вы хотите, чтобы вышеперечисленное стало вашим инструментом для сборки / сборки? Думаю, нет.
Решения
В основном у нас есть несколько вариантов для переноса с ES6 + на ES5:
- Компилятор закрытия
- Желудь
- Вавилон
- Компилятор машинописного текста (tsc)
- Бубле
- … (Есть и другие, но, возможно, не такие популярные)
Давайте проверим их по одному
- Компилятор закрытия
тяжелые Java Depdencies. поддержка mangle-props, хорошо, но я уже стал короче. Нам нужен простой способ, без огромных зависимостей, так что до свидания, сначала закрываем компилятор ...
- Желудь
Это слишком сырой парсер. нам нужно написать логику преобразования с acorn-walk
, babel уже работает с преобразованием, верно? А бабель узнал из желудя, поддержал jsx, неплохо звучит? Далее переходим в babel.
Давай действительно попробуем их
Babel, Typescript и Buble звучат просто, у них есть плагины для бандлера (загрузчик накопительных пакетов / веб-пакетов) или их можно настраивать (ts и buble). Нас легко взломать.
Вавилон
Вкратце расскажем, в чем проблема Babel. Пример ниже довольно прост:
Синтаксис ES6 1: синтаксис класса ES6 2: средство доступа / мутатор свойства
class A { method() { console.log('method'); }
get x() { return 1 } }
const B = { get x() { return 2 } }
Мы ожидали, что транспилятор максимально просто преобразует их в чистый код ES5. Следующий фрагмент является результатом работы babel transpiler с предустановкой ES2015 в свободном режиме.
var A = /*#__PURE__*/ (function() { function A() {}
var _proto = A.prototype;
_proto.method = function method() { console.log("method"); };
_createClass(A, [ { key: "x", get: function get() { return 1; } } ]);
return A; })();
var B = { get x() { return 2; } };
Проблема 1: невозможно транспилировать получатели литерала объекта. Проблема 2: getter цитируется babel в _createClass
helper, что означает, что terser-js (включен mangle-props) в этом случае будет иметь ошибку.
Для проблемы 1: мы могли бы решить ее с помощью плагина @ babel / plugin-transform-property-mutators, что несложно.
Для проблемы 2: как взломать _createClass
помощник? Можем ли мы взломать его до того, как класс трансформируется? Может быть!
Мысли здесь: если бы мы могли переместить логику установки / получения из функции _createClass
, определив их другими способами, такими как (Object.defineProperty), terser-js внесет эти выражения в белый список и не будет искажать их имена. Звучит отлично!
Можем ли мы сделать то же самое с любым плагином? Нет. Хорошо, позволь мне написать один.
Я потратил некоторое время на написание плагина babel для решения проблемы 2, наконец, я сделал babel-plugin-transform-class-property-mutators
С этими плагинами ввод вроде этого
class A {
get x() {
return this._x || 1
}
}
будет выводиться так
Object.defineProperties(A, {
x: {
get: function () {
return A._x || 1;
},
configurable: true,
enumerable: true
}
})
Видеть? getter / setter теперь определены в выражении Object.defineProperties, и это ES5. Выглядит неплохо? Нет, наверное, нет.
Эй, давай подумаем, что мы можем делать внутри методов класса и мутаторов свойств? Класс может быть производным от родителя, не забывайте, что есть ключевое слово super
, а super.xxx
может быть задействовано внутри установщика / получателя.
Затем нам нужно сначала скомпилировать вывод класса! Но у плагинов есть порядок: если вы сначала выполните преобразование класса, все будет сделано, тогда у вас не будет возможности запустить плагин, чтобы что-нибудь взломать.
Грустная история, давай закончим вавилонской беседой.
Машинопись
Станет ли это нашим предпочтением? Типскрипт получил действительно простой вывод с преобразованием класса, я сделал пример как с геттером / сеттером, так и с деривацией. Вот ввод:
пример кода
class P { f() { return 1 } }
class Child extends P { constructor() { super() } get value() { return super.f() } }
let instance = new Child() console.log(instance.value)
Результат выглядит так:
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var P = /** @class */ (function () {
function P() {
}
P.prototype.f = function () {
return 1;
};
return P;
}());
var Child = /** @class */ (function (_super) {
__extends(Child, _super);
function Child() {
return _super.call(this) || this;
}
Object.defineProperty(Child.prototype, "value", {
get: function () {
return _super.prototype.f.call(this);
},
enumerable: true,
configurable: true
});
return Child;
}(P));
var instance = new Child();
console.log(instance.value);
Выглядит отлично, это почти то, что нам нужно! Почти? Не совсем так? Расскажу тебе позже…
Хорошие новости: я нашел несколько плагинов машинописного текста для накопления:
- Https://github.com/ezolenko/rollup-plugin-typescript2
- Https://github.com/rollup/rollup-plugin-typescript
И я интегрирую rollup-plugin-typescript2 с rollup, настраиваю его также для файлов javascript, отключаю некоторые конкретные параметры только для файлов ts (например, создание объявления типов, принудительная проверка типизации…).
Но позже, когда я перешел к их конфигу, обнаружил, что они почти только для файлов машинописного текста. Не только из-за каких-то дефолтных конфигов.
// default config
{
include: ["*.ts+(|x)", "**/*.ts+(|x)"],
// ...
}
Разработчику также будет любопытно и непонятно, почему что-то частично поддерживается машинописным текстом, а не полностью настроено. Вы устанавливаете весь пакет машинописного текста и просто хотите, чтобы он помог вам скомпилировать ES6 js в ES5. Это слишком продумано?
Я удалил машинописный текст, наблюдая за авторскими правами Microsoft в сгенерированных скомпилированных файлах.
Бубле
С помощью нашего примера кода узнайте, что с этим будет делать Bublé. Вы можете поиграть с ним в онлайн-приложении repl: https://buble.surge.sh
Вот так
var P = function P () {};
P.prototype.f = function f () { return 1 };
var Child = (function (P) { function Child() { P.call(this) }
if ( P ) Child.__proto__ = P; Child.prototype = Object.create( P && P.prototype ); Child.prototype.constructor = Child;
var prototypeAccessors = { value: { configurable: true } }; prototypeAccessors.value.get = function () { return P.prototype.f.call(this) };
Object.defineProperties( Child.prototype, prototypeAccessors );
return Child; }(P));
var instance = new Child() console.log(instance.value)
Во-первых: никаких строковых свойств, все, что определено в прототипе или создано definedProperties
, отлично Во-вторых: сгенерированный код класса действительно простой и достаточно короткий, мне нравится prototypeAccessors
Здесь проблем не обнаружено, вывод выполняется правильно, класс ES5 тоже тонкий. Думаю, это окончательный выбор.
История закрыта
В конце концов, я использовал buble в качестве одного из моих накопительных плагинов при создании сборщика пакетов.
В отличие от microbundle, я отключил опасные преобразования buble, просто не хочу ломать код, гоняясь за производительностью.
Вы можете ознакомиться с исходным кодом бандлера здесь: https://github.com/huozhi/bunchee.
исходное сообщение блога на моем сайте: https://huozhi.github.io/post/Comparing-Different-Ways-of-Transpile-ES6-Class-To-ES5