Skip to content

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.

Vlastnosti se deklarují v možnosti props:

js
export default {
  props: ['foo'],
  created() {
    // props jsou vystaveny přes `this`
    console.log(this.foo)
  }
}

Kromě deklarace vlastností pomocí pole řetězců můžeme použít také objektovou syntaxi:

js
export default {
  props: {
    title: String,
    likes: Number
  }
}
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
})
js
export default {
  props: {
    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
export default {
  data() {
    return {
      post: {
        id: 1,
        title: 'Moje cesta s Vue'
      }
    }
  }
}
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'
js
export default {
  props: ['foo'],
  created() {
    // ❌ varování, props jsou read-only!
    this.foo = 'bar'
  }
}

Obvykle jsou dva případy, kdy vypadá lákavě vlastnost měnit:

  1. 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:

    js
    const props = defineProps(['initialCounter'])
    
    // counter použije `props.initialCounter` pouze jako výchozí hodnotu;
    // od budoucích aktualizací je odpojen
    const counter = ref(props.initialCounter)
    js
    export default {
      props: ['initialCounter'],
      data() {
        return {
          // counter použije `props.initialCounter` pouze jako výchozí hodnotu;
          // od budoucích aktualizací je odpojen
          counter: this.initialCounter
        }
      }
    }
  2. 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:

    js
    const props = defineProps(['size'])
    
    // computed proměnná, která se automaticky aktualizuje, 
    // pokud se vlastnost změní
    const normalizedSize = computed(() => props.size.trim().toLowerCase())
    js
    export default {
      props: ['size'],
      computed: {
        // computed proměnná, která se automaticky aktualizuje, 
        // pokud se vlastnost změní
        normalizedSize() {
          return this.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()v možnosti props 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.

js
export default {
  props: {
    // 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'
      }
    }
  }
}

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 hodnotu undefined.

  • Neuvedená Boolean vlastnost bude převedena na hodnotu false. To můžete změnit nastavením default - 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 vlastnosti undefined - to se týká jak případů, kdy vlastnost chybí, tak těch, kdy je předána explicitní hodnota undefined.

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 }}.

Poznámka

Zapamatujte si, že vlastnosti jsou validovány dříve než je vytvořena instance komponenty, takže proměnné instance (např. data, computed, atd.) nebudou uvnitř funkcí default či validator dostupné.

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
})
js
export default {
  props: {
    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
  }
})
js
export default {
  props: {
    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
})
js
export default {
  props: {
    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]
})
js
// disabled bude přetypováno na true
export default {
  props: {
    disabled: [Boolean, Number]
  }
}
  
// disabled bude přetypováno na true
export default {
  props: {
    disabled: [Boolean, String]
  }
}
  
// disabled bude přetypováno na true
export default {
  props: {
    disabled: [Number, Boolean]
  }
}
  
// disabled bude chápáno jako prázdný string (disabled="")
export default {
  props: {
    disabled: [String, Boolean]
  }
}
Vlastnosti (Props) has loaded