Svelte

Form actions и прогрессивное улучшение

Форма входа должна работать даже у того, у кого JavaScript выключен или ещё не загрузился по медленной сети. Классический подход React-приложения - перехватить submit на клиенте и отправить fetch - ломается ровно в этот момент: без JS кнопка ничего не делает. SvelteKit строит наоборот. Сначала обычная HTML-форма с method POST, которую браузер умеет отправлять сам. Сервер обрабатывает её в action. А затем, если JS есть, одна директива use:enhance превращает ту же форму в плавную отправку без перезагрузки страницы.

  • Форма входа: работает и при отключённом JS, и плавно с use:enhance, когда скрипты загрузились
  • Корзина магазина: кнопки добавить и удалить - именованные actions на одной странице товара
  • Комментарии под статьёй: ошибки валидации возвращаются через fail и отображаются без потери введённого текста
  • Настройки профиля: форма сохранения через прогрессивное улучшение, индикатор отправки во время запроса
  • GitHub и многие SSR-сайты: критичные формы остаются нативными POST, так они переживают сбой клиентского JS

Предварительные знания

  • Файловый роутинг и роль +page.server.js в SvelteKit
  • HTML-формы: атрибуты method, action, name у полей ввода
  • Понятие HTTP POST и того, что браузер умеет отправлять форму без JavaScript

Action и обычная HTML-форма

Action это серверная функция для обработки отправленной формы. Она объявляется в +page.server.js внутри экспортируемого объекта actions. Форма с одним обработчиком использует ключ default. Поля формы читаются на сервере через request.formData(), а результат action возвращается странице как проп form.

Ключевая деталь: эта форма работает без единой строки клиентского JavaScript. Атрибут method="POST" говорит браузеру отправить данные нативно, сервер выполнит action и вернёт страницу заново уже с заполненным form. Имена полей (name="email") обязательны - именно по ним formData находит значения.

Функция fail из @sveltejs/kit возвращает ошибку валидации с HTTP-статусом и объектом, который попадёт в проп form. В отличие от error(), fail не уводит на страницу ошибки, а возвращает пользователя к той же форме с введёнными данными и подсказкой, что поправить.

Почему форма с method="POST" и action в +page.server.js работает даже при отключённом JavaScript?

Именованные actions: несколько форм на странице

Когда на одной странице несколько форм - добавить в избранное, удалить, обновить количество - default не хватает: непонятно, какая форма сработала. Тогда каждому обработчику дают имя в объекте actions, а форма указывает, какой action вызвать, через action="?/имя".

Именованный action и default нельзя смешивать в одном объекте actions: либо одна форма с default, либо набор именованных. Попытка держать и default, и именованные приводит к ошибке при сборке.

Префикс ?/ в action="?/add" - не случайный синтаксис. SvelteKit кодирует выбранный action в строке запроса, поэтому даже без JavaScript браузер отправит POST на URL с ?/add, и сервер поймёт, какой обработчик звать. Скрытое поле hidden передаёт id товара, которого нет в видимых полях.

На странице корзины две формы: добавить и удалить товар. Как сервер различит, какую именно отправили?

use:enhance: то же самое, но без перезагрузки

Нативная отправка формы перезагружает страницу. Это надёжно, но не плавно. Директива use:enhance из `$app/forms` перехватывает отправку, когда JavaScript доступен, шлёт запрос в фоне и сама обновляет проп form и перезапускает load - всё без полной перезагрузки. Если JS не загрузился, форма всё равно работает как обычный POST. Это и есть прогрессивное улучшение: базовая функциональность сначала, плавность сверху.

Функция-аргумент use:enhance вызывается перед отправкой - удобно выставить индикатор загрузки. Она может вернуть колбэк, который сработает с ответом сервера. Вызов update() применяет стандартное поведение: обновляет form и перезапускает load. Без своего колбэка use:enhance делает это сам по умолчанию, так что в простом случае хватает голого use:enhance без аргументов.

  • Без use:enhance — Браузер делает полный POST и перезагружает страницу. Работает всегда, но мигает и теряет позицию прокрутки. Базовый, надёжный уровень
  • С use:enhance — Отправка идёт в фоне, страница не перезагружается, form и load обновляются точечно. Требует JS, но даёт плавность поверх рабочей базы

Порядок разработки имеет смысл такой: сначала форма работает как нативный POST с серверным action, потом сверху навешивается use:enhance. Тогда даже при сбое клиентского JS пользователь всё ещё может отправить форму, а не упирается в мёртвую кнопку.

Что делает директива use:enhance, добавленная к рабочей форме с серверным action?

Связь с другими темами

Form actions - стандартный способ записи данных со страницы. Рядом стоят:

  • Load-функции — Чтение данных страницы; после успешного action SvelteKit перезапускает load, чтобы показать свежее состояние
  • API-эндпоинты +server.js — Альтернатива для записи, когда нужен программный JSON-API, а не привязанная к странице форма
  • Hooks — Action читает сессию и пользователя из event.locals, которые заполняет серверный handle

Итог

  • Actions объявляются в объекте actions внутри +page.server.js и обрабатывают POST-запросы формы на сервере
  • Одна форма без имени попадает в action default; несколько форм на странице различают именованными actions через action="?/имя"
  • Данные формы читаются через await request.formData(); ошибки валидации возвращают через fail(status, { ... })
  • Без JavaScript форма работает как обычный HTML POST: браузер сам отправляет данные и получает ответ
  • Директива use:enhance перехватывает отправку на клиенте, делает её без перезагрузки и сама обновляет проп form и данные load

Связанные уроки

  • sv-19-load-functions — Load читает данные для страницы, actions их записывают. Это две половины одного цикла на маршруте
  • sv-21-server-routes — Когда запись данных нужна не из формы, а как программный API для разных клиентов, вместо action берут +server.js
  • sv-22-hooks — Action часто проверяет сессию через event.locals, которые наполняет handle в hooks.server.js
Form actions и прогрессивное улучшение

0

1

Войти