Комментарии 32
Должен сказать, что Marko работает действительно очень быстро. Сообщество тоже достаточно отзывчивое и дружелюбное.
Если Хабру интересно, могу написать о Зое отдельную статью.
именно на этой библиотеке построен фронтенд ebay.com…
Так себе рекомендация, если честно. :)
Когда я вынужденно пользуюсь eBay я всё время подмечаю, что это, наверное "отличный" пример тех самых модных "micro-frontends". Кликаешь на пункт меню — грузится 1 frontend, кликаешь на 2-й грузится 2-й, на 3-й — грузится 3-й. Все при этом выглядят по-разному. Как будто на другой сайт попадаешь. И все визуально как-будто родом из 95-го. Про удобство можно даже не заикаться.
Смотря на код из статьи, невозможно не подметить, что каменный век застрял и в коде. Столько ручной работы. Столько нарушений здравого смысла (скажем загрузка users прописанная прямо в router-е). Стили именования переменных (underscore case и всякие ___implicit: true). Ручные вызовы history. Однобуквенные методы. Вшитые строки с тегами. out.w('<p>not found</p>')
. Мои глазаааа…
Мне кажется статья про "фронтенд от ebay.com" должна называться примерно так: "Вредные советы или как мы докатились до такой жизни | eBay".
Дизайн (хуждожественный) это не совсем к коду замечание. Кстати, нам, которые не были с ebay.com с самого основания возможно не понять почему они не стремятся менять дизайн. Возможно это что-то из разряда того чтобы оставаться тем самым старым добрым ebay.com
Marko.js это библиотека в которой нет роутера. Поэтому замечание по загрузке users прописанная прямо в route — это не к ним а ко мне. Код out.w('not found') — это же авто генерируемый код — у webpacka он не намного лучше?
почему они не стремятся менять дизайн
У них их несколько единовременно. Например вот и вот. Даже в рамках основного меню профиля когда кликаешь видно что подключаются разные дизайны, которые пытались причесать так, чтобы было похоже, что этот один и тот же (на палятся в мелочах).
это же авто генерируемый код — у webpacka он не намного лучше?
Вы знаете, я вот перечитал этот абзац. Это предельно не очевидно, что это генерируемый код. Я принял его за ручной. Рекомендую добавить это в текст статьи.
Честно говоря по статье в целом непонятно, что такое Macro, как он работает и нужен ли он в 2020г. Ниша у него, судя по всему, примерно та же что у какой-нибудь связки вроде backbone + handlebars, но тут нужно вникать чтобы сказать точнее.
Добавил в текст фразу про автогенерацию кода. Ниша этогй библиотеки примерна такая же как у react. Однако библиотека не имеет агрессивного PR результате чего о ней практически ничего не известно. Также нет наработок по ней — то чем сейчас силен React, когда на каждый случай есть рекомендации как это делать правильно.
Таки полез в документацию. Да, вы правы, он из ниши React/Vue/Angular. Просто из вашей статьи сложилось строго обратное впечатление (из неё вообще трудно понять что из себя представляет этот фреймворк).
Для тех кому интересна краткая выжимка:
- macro использует virtualDom
- под капотом, судя по всему, observable, сделанные как во Vue2, через get\set-ры
- использует кастомный формат шаблонов, с довольно богатым сахаром (но кажется сильно уступает Vue, хотя есть и свои фишки вроде возможности задать имя тега и какие-то параметры для субкомпонент (не путать с аттрибутами, они тоже есть))
- очень легковесный (обещают 10 KiB gzip)
- использует всякие хаки с
$
символов как и Svelte (но не уверен что для тех же целей) - поддержка IE9
- имеет 3 синтаксиса шаблонов: один похожий на xml, второй похож на pug\jade, 3-й JSX, но как и в случае с Vue — неудобный JSX
- умеет в SSR (кажется это основная фишка фреймворка), причём умеет в stream-ы, поддерживает асинхронность
- так же поддерживает в шаблонах произвольный JS
- есть ahead of time compilation шаблонов
- умеет импортировать только используемые части macro
- местный аналог PropTypes умеет срабатывать в compile-time
- использует свой компилятор
- всегда работает синхронно (React от этого много лет пытаются уйти)
- есть Single-File-Components
- местный аналог state работает shallowly, предполагается что всё изменяется иммутабельно, в общем pure by default философия
- похожая на React модель event-ов — события вешаются на
<body/>
- система обмена событиями (
.emit('string', value)
) как во Vue
Этакая смесь React & Vue. Самая сильная отличительная черта SSR.
apapacy а можно вот написать про все, что описал faiwer ?
А то в текущей статьей больше вопросов, чем ответов, т.к. куча тезисов просто остались нераскрытыми:
- Marko.js неистово быстр
- Marko.js можно считать SSR-first библиотекой
- Всем известно, что node.js не так хорош, если его нагружать сложными расчетами
- Код компонентов интуитивно понятен
Ко всем тезисам базовый вопрос "почему?".
Из статьи это осталось непонятным.
Также непонятно, почему я мог бы задуматься об его использовании.
- Насколько он удобен в работе?
- Какие фишки у него есть, которых нет у других?
- Легко ли его встроить в существующее приложение?
- А что там по типизации?
- А что по тестам?
- А сторибук поддержиается?
По текущей статье я узнал о существовании universal-router и о том, что, возможно marko.js быстро рендерит на сервере. Хотя не он один использует потоковый рендер.
Кто еще использует рендер потоком и без создания dom с последующей сериалищацией в строку или в поток?
Не знаю, сначала бы найти фреймворк, который создает DOM. Вроде никто DOM не создает. VDOM может быть делают, а может и нет. Не углублялся в исходники фреймворков.
Знаю, что Vue умеет в серверные оптимизации рендера, включая рендер статических частей сразу в строку. В собранном вебпаком бандле можно найти код того же уровня, что и в статье — рендер строкой сразу. И потоковый рендер там есть.
Я не утверждал, что кто-то еще использует потоковый рендер без создания DOM (что бы это ни значило). Я утверждал, что потоковый рендер — не изобретение marko.
И то, что заявления вида "Marko.js неистово быстр" ничем не подкреплены.
В отличие от marko.js все популярные фреймворк создают на стороне сервера рантайм dom в котором работают компоненты. Потом это рантайм созданный комптнент сериализует в строку или в поток — в данном случае это не так важно для производительности по сравнению с тем что нет работы с рантаймом dom. Да есть другой вариант — это обычные шаблонизаторы ко оиые также как и marko на сервере рендерят шаблоны без рантайм dom. Они так же быстры поэтому и экономичны. Но они не обеспечивают воссоздание компонентов на клиенте и требуют на клиенте работу с jquery и т.п. marko на сервере работает как обычный шаблонизатор а на клиенте как условно говоря React или Vue. React же и Vue на стороне сервера работают как бы в упрощенной версии браузера, в рантаймее dom. И этим проигрывают в скорости.
Ок, а что делать если для полноценного SSR нужна поддержка асинхронности? (кажется у них есть что-то об этом в документации) Я просто не очень понимаю какую они решают проблему если они сразу склеивают строку.
Потому как в этом случае вы получите либо недо-HTML без всего необходимого. Кому такое нужно? Я как раз недавно выпиливал такой псевдо-SSR. Он создавал кучу проблем и ни одну не решал.
Альтернативный вариант — готовить абсолютно все данные заранее и поддерживать это внутри приложения (какие-нибудь глобальные переменные, руками сформированный context и прочие костыли). Такое часто делают с React + Redux — формируют redux store state заранее. Не самый приятный опыт SSR, честно говоря.
В противовес этому возможен рендер асинхронный, который просто зарезолвит promise тогда, когда приложение скажет "я готово, пакуй меня". А это подразумевает поддержку жизненного цикла компонент. И конечно никакой склейки строчек. Гибкий и мощный подход. Но далёк от вершин производительности.
Я могу ошибаться, но вот именно 3-й путь выглядит круто и аппетитно. А второе и первое это в любом случае боль и страдания и далеко не самая удобная архитектура. И тащить в проект никому неизвестный framework только из-за потоковой склейки html-строки, это такое.
По поводу асинхронного есть тэг await и есть метод renderToString с кэллбэком. Будет ли при этом работать асинхронный рендеринг на сервере я не знаю нужно проверить. Что касается третьего пути то есть такая возможность у библиотеки riot.js у меня есть по этой библиотеке статьи на Хабре и приложение real world.
Но скорость riot.js на сервере в двадцать раз меньше чем реакт. Так как React все же рендерит в строку виртуальный свой dom. И это довольно быстро. А riot.js пользуется сторонней реализацией dom и по сути реализует браузер на стороне node.js. так что в реальной жизни его на сервере не поюзаешь.
Что касается скорости Preact то по замерам разработчиков Marko Preact на сервере почти так же быстры как Marko и в разы обгоняет React уж не знаю в чем причина такого хорошего результата.
А может они (ebay.com) просто не могут с него слезть из-за огромного легаси, и чтобы найти разработчиков себе в команду решили его пиарить, чтобы люди изучали его думая, что он новый и современный, а потом их нанимать?
<--/irony-->
Я об этом фремверке узнал из статьи главного разработчика Svelte. Не уверен что его кто то вообще пиарит. Сам фреймворк более поздний по началу активности чем React и возник как наш ответ React.js то есть пытался сделать то же но лучше. И в части ssr это им явно удалось
Честно говоря, глядя на документацию, мне показалось, что они больше с Vue пытаются конкурировать. Имею ввиду по тому как они у себя всё имплементировали видно куда больше пересечений именно с Vue. Однако фанатам Vue, я полагаю, очень не зайдёт "pure by default".
Или скажем с Preact, который тоже имеет копеечный размер.
И в части ssr это им явно удалось
Тут стоит отметить, что в 1-ую очередь это заслуга самой команды React. Им, кажется, настолько плевать на SSR, что практически всё что угодно, будет лучше чем SSR в React.
Когда-то в не столь бородатые времена (2015-2016) использовал Marko 2-3 для шаблонизации на ноде, важны были возможность выделять кастомные компоненты и максимальная скорость работы.
К примеру, такой вот компонент (template.marko):
<script src="${data.urlRoot}${data.urlScripts}/${data.scriptName}?v=${new Date().getTime()}"></script>
Преобразовывался при первичном рендере в чистую функцию (библиотека создает рядом файл template.marko.js):
function create(__helpers) {
return function render(data, out) {
out.w("<script src=\"" +
escapeXmlAttr(data.urlRoot) +
escapeXmlAttr(data.urlScripts) +
"/" +
escapeXmlAttr(data.scriptName) +
"?v=" +
escapeXmlAttr((new Date()).getTime()) +
"\"></script>");
};
}
(module.exports = require("marko").c(__filename)).c(create);
И это все отдавалось нодой:
app.post('/get_template', function(req, res) {
const data = getTemplateParams(req); // { urlRoot, urlScripts, scriptName }
const templateFile = marko.load(path.resolve('templates/', templateType + '.marko'));
templateFile.renderToString(data, (err, html) => {
res.end(JSON.stringify({ template: html }));
});
});
Благодаря преобразованию шаблонов в чистые функции скорость была просто ошеломительной, фактически соответствуя скорости работы самой ноды. Делал сравнения с несколькими имевшимися тогда шаблонизаторами типа Pug, Jade — разрыв по скорости ответа и потреблению памяти исчислялся десятками раз, а так как проект был хайлоадом, Marko стал спасением. Да и синтаксис довольно приятный, и локализацию легко встроить, и кастомизировать рендеринг не сложно. Это все к вопросу, где он может быть востребован — для render-сервера, который принимает параметры из других сервисов и отдает локализованный шаблонизированный html — подходит отлично. А как фреймворк не пробовал использовать, тут явно другие лидеры
Pug, Jade — разрыв по скорости ответа и потреблению памяти исчислялся десятками раз
Очень странно. Если мне не изменяет память то Jade компилирует всё в обычную конкатенацию строк. Вы случаем не забыли кеш включить? Я понимаю что можно написать более быстрый алгоритм, но как можно у обыкновенной конкатенации строк выиграть в десятки раз. Наверное всё у вас каждый раз была компиляция шаблона с нуля.
Конечно, это с вариантом без кеширования. Не знаю, как сейчас, но тогда мне не удалось подружить jade.compileFile('./templates/foo.jade', { cache: true });
и передачу кастомных параметров на каждый запрос, то есть было впечатление, что с кешем он просто формирует набор готовых строк, и при новых параметрах рендерит заново.
Также первичный рендер был значительно медленнее, поэтому детально разбираться я не стал — возможно, тут мое мнение о Jade некорректное и можно добиться лучших результатов.
Конечно, это с вариантом без кеширования
Ну тогда ничего удивительного :-)
Я думаю причина была в чём-то другом, а не в кастомных параметрах. Возможно вы наткнулись на какой-то хитрый баг. Я лет 7 назад ковырял этот Jade (тогда он ещё так назывался) и у меня таких проблем не было. Рендеринг был молниеносным.
тут мое мнение о Jade некорректное и можно добиться лучших результатов
Полагаю Jade должен быть намного быстрее, чем Marko. Ну просто потому что у него очень тупая архитектура (у Jade). Он компилирует шаблон в js-код который потом eval-ит (единоразово). Сам этот код просто последовательно складывает строки. В общем такой PHP-way. Насколько я могу судить это одновременно и предельно быстрый подход и предельно нерасширяемый.
Мы на одном из текущих проектов вообще сделали ход конём: написали кастомный TSX рендер движок. Который сразу формирует html-строку. Т.е. код шаблонов пишется прямо как React-компоненты. И даже поддерживает под-компоненты. Понятное дело что это всё без хуков и прочих штук. Просто XML в TS с интерполяцией. Но зато получили все фишки IDE/редакторов, которые есть для React. Вывод типов, проверка типов, автокомплит, отсутствие отдельных файлов-шаблонов, подсветка синтаксиса. При желании можно даже какие-нибудь eslint-валидаторы воткнуть.
Скорость тоже на высоком уровне. Но не предельная. Т.е. строка собирается не последовательно, а древовидно. На момент написания этого движка (строк 400) я сильно удивился что существующие готовые решения все кривые и совершенно неюзабельные (например без экраниварония). Но идея лежала на поверхности и взлетела. Теперь планируем использовать это везде где нужен серверный рендеринг. К примеру для писем.
Да, сейчас я тоже бы использовал что-то подобное. Довольно быстро работает связка
babel-plugin-inferno (преобразует jsx в vdom) + inferno-server, который все это в строку рендерит. Тоже ради интереса объединял это дело в простую оптимизированную функцию, чтобы и все фичи jsx, и быстрая скорость работы (хуков в inferno и так нет, так что выпиливать пришлось не много). Если еще отвязать от реактовой специфики, то получится как раз ваш вариант — согласен, что бенефитов от такого подхода масса. Там же и стримовая реализация есть
Там же и стримовая реализация есть
Good!
По сути вариант что я выше описал (с TSX) можно оптимизировать до уровня компиляции в JS-строку и eval с последующим кешем. Вот это уже будет best performance :) Там же можно будет и на чанки дробить, чтобы потоковую обработку у сделать.
Но нам такая производительность сейчас нафиг не нужна, поэтому за пределы идей в голове оно не уйдёт :D
По бенчмаркам которые делали разработчики markojs pug примерно так же быстры как markojs, иногда быстрее. Но jade в несколько раз уступает pug. Наверное там не только название поменялось. Ну и не лишнеиметь в виду что pug еще нуждается в клиентском фреймворк. Чтобы привязывать события и databinding. А markojs имеет все что нужно и довольно быстро работает на клиенте
иметь в виду что pug еще нуждается в клиентском фреймворк. Чтобы привязывать события и databinding
Мне кажется он в целом совсем не предназначен для таких целей.
Я имел в виду что любой статический html сгенерированный любым сервером так или иначе попадает в браузер (в 99% случаев) а там как раз обычно требуется JavaScript. И тут начинаются муки выбора фреймворка который в 99% при таком сочетании (серверный рендеринг) — это jquery с плагинами.
Решения подобные markojs, next/nuxt позволяют все делать в рамках одного фреймворка на сервере и на клиенте…
Marko.js — фронтенд от ebay.com