{#snippet}, {@render} и {@const} или же почему не слоты?Привет, спишь? Я нет, вернулся из магазина с хлебом и
Svelte 😑Бывает ведь такое, что порой мы не только красим кнопки, но еще всякие компоненты делаем гибкие и новомодные? Бывает, конечно
И порой случается так, что для большей гибкости нам приходится использовать дополнительный инструментарий, например
Svelte и Vue для этого предоставляют <slot>Но иногда приходится сталкиваться с тем, что определенный фрагмент разметки повторяется в разных компонентах по дизайну, хотя по факту своих стилей и наполнения он один и тот же, для этого Svelte предоставил нам сниппеты, рендер теги и тег-константу
Но обо всем по порядку, к практике 🍿
🌟Мы, как обычно, делаем запрос на сервер при монтировании компонента, забираем пользователей в переменную users, за сохранность которой отвечает руна
$state, известная нам прежде
<script>
import { onMount } from 'svelte';
let users = $state([]);
onMount(async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
users = await response.json();
});
</script>
{#each users as user (user.id)}
<article class="user">
<div class="user__header">
<span>{user.name}</span> |
<span>{user.email}</span> |
<span>{user.username}</span> |
<span>{user.phone}</span> |
<a href={user.website}>{user.website}</a> |
</div>
<address class="user__address">
<span>{user.address.street} St.</span> |
<span>{user.address.suite} suite</span> |
<span>{user.address.city} city</span> |
<span>{user.address.zipcode}</span> |
<span>{user.address.geo.lat}</span> |
<span>{user.address.geo.lng}</span>
</address>
<div class="user-company">
<span>Company: <b>{user.company.name}</b></span>
<ul class="user-company__list">
<li class="user-company__item">Catch phrase: <i>{user.company.catchPhrase}</i></li>
<li class="user-company__item">BS: <i>{user.company.bs}</i></li>
</ul>
</div>
</article>
<hr />
{/each}
🌟Разбиваем нашу текущую разметку на три сниппета:
userHeader, userAddress и userCompany, каждый из них берет в себя аргументом объект пользователя и забирает нужные поля, которые мы с помощью тега @const спокойно деструктурируем прямо по месту
{#snippet userHeader(user)}
{@const {name, email, username, phone, website} = user}
<div class="user__header">
<span>{user.name}</span> |
<span>{user.email}</span> |
<span>{user.username}</span> |
<span>{user.phone}</span> |
<a href={user.website}>{user.website}</a> |
</div>
{/snippet}
{#snippet userAddress(user)}
{@const { address: { street, suite, city, zipcode, geo } } = user}
<address class="user__address">
<span>{street} St.</span> |
<span>{suite} suite</span> |
<span>{city} city</span> |
<span>{zipcode}</span> |
<span>{geo.lat}</span> |
<span>{geo.lng}</span>
</address>
{/snippet}
{#snippet userCompany(user)}
{@const { company: { name, catchPhrase, bs } } = user}
<div class="user__company user-company">
<span>Company: <b>{name}</b></span>
<ul class="user-company__list">
<li class="user-company__item">Catch phrase: <i>{catchPhrase}</i></li>
<li class="user-company__item">BS: <i>{bs}</i></li>
</ul>
</div>
{/snippet}
🌟После чего создаем компонент
User, который принимает в себя пропсами объект пользователя и имена этих сниппетов, где в итоге с помощью render тега мы выводим все, что прежде организовали:
<script>
const { user, userHeader, userAddress, userCompany } = $props();
</script>
<article class="user">
{@render userHeader(user)}
{@render userAddress(user)}
{@render userCompany(user)}
</article>
🌟И в цикле заменяем все наше полотно одним емким компонентом:
{#each users as user (user.id)}
<User {user} {userHeader} {userAddress} {userCompany} />
<hr />
{/each}
И вот, волки – сыты, овцы – целы, мы – довольны, а все свое мнение всегда можно высказать прямо в комментариях 👆
#svelte