Динамические маршруты

Fresh — это современный фреймворк для создания веб-приложений на JavaScript и TypeScript, ориентированный на серверный рендеринг с минимальной клиентской нагрузкой. Основная цель Fresh — предоставление максимально быстрой загрузки страниц, низкой задержки и удобного подхода к динамическому рендерингу без необходимости сборки большого фронтенд-бандла. Fresh построен на Deno, что обеспечивает интеграцию с современными стандартами веб-разработки и модульной системой ES.


Архитектура Fresh

Fresh использует концепцию islands architecture, при которой статический HTML генерируется на сервере, а интерактивные компоненты (острова) подгружаются только при необходимости. Это позволяет:

  • минимизировать объем загружаемого JavaScript на клиенте;
  • ускорить время первого рендеринга;
  • повысить SEO-защищенность страниц, так как контент сразу доступен в HTML.

Компоненты Fresh написаны с использованием JSX, что делает их похожими на React, но без клиентского виртуального DOM. Они могут быть как статическими, так и динамическими, что особенно важно при работе с маршрутами.


Динамические маршруты

Динамические маршруты в Fresh позволяют создавать страницы с переменными сегментами в URL. Такой подход необходим для отображения данных, зависящих от параметров, например, профилей пользователей, постов или товаров.

Определение динамического маршрута

Файл динамического маршрута создается с использованием квадратных скобок в имени файла. Пример структуры:

routes/
  posts/
    [id].tsx

Здесь [id].tsx соответствует маршруту /posts/:id. При обращении к /posts/123 параметр id будет равен "123".

Получение параметров маршрута

В Fresh параметр динамического сегмента доступен через объект params, передаваемый в функцию handler или компонент страницы:

import { Handlers } from "$fresh/server.ts";

export const handler: Handlers = {
  async GET(req, ctx) {
    const { id } = ctx.params;
    return new Response(`Пост с ID: ${id}`);
  }
};

В JSX-компоненте параметр можно использовать через ctx.params для генерации содержимого страницы:

export default function PostPage({ params }: { params: { id: string } }) {
  return <h1>Пост ID: {params.id}</h1>;
}

Вложенные динамические маршруты

Fresh поддерживает вложенные маршруты с параметрами на любом уровне структуры:

routes/
  users/
    [userId]/
      posts/
        [postId].tsx

Запрос к /users/42/posts/10 предоставит доступ к:

ctx.params.userId // "42"
ctx.params.postId // "10"

Это позволяет строить сложные структуры URL, полностью соответствующие REST-подходу.


Рендеринг и загрузка данных

Для динамических маршрутов важно подгружать данные на сервере. Fresh использует Server-Side Rendering (SSR), поэтому загрузка данных происходит перед генерацией HTML. Пример с API-запросом:

export const handler: Handlers = {
  async GET(_req, ctx) {
    const { id } = ctx.params;
    const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
    const post = await res.json();
    return ctx.render(post);
  }
};

export default function PostPage({ data }: { data: any }) {
  return (
    <div>
      <h1>{data.title}</h1>
      <p>{data.body}</p>
    </div>
  );
}

Такой подход гарантирует, что HTML страницы сразу содержит актуальные данные без дополнительного клиентского рендеринга.


Обработка ошибок и fallback

Динамические маршруты могут включать обработку некорректных параметров или отсутствующих данных. Для этого используется объект ctx.renderNotFound():

export const handler: Handlers = {
  async GET(_req, ctx) {
    const { id } = ctx.params;
    const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
    if (res.status === 404) return ctx.renderNotFound();
    const post = await res.json();
    return ctx.render(post);
  }
};

При вызове ctx.renderNotFound() Fresh автоматически возвращает страницу 404.


Ссылки и навигация

Для динамических маршрутов Fresh предоставляет компонент <A> для клиентской навигации без полной перезагрузки страницы:

import { A } from "$fresh/runtime.ts";

export default function PostsList({ posts }: { posts: any[] }) {
  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>
          <A href={`/posts/${post.id}`}>{post.title}</A>
        </li>
      ))}
    </ul>
  );
}

Компонент <A> автоматически подгружает страницу через JavaScript, поддерживая интерактивность без полного перерендеринга.


Динамические маршруты и SEO

Поскольку Fresh генерирует HTML на сервере, динамические страницы полностью индексируются поисковыми системами. Параметры маршрута используются для генерации уникальных URL и контента, что повышает качество SEO и позволяет строить семантическую структуру сайта без дополнительных клиентских рендеров.


Рекомендации по организации

  • Использовать вложенные маршруты для логической структуры URL;
  • Минимизировать количество динамических сегментов, чтобы избежать сложной маршрутизации;
  • Обрабатывать отсутствующие данные через ctx.renderNotFound();
  • Для интерактивных элементов подключать только необходимые острова, не перегружая страницу.

Динамические маршруты в Fresh обеспечивают гибкий, высокопроизводительный способ создания веб-приложений с серверным рендерингом, поддержкой вложенной структуры и полной интеграцией с современными практиками SEO и оптимизации загрузки.