Formatting с Deno

Для начала работы с Fresh требуется установленный Deno версии 1.36 или выше. Fresh использует встроенные возможности Deno, такие как импорт модулей по URL и встроенный TypeScript, что позволяет обойтись без дополнительной сборки. Создание нового проекта выполняется через команду:

deno run -A https://fresh.deno.dev my-app

Эта команда создаёт структуру проекта с базовыми директориями: routes, components, static и конфигурационным файлом deno.json.

Ключевой момент — Fresh полностью ориентирован на серверный рендеринг с автоматическим генератором маршрутов. Любой файл .tsx в папке routes автоматически становится маршрутом.


Форматирование страниц и компонентов

Fresh использует JSX/TSX синтаксис для описания компонентов. Форматирование кода и компонентов играет критическую роль для поддерживаемости и читаемости проекта.

Пример базового компонента:

export default function Home() {
  return (
    <main class="p-4 max-w-2xl mx-auto">
      <h1 class="text-3xl font-bold mb-6">Привет, Fresh!</h1>
      <p class="text-gray-700">Это серверно-рендеринговое приложение с Deno.</p>
    </main>
  );
}

Ключевые моменты форматирования:

  • JSX-теги выравниваются по вложенности. Каждое вложение получает новый уровень отступа.
  • Атрибуты пишутся в одну строку, если их количество небольшое. Для длинных списков атрибутов используют многострочный формат:
<Component
  propOne="value"
  propTwo={true}
  propThree={someFunction()}
/>
  • CSS-классы с Tailwind рекомендуется группировать по смыслу: от общих стилей контейнера к специфическим элементам.

Работа с маршрутизацией и динамическими параметрами

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

routes/
 └─ blog/
     └─ [id].tsx

Внутри компонента доступ к параметрам осуществляется через объект params:

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

export const handler: Handlers = {
  GET(req, ctx) {
    const { id } = ctx.params;
    return ctx.render({ postId: id });
  },
};

export default function BlogPost({ data }: { data: { postId: string } }) {
  return <div>Пост с ID: {data.postId}</div>;
}

Форматирование кода в таких случаях требует четкого разделения логики обработки запроса и рендеринга компонента. Логика должна быть максимально компактной, чтобы JSX оставался легко читаемым.


Использование встроенного CSS и Tailwind

Fresh автоматически поддерживает Tailwind через конфигурацию tailwind.config.js. Для больших страниц рекомендуется использовать:

  • Разделение CSS по компонентам: каждый компонент импортирует только необходимые классы.
  • Псевдоклассы: для hover, focus и responsive-стилей использовать встроенные утилиты Tailwind.
<button class="bg-blue-500 hover:bg-blue-600 text-white font-semibold py-2 px-4 rounded">
  Нажми меня
</button>

Фокус на читаемость: длинные цепочки классов выравниваются по вертикали при превышении 80–100 символов:

<div class="
  p-6
  max-w-lg
  mx-auto
  bg-white
  shadow-md
  rounded-lg
">
  Контент
</div>

Форматирование серверной логики

Handlers в Fresh должны быть компактными и читаемыми. Структура:

  1. Импорт зависимостей
  2. Определение обработчиков GET, POST, PUT
  3. Рендеринг компонента через ctx.render
import { Handlers } from "$fresh/server.ts";

export const handler: Handlers = {
  GET(req, ctx) {
    const data = fetchData();
    return ctx.render({ data });
  },
};

function fetchData() {
  return [
    { id: 1, title: "Первый элемент" },
    { id: 2, title: "Второй элемент" },
  ];
}

export default function List({ data }: { data: { id: number; title: string }[] }) {
  return (
    <ul>
      {data.map(item => (
        <li key={item.id}>{item.title}</li>
      ))}
    </ul>
  );
}

Рекомендации по форматированию:

  • Использовать стрелочные функции для небольших обработчиков.
  • Разделять серверную логику и JSX компонент пустой строкой для визуальной структуры.
  • Map и другие массивные операции выравнивать с отступом для каждого вложенного элемента.

Организация проекта и структурное форматирование

Для больших приложений важно поддерживать единый стиль:

  • routes/ — все страницы и динамические маршруты.
  • components/ — переиспользуемые компоненты, с отдельными файлами для сложных UI-элементов.
  • islands/ — интерактивные элементы, рендерящиеся на клиенте.
  • static/ — статические ресурсы (изображения, favicon).
  • deno.json — конфигурация проекта, включая import maps и разрешения.

Следует поддерживать единую систему именования файлов и компонентов, использовать PascalCase для компонентов и camelCase для переменных и функций.


Работа с интерактивными островами (Islands)

Islands — ключевая особенность Fresh. Они позволяют подключать интерактивный JavaScript к серверно-рендеренным страницам без полного SPA:

import Counter from "../islands/Counter.tsx";

export default function Page() {
  return (
    <main class="p-4">
      <h1>Счётчик</h1>
      <Counter start={0} />
    </main>
  );
}

Форматирование Islands:

  • Каждый интерактивный компонент изолирован в отдельный файл.
  • Начальное состояние передается через пропсы.
  • Внутренние функции и обработчики событий выравниваются по вложенности, JSX в компоненте читается сверху вниз.
export default function Counter({ start }: { start: number }) {
  const [count, setCount] = useState(start);
  
  return (
    <button
      class="px-4 py-2 bg-green-500 text-white rounded"
      onCl ick={() => setCount(count + 1)}
    >
      {count}
    </button>
  );
}

Итоговая структура форматирования

  • Четкая вложенность JSX для читаемости.
  • Минимизация логики внутри JSX: вычисления и фильтры выносить в отдельные функции.
  • Единый стиль именования и организации файлов.
  • Использование Tailwind для быстрого стилизованного форматирования.
  • Отделение серверной логики (Handlers) от представления (JSX/TSX).

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