Vlastnosti (Props)
Tato stránka předpokládá, že už jste četli Základy komponent. Pokud jsou pro vás komponenty nové, přečtěte si je jako první.
Deklarace vlastností
Vue komponenty vyžadují explicitní deklaraci vlastností, aby Vue vědělo, které externí hodnoty předávané komponentě mají být považovány za „fallthrough“ atributy (což bude probráno v příslušné sekci).
V Single-file komponentách (SFC) s využitím <script setup>
, je možné vlastnosti deklarovat pomocí makra defineProps()
:
vue
<script setup>
const props = defineProps(['foo'])
console.log(props.foo)
</script>
Ve komponentách bez <script setup>
, se vlastnosti deklarují v možnosti props
:
js
export default {
props: ['foo'],
setup(props) {
// setup() obdrží `props` jako první parametr
console.log(props.foo)
}
}
Všimněte si, že parametr předávaný do defineProps()
je stejný jako hodnota předávaná ze možnosti props
: pro deklaraci vlastností je mezi oběma styly deklarace sdíleno stejné API.
Kromě deklarace vlastností pomocí pole řetězců můžeme použít také objektovou syntaxi:
js
// v rámci `<script setup>`
defineProps({
title: String,
likes: Number
})
js
// pokud není použito `<script setup>`
export default {
props: {
title: String,
likes: Number
}
}
Pro každou vlastnost deklarovanou objektovou syntaxí je klíčem jméno vlastnosti, zatímco hodnotou by měla být funkce konstruktoru očekávaného typu.
Nejen, že to vaší komponentu popisuje, ale také budou prostřednictvím výpisu do konzole prohlížeče varováni ostatní vývojáři, kteří vaši komponetu použjí, pokud budou předávat špatný datový typ. O validaci vlastností budeme více mluvit později níže na této stránce.
Pokud používáte TypeScript a <script setup>
, je také možné deklarovat vlastnosti s použitím „pure“ typových anotací:
vue
<script setup lang="ts">
defineProps<{
title?: string
likes?: number
}>()
</script>
Více informací: Typování vlastností komponent
Reaktivní destrukturování vlastností
Systém reaktivity Vue sleduje změny stavu na základě přístupu k vlastnostem. Např. když přistoupíte na props.foo
v computed getter funkci nebo ve watcheru, vlastnost foo
bude sledována jako reaktivní závislost.
V následujícím kódu:
ts
const { foo } = defineProps(['foo'])
watchEffect(() => {
// před Vue 3.5 se spustí pouze jednou
// ve Vue 3.5+ se spustí znovu při každé změně "foo"
console.log(foo)
})
Ve verzi 3.4 a nižší je foo
ve skutečnosti konstanta a nikdy se nezmění. Ve verzi 3.5+ doplní Vue překladač automaticky props.
, když kód ve stejném <script setup>
bloku přistupuje na proměnné dekonstruované z defineProps
. Takže výše uvedený kód bude zkompilován do následujícího ekvivalentu:
js
const props = defineProps(['foo'])
watchEffect(() => {
// `foo` je překladačem transformováno na `props.foo`
console.log(props.foo)
})
Navíc je možné použít nativní JavaScript syntaxi pro deklaraci výchozích hodnot vlastností. To je zvlášť užitečné při použití type-based deklarace:
ts
const { foo = 'ahoj' } = defineProps<{ foo?: string }>()
Pokud vám ve vašem IDE vyhovuje mít lepší vizuální rozlišení mezi destrukturovanými vlastnostmi a obyčejnými proměnnými, Vue rozšíření pro VS Code nabízí nastavení, které pro destrukturované vlastnosti zpřístupní inline nápovědu.
Předávání destrukturovaných vlastností do funkcí
Když předáváme destrukturované vlastnosti do funkce, např.:
js
const { foo } = defineProps(['foo'])
watch(foo, /* ... */)
Nebude to fungovat podle očekávání, protože to odpovídá watch(props.foo, ...)
- do watch
předáváme hodnotu místo reaktivního zdroje dat. Překladač Vue v takovém případě vyvolá varování.
Stejně jako můžeme sledovat běžnou proměnnou pomocí watch(() => props.foo, ...)
, můžeme i destrukturovanou vlastnost sledovat obaleném do getteru:
js
watch(() => foo, /* ... */)
Kromě toho je doporučený postup při předávání destrukturované vlastnosti do externí funkce se zachováním její reaktivity použít:
js
useComposable(() => foo)
Když externí funkce potřebuje sledovat změny předané proměnné, např. uvnitř getteru computed či watcheru, může zavolat getter (nebo ji normalizovat pomocí toValue).
Detaily předávání vlastností
Velká a malá písmena v názvech vlastností
Dlouhé názvy vlastností deklarujeme pomocí camelCase, protože se tak vyhneme nutnosti doplňovat uvozovky při jejich použití jako klíčů vlastností a umožní nám to odkazovat přímo na ně ve výrazech šablon, jelikož se jedná o platné JavaScript identifikátory:
js
defineProps({
greetingMessage: String
})
template
<span>{{ greetingMessage }}</span>
Technicky lze camelCase použít i při předávání vlastností do komponenty potomka (kromě in-DOM šablon). Konvence však ve všech případech používá kebab-case, aby se sladila s HTML atributy:
template
<MyComponent greeting-message="hello" />
Kdykoli je to možné, používáme pro tagy komponent PascalCase, protože to zlepšuje čitelnost šablony tím, že odlišuje Vue komponenty od nativních elementů. Použití camelCase při předávání vlastností však takový praktický přínos nemá, proto jsme se rozhodli dodržovat konvence jednotlivých jazyků.
Statické vs. dynamické vlastnosti
Zatím jste viděli vlastnosti předávané jako statické hodnoty, jako např.:
template
<BlogPost title="Moje cesta s Vue" />
Viděli jste také dynamické přiřazování vlastností pomocí v-bind
nebo jeho zkratky :
, jako třeba v:
template
<!-- Dynamicky přiřazená hodnota z jiné proměnné -->
<BlogPost :title="post.title" />
<!-- Dynamicky přiřazená hodnota komplexního výrazu -->
<BlogPost :title="post.title + ' od ' + post.author.name" />
Předávání různých datových typů
Ve dvou výše uvedených příkladech jsme předávali hodnoty typu string, ale jako vlastnost lze předat jakýkoli datový typ.
Číslo
template
<!-- I když je `42` statická hodnota, je třeba v-bind, abychom řekli Vue, -->
<!-- že toto je JavaScript výraz a nikoli prostý string. -->
<BlogPost :likes="42" />
<!-- Dynamicky přiřazená hodnota z jiné proměnné -->
<BlogPost :likes="post.likes" />
Boolean
template
<!-- Vlastnost bez hodnoty bude mít implicitně hodnotu `true`. -->
<BlogPost is-published />
<!-- I když je `false` statická hodnota, je třeba v-bind, abychom řekli Vue, -->
<!-- že toto je JavaScript výraz a nikoli prostý string. -->
<BlogPost :is-published="false" />
<!-- Dynamicky přiřazená hodnota z jiné proměnné -->
<BlogPost :is-published="post.isPublished" />
Pole
template
<!-- I když je hodnota pole statická, je třeba v-bind, abychom řekli Vue, -->
<!-- že toto je JavaScript výraz a nikoli prostý string. -->
<BlogPost :comment-ids="[234, 266, 273]" />
<!-- Dynamicky přiřazená hodnota z jiné proměnné -->
<BlogPost :comment-ids="post.commentIds" />
Objekt
template
<!-- I když je hodnota objektu statická, je třeba v-bind, abychom řekli Vue, -->
<!-- že toto je JavaScript výraz a nikoli prostý string. -->
<BlogPost
:author="{
name: 'Veronica',
company: 'Veridian Dynamics'
}"
/>
<!-- Dynamicky přiřazená hodnota z jiné proměnné -->
<BlogPost :author="post.author" />
Binding více vlastností s využitím objektu
Pokud chcete předat všechny vlastnosti objektu najednou, můžete použít v-bind
bez parametru (v-bind
místo :prop-name
). Například pokud máte objekt post
:
js
const post = {
id: 1,
title: 'Moje cesta s Vue'
}
Následující šablona:
template
<BlogPost v-bind="post" />
Bude stejná jako:
template
<BlogPost :id="post.id" :title="post.title" />
Jednosměrný datový tok
Všechny vlastnosti tvoří jednosměrný binding směrem dolů mezí nadřízenou a podřízenou vlastností: když se aktualizuje vlastnost v komponentě rodiče, přenese se to dolů na vlastnost potomka, ale nikoli naopak. To zabraňuje tomu, aby komponenty potomků omylem měnily stav vlastností rodiče, což by mohlo ztížit pochopení toku dat ve vaší aplikaci.
Kromě toho se při každé aktualizaci komponenty rodiče všechny vlastnosti v komponentě potomka obnoví na nejnovější hodnotu. To znamená, že byste se neměli pokoušet měnit vlastnost uvnitř komponenty potomka. Pokud to uděláte, Vue vás na to upozorní v konzoli:
js
const props = defineProps(['foo'])
// ❌ varování, props jsou read-only!
props.foo = 'bar'
Obvykle jsou dva případy, kdy vypadá lákavě vlastnost měnit:
Vlastnost slouží k předání počáteční hodnoty; komponenta potomka ji chce následně použít jako lokální datovou hodnotu. V tomto případě je nejlepší definovat lokální datovou proměnnou, která používá předanou vlastnost jako svou výchozí hodnotu:
jsconst props = defineProps(['initialCounter']) // counter použije `props.initialCounter` pouze jako výchozí hodnotu; // od budoucích aktualizací je odpojen const counter = ref(props.initialCounter)
Vlastnost je předána jako surová (raw) hodnota, kterou je třeba transformovat. V tom případě je nejlepší pomocí hodnoty vlastnosti definovat computed proměnnou:
jsconst props = defineProps(['size']) // computed proměnná, která se automaticky aktualizuje, // pokud se vlastnost změní const normalizedSize = computed(() => props.size.trim().toLowerCase())
Změny vlastností typu objekt / pole
Pokud jsou objekty a pole předávány jako vlastnosti, komponenta potomka sice nemůže měnit binding na vlastnosti, ale bude moci měnit vnořené prvky objektu nebo pole. Je to proto, že v jazyce JavaScript se objekty a pole předávají pomocí odkazů (pass by reference) a pro Vue je nepřiměřeně nákladné takovým změnám zabránit.
Hlavní nevýhodou takových změn je, že umožňují komponentám potomka ovlivňovat stav rodičů způsobem, který není pro komponentu rodiče zřejmý, což může v budoucnu ztížit uvažování o toku dat. V rámci osvědčených postupů byste se měli takovým změnám vyhnout, pokud nejsou komponenty rodiče a potomka už z definice úzce propojeny (tightly coupled). Ve většině případů by měl potomek vyvolat událost, aby nechal změnu provést svého rodiče.
Validace vlastností
Komponenty mohou specifikovat požadavky na své vlastnosti, například datové typy, které jste již viděli. Pokud některý požadavek není splněn, Vue vás na to upozorní v JavaScript konzoli prohlížeče. To je užitečné zejména při vývoji komponenty, která má být používána jinými uživateli.
Chcete-li zadat ověřování vlastností, můžete makru defineProps()
místo pole řetězců zadat objekt s požadavky na ověření. Například:
js
defineProps({
// základní kontrola typu
// (hodnoty `null` a `undefined` umožní jakýkoli typ)
propA: Number,
// více možných typů
propB: [String, Number],
// povinný string
propC: {
type: String,
required: true
},
// povinný string, který může nabývat hodnoty `null`
propD: {
type: [String, null],
required: true
},
// číslo s výchozí hodnotou
propE: {
type: Number,
default: 100
},
// objekt s výchozí hodnotou
propF: {
type: Object,
// výchozí hodnota objektu či pole musí být
// vrácena z tovární (factory) metody
// parametrem funkce budou surové (raw) vlastnosti,
// které jsou předány do komponenty
default(rawProps) {
return { message: 'hello' }
}
},
// vlastní validační funkce
// od verze 3.4+ jsou jako druhý parametr předávány kompletní props
propG: {
validator(value, props) {
// hodnota musí odpovídat jednomu z těchto tří řetězců
return ['success', 'warning', 'danger'].includes(value)
}
},
// funkce s výchozí hodnotou
propH: {
type: Function,
// na rozdíl od výchozí hodnoty objektu či pole,
// toto není tovární (factory) metoda
// toto je funkce, která bude nabídnuta jako výchozí hodnota
default() {
return 'Default function'
}
}
})
TIP
Kód uvnitř makra defineProps()
nemůže přistupovat k jiným proměnným deklarovaným v <script setup>
, protože celý výraz je při kompilaci přesunut do vnějšího function scope.
Další podrobnosti:
Všechny vlastnosti jsou ve výchozím nastavení nepovinné, pokud není zadáno
required: true
.Neuvedená nepovinná vlastnost kromě
Boolean
bude mít hodnotuundefined
.Neuvedená
Boolean
vlastnost bude převedena na hodnotufalse
. To můžete změnit nastavenímdefault
- tj.:default: undefined
, aby se chovala jako ne-Boolean vlastnost.Pokud je zadána
default
hodnota, bude použita, pokud je předáváná hodnota vlastnostiundefined
- to se týká jak případů, kdy vlastnost chybí, tak těch, kdy je předána explicitní hodnotaundefined
.
Pokud validace vlastnosti selže, zobrazí Vue varování do konzole (pokud je aplikace sestavena v development módu).
Pokud používáte Type-based deklarace vlastností , Vue se pokusí kompilovat typové anotace na odpovídající runtime deklarace vlastností jak nejlépe dovede. Například, defineProps<{ msg: string }>
bude při kompilaci převedeno na{ msg: { type: String, required: true }}
.
Kontrola typů za běhu
Hodnota type
může být jeden z následujících nativních konstruktorů:
String
Number
Boolean
Array
Object
Date
Function
Symbol
Error
Kromě toho může být type
také vlastní třída nebo funkce konstruktoru a ověření bude provedeno pomocí kontroly instanceof
. Například následující třídu:
js
class Person {
constructor(firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
}
Můžete použít jako typ vlastnosti:
js
defineProps({
author: Person
})
Vue použije instanceof Person
k ověření, zda je hodnota vlastnosti author
skutečně instancí třídy Person
.
Nullable Type
Pokud je typ povinný, ale hodnota může být null, lze použít syntaxi pole, které obsahuje null
:
js
defineProps({
id: {
type: [String, null],
required: true
}
})
Pamatujte, že pokud je type
pouze null
bez použití syntaxe pole, bude povolen jakýkoliv typ.
Přetypování Boolean
Vlastnosti typu Boolean
mají speciální pravidla přetypování k napodobení chování nativních boolean atributů. Pokud máme <MyComponent>
s následující definicí:
js
defineProps({
disabled: Boolean
})
Lze komponentu použít i tímto způsobem:
template
<!-- stejné jako předání :disabled="true" -->
<MyComponent disabled />
<!-- stejné jako předání :disabled="false" -->
<MyComponent />
Když je vlastnost deklarována, aby umožnila více typů, budou uplatněna rovněž pravidla přetypování pro Boolean
. Ovšem je tu krajní případ, kdy jsou povoleny jak String
, tak Boolean
- pravidlo přetypování na Boolean bude uplatněno pouze pokud se Boolean objeví před String:
js
// disabled bude přetypováno na true
defineProps({
disabled: [Boolean, Number]
})
// disabled bude přetypováno na true
defineProps({
disabled: [Boolean, String]
})
// disabled bude přetypováno na true
defineProps({
disabled: [Number, Boolean]
})
// disabled bude chápáno jako prázdný string (disabled="")
defineProps({
disabled: [String, Boolean]
})