Фреймворки дают клиентский роутинг из коробки. У HTML и Web Components его нет. Если сайт уже отдаёт полные страницы, а клиентская навигация нужна только для части разделов, переписывать всё на фреймворк незачем.

Такая задача возникает в нескольких случаях. На существующем сайте два-три раздела должны переключаться без полной перезагрузки, а остальное по-прежнему жить как обычные HTML-страницы. В отдельном микрофронтенде или встраиваемом разделе не хочется добавлять фреймворк только ради навигации. При постепенном рефакторинге прямые URL должны оставаться полноценными страницами: для SEO, для первого экрана и на случай, если JavaScript не загрузился.

Эту задачу можно решить напрямую через History API, но тогда загрузку HTML, извлечение нужного фрагмента, вложенные макеты и сохранение обычных переходов при отключённом JavaScript придётся предусмотреть самостоятельно. Можно взять мини-роутер с конфигурацией в JavaScript и собственным деревом представлений — это удобно, когда маршруты и представления уже описываются в JavaScript.

Если исходной точкой должен оставаться HTML, который отдаёт сервер, можно использовать Aura Router: маршруты объявляются как Custom Elements, а ссылки остаются обычными <a href>. Без JavaScript те же URL открываются как обычные страницы.

Сделаем сайт из двух страниц. В пустой папке проекта выполните:

npm init -y
npm install --save-exact @auraui/router@0.1.0
npm install --save-dev vite

index.html:

<!doctype html>
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Home</title>
    <script type="module" src="/src/main.js"></script>
  </head>
  <body>
    <nav>
      <a href="/" aura-router-link>Home</a>
      <a href="/about/" aura-router-link>About</a>
    </nav>

    <main id="content">
      <h1>Home</h1>
      <p>Это полная HTML-страница.</p>
    </main>

    <aura-outlet></aura-outlet>

    <aura-router extract="#content">
      <aura-route path="/" view="/"></aura-route>
      <aura-route path="/about/" view="/about/"></aura-route>
    </aura-router>
  </body>
</html>

Ссылки остаются обычными. Атрибут aura-router-link говорит роутеру обработать клик клиентской навигацией, а не полной перезагрузкой страницы. Селектор extract="#content" указывает, какой фрагмент полной HTML-страницы вставлять в <aura-outlet>. При первой загрузке Aura подхватывает уже находящийся в документе #content и не запрашивает текущую страницу повторно. При переходах роутер загружает новую страницу и извлекает из неё фрагмент, соответствующий селектору #content.

Скопируйте index.html в about/index.html. Смените <title> на About и замените блок #content:

<main id="content">
  <h1>About</h1>
  <p>Прямой заход на этот URL тоже отдаёт полную страницу.</p>
</main>

Навигацию, <aura-outlet>, маршруты и скрипт оставьте теми же.

Создайте src/main.js:

import { AuraRouter } from "@auraui/router";

AuraRouter.install();

Это весь JavaScript.

Запуск:

npx vite

Откройте URL, который напечатает Vite, и проверьте три пункта:

  1. Прямой заход на /about/ и перезагрузка страницы — отдаётся полная HTML-страница.

  2. Клики по Home и About — меняются URL и #content, документ не перезагружается.

  3. Отключите JavaScript и пройдите те же ссылки — переходы станут обычными загрузками страниц, но страницы и ссылки продолжат работать.

Без npm тоже можно проверить. Если страницы уже отдаются по HTTP, вместо /src/main.js вставьте в обе страницы:

<script type="module">
  import { AuraRouter } from "https://esm.sh/@auraui/router@0.1.0";
  AuraRouter.install();
</script>

Для продакшена пакет лучше установить из npm и включить в сборку приложения.

Если общий интерфейс не должен размонтироваться при смене дочернего URL, вынесите его в родительский макет (layout):

<template id="workspace-shell">
  <workspace-sidebar></workspace-sidebar>
  <aura-outlet></aura-outlet>
</template>

<aura-route path="/workspace/" layout="workspace-shell">
  <aura-route path="." view="/workspace/"></aura-route>
  <aura-route path="settings" view="/workspace/settings/"></aura-route>
</aura-route>

Пока навигация остаётся внутри /workspace/, родительский макет остаётся смонтированным, меняется только вложенный <aura-outlet>. Прямой заход на вложенный URL должен вернуть родительский макет, вложенный <aura-outlet> и дочерний контент. Aura эту серверную разметку не генерирует.

В результате каждый URL остаётся полноценной HTML-страницей, а при включённом JavaScript переходы становятся клиентскими. Это базовая модель Aura Router; более сложные деревья маршрутов строятся по тому же принципу.

Текущий релиз — 0.1.0, публичный API ещё может измениться до 1.0. Поэтому лучше зафиксировать точную версию.

Примеры и дополнительные материалы: