$props
Входные данные компонента называются props, что является сокращением от properties. Вы передаете пропсы компонентам так же, как передаете атрибуты элементам:
// App.svelte
<script>
import MyComponent from './MyComponent.svelte';
</script>
<MyComponent adjective="cool" />
// App.svelte
<script lang="ts">
import MyComponent from './MyComponent.svelte';
</script>
<MyComponent adjective="cool" />
С другой стороны, внутри MyComponent.svelte
мы можем получать пропсы с помощью руны $props
…
// MyComponent.svelte
<script>
let props = $props();
</script>
<p>этот компонент — {props.adjective}</p>
// MyComponent.svelte
<script lang="ts">
let props = $props();
</script>
<p>этот компонент — {props.adjective}</p>
…хотя чаще всего вы будете деструктурировать ваши пропсы:
// MyComponent.svelte
<script>
let { adjective } = $props();
</script>
<p>этот компонент — {adjective}</p>
// MyComponent.svelte
<script lang="ts">
let { adjective } = $props();
</script>
<p>этот компонент — {adjective}</p>
Значения по умолчанию
Деструктуризация позволяет нам объявлять значения по умолчанию, которые используются, если родительский компонент не устанавливает данный пропс:
let { adjective = 'happy' } = $props();
Переименование пропсов
Мы также можем использовать деструктурирующее присваивание для переименования пропсов, что необходимо, если они являются недопустимыми идентификаторами или ключевым словом JavaScript, таким как super
:
let { super: trouper = 'свет меня найдёт' } = $props();
Остаточные пропсы
Наконец, мы можем использовать остаточное свойство (rest property), чтобы получить все остальные, необъявленные, пропсы:
let { a, b, c, ...others } = $props();
Обновление пропсов
Ссылки на пропс внутри компонента обновляются, когда сам пропс обновляется — когда count
изменяется в App.svelte
, он также изменится внутри Child.svelte
. Однако дочерний компонент может временно переопределить значение пропса, что может быть полезно для несохранённого эфемерного состояния (демонстрация):
// App.svelte
<script>
import Child from './Child.svelte';
let count = $state(0);
</script>
<button onclick={() => (count += 1)}>
нажатий (parent): {count}
</button>
<Child {count} />
// App.svelte
<script lang="ts">
import Child from './Child.svelte';
let count = $state(0);
</script>
<button onclick={() => (count += 1)}>
нажатий (parent): {count}
</button>
<Child {count} />
// Child.svelte
<script>
let { count } = $props();
</script>
<button onclick={() => (count += 1)}>
нажатий (child): {count}
</button>
// Child.svelte
<script lang="ts">
let { count } = $props();
</script>
<button onclick={() => (count += 1)}>
нажатий (child): {count}
</button>
Хотя вы можете временно переопределять пропсы, вы не должны их изменять, если они не являются привязываемыми.
Если пропс является обычным объектом, изменение не окажет никакого эффекта (демонстрация):
// App.svelte
<script>
import Child from './Child.svelte';
</script>
<Child object={{ count: 0 }} />
// App.svelte
<script lang="ts">
import Child from './Child.svelte';
</script>
<Child object={{ count: 0 }} />
// Child.svelte
<script>
let { object } = $props();
</script>
<button onclick={() => {
// не имеет эффекта
object.count += 1
}}>
нажатий: {object.count}
</button>
// Child.svelte
<script lang="ts">
let { object } = $props();
</script>
<button onclick={() => {
// не имеет эффекта
object.count += 1
}}>
нажатий: {object.count}
</button>
Однако, если пропс является прокси реактивного состояния, то изменения будут иметь эффект, но вы увидите предупреждение ownership_invalid_mutation
, потому что компонент изменяет состояние, которое ему не «принадлежит» (демонстрация):
// App.svelte
<script>
import Child from './Child.svelte';
let object = $state({count: 0});
</script>
<Child {object} />
// App.svelte
<script lang="ts">
import Child from './Child.svelte';
let object = $state({count: 0});
</script>
<Child {object} />
// Child.svelte
<script>
let { object } = $props();
</script>
<button onclick={() => {
// вызовет обновление счётчика ниже,
// но с предупреждением. Не изменяйте
// объекты, которые вам не принадлежат!
object.count += 1
}}>
нажатий: {object.count}
</button>
// Child.svelte
<script lang="ts">
let { object } = $props();
</script>
<button onclick={() => {
// вызовет обновление счётчика ниже,
// но с предупреждением. Не изменяйте
// объекты, которые вам не принадлежат!
object.count += 1
}}>
нажатий: {object.count}
</button>
Значение по умолчанию пропса, не объявленного с помощью $bindable
, остается нетронутым — оно не превращается в прокси реактивного состояния — что означает, что изменения не вызовут обновления (демонстрация)
// Child.svelte
<script>
let { object = { count: 0 } } = $props();
</script>
<button onclick={() => {
// не имеет эффекта, если используется значение по умолчанию
object.count += 1
}}>
нажатий: {object.count}
</button>
// Child.svelte
<script lang="ts">
let { object = { count: 0 } } = $props();
</script>
<button onclick={() => {
// не имеет эффекта, если используется значение по умолчанию
object.count += 1
}}>
нажатий: {object.count}
</button>
В заключение: не изменяйте пропсы. Либо используйте колбэк-пропсы для передачи изменений, либо — если родитель и потомок должны использовать один и тот же объект — используйте руну $bindable
.
Безопасность типов
Вы можете добавить безопасность типов к вашим компонентам, аннотируя ваши пропсы, как вы бы сделали с любым другим объявлением переменной. В TypeScript это может выглядеть так…
<script lang="ts">
let { adjective }: { adjective: string } = $props();
</script>
…в то время как в JSDoc вы можете сделать это так:
<script>
/** @type {{ adjective: string }} */
let { adjective } = $props();
</script>
Вы, конечно, можете отделить объявление типа от аннотации:
<script lang="ts">
interface Props {
adjective: string;
}
let { adjective }: Props = $props();
</script>
Рекомендуется добавлять типы, так как это подскажет пользователям вашего компонента, какие пропсы они должны предоставить.
$props.id()
Эта руна, появившаяся в версии 5.20.0, создаёт уникальный идентификатор для конкретного экземпляра компонента. При гидратации серверного рендеринга значение сохраняется как на сервере, так и на клиенте.
Это особенно удобно для связывания элементов с помощью атрибутов, таких как for
и aria-labelledby
.
<script>
const uid = $props.id();
</script>
<form>
<label for="{uid}-firstname">First Name: </label>
<input id="{uid}-firstname" type="text" />
<label for="{uid}-lastname">Last Name: </label>
<input id="{uid}-lastname" type="text" />
</form>