Подумал а вось кому будет интересно порассуждать об особенностях разработки без фреймворков. Только давайте без копетанства мол это не поможет найти работу и всего в таком духе...
Development
24 3 312
29
Raised, не изобретай велосипед, вот это вот отсутствие виртуального дома и всего такого - путь svelte, для полноценных приложений есть sapper (на базе svelte), там SSR поддерживается
21
Есть обновления. Пожалуй, стоит начать с того что при описанном выше подходе помимо роутинга на клиенте была еще одна проблема: пререндеринг на сервере, SEO.
Ну а чтоб решить эту проблему пришлось разобраться с роутингом на сервере. Так как параллельный массив с захардкодженными парами вида {реквест: страница} не достаточно гибок, пришлось подумать что еще можно с этим сделать. В итоге остановился на подходе, аналогичном react-router:
`<main id='content'>
   ${await blog(route(thread, 'blog'))} 
   ${await projects(route(thread, { pathname: 'projects', exact: true, preload: true, split: true }))}
 </main>`
С примера выше понятно что в ход пошли шаблонная строка + асинк/авэйт. Не буду нудить деталями имплементации и как все умно и красиво все сделано, пока что скажу что это работает таким же образом как и реактовский роутер, грубо говоря — динамично и в результате получается древообразная структура.
Осталась проблема синхронизации DOM-референсов сгенерированной страницы с клиентом. Ну а так как виртуальный DOM я не хочу да и вряд ли могу с таким подходом, надо придумать какой-то алгоритм для передачи референсов. Есть одна идея которую буду сегодня тестировать, но она мне кажется немного упоротой. Пример синхронизации хуков клиента с сокетами, сгенерированными на сервере:
import route from './../../router.js';
import client from './../../client.js';

import aboutSomething from './about-something/logic.js';


export default async function blog(thread) {

  if (thread) {

    const id = `node=${thread.node.pathname}`;

    client.node({ thread, name: 'my-bundle-name' }, `
      window.addEventListener('DOMContentLoaded', () => {
        let header = document.querySelector(\`${id}::my-header\`);
      });
    `);

    return `
      <h4 ${id}::my-header>About something</h4>
      ${await aboutSomething(route(thread, 'about-something'))}
    `;
  }

  return '';
}
Метод node, генерирует бандл для клиента, присваевает ему указанное имя или имя, сгенерированное с названия узла и инжектит ссылки на файл в тело страницы.
Все это происходит при запуске сервера. Генерируются отрендерренные версии узлов, генерируются бандлы для узлов и компонентов. Генерируется статичный ассоциативный массив вида {запрос: файл}, и сгенерированные ассеты уже обслуживаются по нему.
Думаю, стояло бы нарисовать схему и углубится в имплементацию, но честно говоря, не думаю что все это будет хоть кому-либо интересно. Юзай фреймворк n, добавь магии в свою жизнь...
21
Пока вижу роутинг одной из ключевых проблем: как правило роутер определяет что рисовать при каком запросе, на время сессии запоминает список доступных* и посещенных/загруженных страниц. С MPA вся логика находится на сервере. Чтоб получить все преимущества SPA (PVA, например) нужен роутер на клиенте.
На всякий случай выведу оперативное определение роутера:
определяет что рисовать при каком запросе, на время сессии запоминает список доступных* и посещенных/загруженных страниц (полу-PVA, хотя полноценный PVA отсюда вывести несложно).
При таком подходе загруженный роутером скрипт инициирует вьюху и делает ее активной/текущей (пряча все остальные). Прирост производительности получается за счет того что уже посещенную страницу можно просто вытащить из списка, вместо отправки очередного запроса на сервер и гуфа единственного контейнера. Ясное дело подразумевается что нужны опции, позволяющие указать стоит ли пытаться подгрузить более актуальную версию вьюхи с сервака, каков должен быть интервал между запросами и т.д.
Есть только одна проблема: в некоторых случаях некоторые части страницы остаются неизменными. Вот представим кейс с information-dense сайдменюхой с вкладками и мейн контейнером со списком статтей для вьюхи. Я позволю себе усомнится в целесообразности перерисовки всей страницы при переключении вкладки => смене роута. Тут нужен рекурсивный подход и чтоб помимо "Что?", роутер определял и "Где?" это рисовать. То есть определение придется расширить и как-то рекурсивно проходить по чему-то при смене роута. Но как и по чему именно я так пока и не додумался...
Постараюсь ка-то прикинуть решение как будет на это время...
31
ты видимо проспал релиз HTTP/2 где большое кол-во маленьких запросов приветствуется
Кем приветствуется? Если там появилось мультиплексирование, то это не значит, что его нужно юзать гденипопадя.


