Svelte
.svelte компонент: script, разметка, стили
Разработчик открывает файл Card.svelte и видит три части в одном месте: вверху JavaScript, посередине разметка, внизу стили. Возникает вопрос из мира React: разве смешивать всё в одном файле - не плохо? В Svelte это устроено так намеренно. Один .svelte файл - это один компонент целиком: логика, разметка и стили лежат рядом, а стили автоматически изолированы от остального приложения. Класс card в одном компоненте не сломает класс card в другом - компилятор позаботится об этом сам.
- Дизайн-системы на Svelte: каждый элемент (кнопка, инпут, модалка) - отдельный .svelte файл со своими стилями
- Компонентные библиотеки вроде Skeleton и Flowbite Svelte распространяют интерфейс именно набором .svelte файлов
- Командная разработка: правка стилей одного компонента не задевает остальные благодаря изоляции
- Документация svelte.dev строит интерактивные примеры из небольших самодостаточных .svelte компонентов
- Перенос компонента в другой проект сводится к копированию одного файла со всем содержимым
Предварительные знания
- HTML и CSS на базовом уровне: теги, классы, селекторы
- JavaScript: объявление переменных и функций, импорт модулей
- Готовый каркас SvelteKit из предыдущего урока
Три части .svelte файла
Файл .svelte состоит максимум из трёх частей, и порядок привычный: сначала тег script с логикой, затем разметка, в конце тег style. Логика - это обычный JavaScript или TypeScript: переменные, функции, импорты. Разметка - почти обычный HTML, в который можно вставлять выражения. Стили в теге style применяются только к этому компоненту. Любую из частей можно опустить.
Фигурные скобки в разметке - это вставка JavaScript-выражения. Запись {name} подставит значение переменной, а внутри атрибута onclick живёт обычная стрелочная функция. Переменная count объявлена через руну `$state`, поэтому при её изменении компилятор обновит соответствующий текст на странице.
- Тег script: логика компонента, выполняется один раз при создании экземпляра
- Разметка: HTML с вставками выражений в фигурных скобках
- Тег style: CSS, по умолчанию изолированный в пределах компонента
Для TypeScript к тегу указывают атрибут lang со значением ts: запись script с lang равным ts включает проверку типов в этом компоненте. Всё остальное в файле работает так же.
Что означают фигурные скобки в разметке .svelte компонента, например запись {name}?
Изолированные стили
Главная особенность тега style в Svelte - изоляция. Селекторы внутри него применяются только к разметке этого компонента, а не ко всей странице. Компилятор находит элементы компонента, добавляет им уникальный класс вида svelte-хэш и дописывает этот класс в селекторы. В результате правило для класса title в одном компоненте никак не заденет такой же класс title в другом.
Оба компонента используют класс box, но стили не перемешиваются: Alert останется красным, Note - синим. Компилятор изолировал каждый набор правил. Это снимает целый класс проблем больших проектов, где глобальные стили незаметно перетирают друг друга.
| Запись | Область действия |
|---|---|
| Селектор в теге style | Только разметка этого компонента |
| :global(селектор) | Выход из изоляции, применяется глобально |
| Стили в src/app.css | Глобально для всего приложения |
Если стиль нужно применить ко всему приложению или к содержимому, пришедшему извне, изоляцию можно отключить через обёртку :global вокруг селектора. Без неё селектор сработает только на собственной разметке компонента, и стиль для дочернего компонента может не примениться.
Почему класс .box в компоненте Alert.svelte не влияет на класс .box в компоненте Note.svelte?
Импорт и использование компонентов
Сила компонентов в том, что один вкладывается в другой. Чтобы использовать компонент, его импортируют как обычный модуль в теге script, а затем вставляют в разметку как тег. Имя тега компонента пишется с заглавной буквы - так Svelte отличает компонент от обычного HTML-элемента вроде div или p.
Содержимое между открывающим и закрывающим тегами компонента - это его дети. В примере заголовок, абзац и кнопка переданы внутрь Card. Чтобы Card отобразил эту вложенную разметку, он использует сниппеты и команду рендера, о которых речь в уроках про шаблоны и props. Пока важно одно: компоненты вкладываются друг в друга так же естественно, как обычные теги.
- Импортировать компонент в теге script указанием пути к .svelte файлу
- Вставить его в разметку как тег с заглавной буквы
- При необходимости передать данные через атрибуты-props и вложенную разметку
Имя при импорте задаёт разработчик, и оно же становится именем тега. Импорт компонента под именем Card даёт тег Card, импорт под именем ProductCard - тег ProductCard. Имя файла и имя при импорте не обязаны совпадать, но совпадение упрощает чтение кода.
Почему имя компонента в разметке пишут с заглавной буквы, например Card, а не card?
Связь с другими темами
Компонент - базовый кирпич Svelte. Дальше курс учит наполнять и связывать кирпичи:
- Структура проекта SvelteKit — Компоненты живут в src/lib и src/routes. Без каркаса некуда их класть
- Синтаксис шаблонов — Разметка компонента оживает через выражения, условия и циклы
- Входные параметры через `$props` — Чтобы компонент принимал данные снаружи, ему нужны props
Итог
- Файл .svelte - это один компонент: блок script с логикой, разметка как обычный HTML и тег style со стилями
- Все три части необязательны: компонент может состоять только из разметки или только из логики и разметки
- Стили внутри тега style автоматически изолированы (scoped): они применяются только к разметке этого компонента
- Компилятор добавляет уникальный класс к элементам компонента, поэтому одинаковые имена классов в разных файлах не конфликтуют
- Компонент подключается обычным импортом и используется как тег с заглавной буквы внутри разметки другого компонента
Связанные уроки
- sv-02-setup-sveltekit — Компоненты живут внутри каркаса проекта, поэтому сначала нужно понять структуру SvelteKit
- sv-04-template-syntax — Разметка компонента наполняется выражениями, условиями и циклами - это следующий урок
- sv-09-props — Чтобы передавать данные в компонент, нужны входные параметры через `$props`