Все главы
  1. 1 Перед началом
  2. 2 Установка
  3. 3 Страницы
  4. 4 Первый шаблон
  5. 5 Компоненты
  6. 6 Рамка сайта
  7. 7 Поля страниц
  8. 8 Списки страниц
  9. 9 Свои настройки
  10. 10 Заявка на замер
  11. 11 Живые части
  12. 12 В интернет
  13. 13 Что дальше

Компоненты: шапка, меню и подвал

Шапку и подвал удобно держать отдельными кусочками и подключать в любой шаблон. Такие кусочки называются компонентами. Сделаем три: меню, шапку и подвал.

Глава 5 из 13 · 4 минуты чтения

Зачем нужны компоненты

Скоро у сайта появятся другие шаблоны: для кухонь, для каталога, для проектов. Шапка и подвал нужны везде. Если копировать их в каждый шаблон, то, чтобы поменять телефон в подвале, придётся править пять мест и не забыть ни одно.

Компонент решает это: шапка пишется один раз и подключается одной строкой. Поменяли компонент, и все шаблоны получили новую шапку.

Начнём с меню. Оно должно показывать страницы верхнего уровня и само обновляться, когда вы добавляете страницу. Создайте компонент: на вкладке Элементы нажмите + рядом со словом «Компоненты» и напишите имя Nav.

Компонент Nav
<!-- @component
Меню сайта: страницы верхнего уровня.
-->
<script>
  import { children, current, link } from '$core';

  const here = current();
  const items = children(null);
</script>

<nav class="nav">
  {#each items as item (item.id)}
    <a
      href={link(item.id)}
      aria-current={here?.id === item.id ? 'page' : undefined}
    >
      {item.menuTitle || item.title}
    </a>
  {/each}
</nav>

<style>
  .nav {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem 1.25rem;
  }

  .nav a {
    color: inherit;
    text-decoration: none;
  }

  .nav a[aria-current] {
    font-weight: 700;
  }
</style>

Что здесь происходит:

  • <!-- @component … -->: описание компонента. Менеджер покажет его рядом с именем, а вы через месяц вспомните, зачем он нужен.
  • import { children, current, link } from '$core';: берём из $core три функции. $core это модуль, через который шаблоны и компоненты получают данные сайта.
  • children(null): страницы верхнего уровня, в том порядке, как они стоят в дереве. Вместо null можно передать id страницы, и получите её дочерние.
  • link(item.id): адрес страницы по её номеру. Переименуете страницу, и ссылка поменяется сама.
  • current(): страница, которую сейчас смотрит посетитель. Сравнив номера, мы помечаем текущий пункт меню, и он становится жирным.
  • (item.id) после as item: ключ пункта списка. Svelte по нему отличает пункты друг от друга.

Новый компонент Header:

Компонент Header
<!-- @component
Шапка: название сайта и меню.
-->
<script>
  import Nav from '$components/Nav';
  import { site } from '$core';
</script>

<header class="header">
  <a class="logo" href="/">{site().name}</a>
  <Nav />
</header>

<style>
  .header {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    max-width: 48rem;
    margin: 0 auto;
    padding: 1.5rem;
  }

  .logo {
    color: inherit;
    font-size: 1.25rem;
    font-weight: 800;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    text-decoration: none;
  }
</style>

Компонент подключает другой компонент так же, как шаблон: import Nav from '$components/Nav'; и дальше <Nav />. Название сайта берём через site().name: в компонент site не приходит параметром, его спрашивают у $core.

У подвала будет параметр: строка рядом с названием. Так один компонент может выглядеть по-разному в разных шаблонах.

Компонент Footer
<!-- @component
Подвал: название, строка о студии и год.
-->
<script>
  import { site } from '$core';

  let {
    /** Строка рядом с названием */
    note = 'кухни на заказ',
  } = $props();

  const year = new Date().getFullYear();
</script>

<footer class="footer">
  <p><b>{site().name}</b> · {note}</p>
  <p>© {year}</p>
</footer>

<style>
  .footer {
    max-width: 48rem;
    margin: 0 auto;
    padding: 1.5rem;
    color: #6b6862;
    font-size: 0.9rem;
  }
</style>

Параметры компонент получает через $props(), как шаблон получает page. После знака = стоит значение по умолчанию: если параметр не передали, подвал напишет «кухни на заказ». Комментарий /** … */ над параметром менеджер покажет как подсказку.

Собрать шаблон из компонентов

Теперь шаблон Base становится короче. Замените его код целиком:

Шаблон Base
<script>
  import Content from '$core/Content';
  import Footer from '$components/Footer';
  import Header from '$components/Header';

  let { page, site } = $props();
</script>

<svelte:head>
  <title>{page.longTitle || page.title} — {site.name}</title>
</svelte:head>

<Header />

<main>
  <h1>{page.longTitle || page.title}</h1>
  <Content of={page} />
</main>

<Footer note="кухни по вашим размерам" />

<style>
  :global(body) {
    margin: 0;
    background: #f1efeb;
    color: #1f1e1c;
    font: 18px/1.6 system-ui, sans-serif;
  }

  main {
    max-width: 48rem;
    margin: 0 auto;
    padding: 0 1.5rem 2rem;
  }

  h1 {
    font-size: 2.75rem;
    line-height: 1.05;
    letter-spacing: -0.02em;
  }
</style>

Подвалу мы передали свою строку: <Footer note="кухни по вашим размерам" />. Параметры пишутся как атрибуты тега.

Шапка с меню и подвал на сайте студии
Шапка с меню и подвал на сайте студии