Если бы оно так сильно приветствовалось, то я бы каждый селектор css вынес бы в отдельный файл и грузил бы их скопом, но что-то так никто не делает.
29
NazarPunk, ты видимо проспал релиз HTTP/2 где большое кол-во маленьких запросов приветствуется

То что ты говоришь, это не каждый чих, а разбивка на чанки, чтобы при обновлении кода, обновлялся не весь бандл, а только определенный чанк
31
Один только jquery весит 87 Кб, функций которые ты почти не используешь
Я смотрю просто адские размеры без gzip, что ни один браузер их не потянет,
лучше делать 100500 GET запросов на каждый чих
29
Причём тут первый рендер, если для него нужен только html и css.
ошибаешься, js, картинки и прочее тоже участвуют в процессе первого рендера. Причем для js еще можно указать, чтобы он был исключен из первого рендера и вообще грузился асинхронно без первого приоритета.
NazarPunk:
Префиксы он тоже сам добавляет?
да, посмотри babel
Я при сборке проэкта тоже получаю всего два файла .js и .css, которые успешно попадают в кэш.
не забывай о размере бандла, при чистом виде он будет минимальным. Один только jquery весит 87 Кб, функций которые ты почти не используешь, а мы умещаем в этот размер целое приложение с нехилой функциональностью полноценным дизайном и поддержкой всех не модных брузеров
Чтобы успешно сложить и использовать кеш тебе нужен стабильный интернет и постоянный пользователь. Если рассматривать мобильных юзеров и стандартный поток клиентов на сайт, то твоя теория сильно наворачивается.
31
Ну как сказать... Разница не велика по написанию кода, а просадка по производительности и времени первого рендера и дополнительный сетевой трафик для мобилок обеспечен
Причём тут первый рендер, если для него нужен только html и css.
ты не учел, что в результата компиляции свелт ты получаешь 2 чистых файла (js + css) или 1 (js, стили вшиваются), которые самодостаточные и ничего дополнительного не надо подключать.
Префиксы он тоже сам добавляет? Я при сборке проэкта тоже получаю всего два файла .js и .css, которые успешно попадают в кэш.
29
Обёртки над js всёравно нужно писать ибо таже работа с DOM на чистом js то ещё удовольствие, а единый синтаксис для них это удобно
Ну как сказать... Разница не велика по написанию кода, а просадка по производительности и времени первого рендера и дополнительный сетевой трафик для мобилок обеспечен
// Native JS
const elementsCollection = document.querySelectorAll("button[data-action='show-registration-form']");
elementsCollection.forEach(buttonElement =>
	buttonElement.addEventListener("click", clickEvent => {
		// ....
	}
);
// JQuery
const elementsCollection = $("button[data-action='show-registration-form']");
elementsCollection.on("click", clickEvent => {
	// ...
});
Да и не забывайте, что в ТЗ всегда может быть строчка о поддержке старых браузеров.
Для этого уже давно используются нормальные средства разработки, подключение дополнительного бандла с полифилами и транспайлер в старую версию ES. А потом немного магии с помощью нового nomodule аттрибута, который отключает лишние бандлы для современных браузеров. И все, даже поддержка всратого IE8 обеспечена
посмотрел я на Svelte и вы ещё ругаете jQuery в лишних обёртках
ты не учел, что в результата компиляции свелт ты получаешь 2 чистых файла (js + css) или 1 (js, стили вшиваются), которые самодостаточные и ничего дополнительного не надо подключать.