Vue Props

Props 是 Vue 中的一个配置选项。

使用 props,我们可以通过组件标记的自定义属性将数据传递给组件。


将数据传递到组件

你还记得上一页的例子吗,3 个组件都写着 'Apple'?有了 Props,我们现在可以将数据传递到组件,为它们提供不同的内容,并使它们看起来不同。

让我们制作一个简单的页面来显示 'Apples', 'Pizza' 和 'Rice'.

在主应用程序文件 App.vue 中,我们创建了自己的属性“food-name”,以传递带有 <food-item/> 组件标签的 props

  1. <template>
  2. <h1>Food</h1>
  3. <food-item food-name="Apples"></food>
  4. <food-item food-name="Pizza"></food>
  5. <food-item food-name="Rice"></food>
  6. </template>
  7. <script></script>
  8. <style>
  9. #app > div {
  10. border: dashed black 1px;
  11. display: inline-block;
  12. width: 120px;
  13. margin: 10px;
  14. padding: 10px;
  15. background-color: lightgreen;
  16. }
  17. </style>

接收组件内部的数据

为了接收通过 App.vue 的 'food-item' 属性发送的数据,我们使用了这个新的 'props' 配置选项。我们列出了收到的属性,以便我们的组件 *.vue 文件了解它们,现在我们可以像使用数据属性一样,在任何我们想要的地方使用 Props

FoodItem.vue:

  1. <script>
  2. export default {
  3. props: [
  4. 'foodName'
  5. ]
  6. }
  7. </script>
Props 属性在 <template> 标记中用短划线 - 分隔单词(kebab-case 规则),但 kebab-case 在 JavaScript 中是不合法的。因此,我们需要在 JavaScript 中将属性名称写为 camelCase(驼峰式),Vue 会自动识别!

最后,我们用 <div> 元素表示 'Apples', 'Pizza' 和 'Rice' 的例子如下:

实例

App.vue:

  1. <template>
  2. <h1>Food</h1>
  3. <food-item food-name="Apples"/>
  4. <food-item food-name="Pizza"/>
  5. <food-item food-name="Rice"/>
  6. </template>

FoodItem.vue:

  1. <template>
  2. <div>
  3. <h2>{{ foodName }}</h2>
  4. </div>
  5. </template>
  6. <script>
  7. export default {
  8. props: [
  9. 'foodName'
  10. ]
  11. }
  12. </script>
  13. <style></style>

很快,我们将学习如何将不同的数据类型作为 props 属性传递给组件,但在此之前,让我们用每种食物类型的描述来扩展代码,并将食物 <div> 元素放入 Flexbox 中。

实例

App.vue:

  1. <template>
  2. <h1>Food</h1>
  3. <div id="wrapper">
  4. <food-item
  5. food-name="Apples"
  6. food-desc="Apples are a type of fruit that grow on trees."/>
  7. <food-item
  8. food-name="Pizza"
  9. food-desc="Pizza has a bread base with tomato sauce, cheese, and toppings on top."/>
  10. <food-item
  11. food-name="Rice"
  12. food-desc="Rice is a type of grain that people like to eat."/>
  13. </div>
  14. </template>
  15. <script></script>
  16. <style>
  17. #wrapper {
  18. display: flex;
  19. flex-wrap: wrap;
  20. }
  21. #wrapper > div {
  22. border: dashed black 1px;
  23. margin: 10px;
  24. padding: 10px;
  25. background-color: lightgreen;
  26. }
  27. </style>

FoodItem.vue:

  1. <template>
  2. <div>
  3. <h2>{{ foodName }}</h2>
  4. <p>{{ foodDesc }}</p>
  5. </div>
  6. </template>
  7. <script>
  8. export default {
  9. props: [
  10. 'foodName',
  11. 'foodDesc'
  12. ]
  13. }
  14. </script>
  15. <style></style>

布尔类型 Props

我们可以通过传递不同数据类型的 Props 来实现不同的功能,并且我们能够定义在从 App.vue 创建组件时如何给定属性的规则。

让我们添加一个新 Props 'isFavorite'。这应该是一个值为 truefalse 的布尔 Props,这样,如果食物被认为是最喜欢的,我们就可以直接将其与 v-show 一起使用,以显示最喜欢的 <img> 标签。

要传递数据类型不同于字符串的 props,我们必须在要传递的属性前面写 v-bind:

这就是我们如何将 App.vue 中的布尔值 'is-favorite' prop 作为属性 'is-favorite' 传递的方式:

App.vue:

  1. <template>
  2. <h1>Food</h1>
  3. <p>My favorite food has a diploma image attached to it.</p>
  4. <div id="wrapper">
  5. <food-item
  6. food-name="Apples"
  7. food-desc="Apples are a type of fruit that grow on trees."
  8. v-bind:is-favorite="true"></food>
  9. <food-item
  10. food-name="Pizza"
  11. food-desc="Pizza has a bread base with tomato sauce, cheese, and toppings on top."
  12. v-bind:is-favorite="false"></food>
  13. <food-item
  14. food-name="Rice"
  15. food-desc="Rice is a type of grain that people like to eat."
  16. v-bind:is-favorite="false"></food>
  17. </div>
  18. </template>

我们在 FoodItem.vue 中收到布尔值 'is-favorite' props,如果食物被认为是最喜欢的,则显示最喜欢的:

实例

FoodItem.vue:

  1. <template>
  2. <div>
  3. <h2>
  4. {{ foodName }}
  5. <img src="/img_quality.svg" v-show="isFavorite">
  6. </h2>
  7. <p>{{ foodDesc }}</p>
  8. </div>
  9. </template>
  10. <script>
  11. export default {
  12. props: ['foodName','foodDesc','isFavorite']
  13. }
  14. </script>
  15. <style>
  16. img {
  17. height: 1.5em;
  18. float: right;
  19. }
  20. </style>

Props 接口

在上面的例子中,根据 FoodItem.vue 中的代码,我们不能确定是否收到了 'is-favorite' props,也不能确定它是否是布尔值。为了帮助我们做到这一点,我们可以定义我们接收的 props 的数据类型,我们可以将 props 设置为必需的,我们甚至可以创建验证函数来验证我们接收到的 props


Props 作为对象

FoodItem.vue 中,我们注释了如何在数组中定义 props 以将其作为引用,而不是在对象中定义 props。除了 props 名称之外,我们还可以定义每个 props 的数据类型,如下所示:

FoodItem.vue:

  1. <script>
  2. export default {
  3. // props: ['foodName','foodDesc','isFavorite']
  4. props: {
  5. foodName: String,
  6. foodDesc: String,
  7. isFavorite: Boolean
  8. }
  9. }
  10. </script>

通过这种方式定义 props,其他人可以查看 FoodItem.vue 内部,并轻松地看到组件的值。

如果一个组件是从父元素(在我们的例子中是 App.vue)创建的,并且被赋予了一个具有错误数据类型的 props,那么您会在控制台中收到警告,如下所示:

这样的警告有助于让我们自己和他人知道组件没有按照正确方式使用,并告诉我们哪里出了问题,以便我们能够纠正错误。


Required Props

要告诉 Vue 需要 props,我们需要将 props 定义为对象。让我们制作 props 'foodName',如下所示:

FoodItem.vue:

  1. <script>
  2. export default {
  3. // props: ['foodName','foodDesc','isFavorite']
  4. props: {
  5. foodName: {
  6. type: String,
  7. required: true
  8. },
  9. foodDesc: String,
  10. isFavorite: Boolean
  11. }
  12. }
  13. </script>

如果一个组件是从父元素(在我们的例子中是 App.vue)创建的,并且没有定义所需的 props,那么您会在控制台中收到警告,如下所示:


默认值

我们可以为 props 设置一个默认值。

让我们为 'FoodItem' 组件中的 'foodDesc' props 创建一个默认值,然后为 rice 创建这样一个项目,而不定义 'foodDesc' props

实例

App.vue:

  1. <template>
  2. <h1>Food</h1>
  3. <p>My favorite food has a diploma image attached to it.</p>
  4. <div id="wrapper">
  5. <food-item
  6. food-name="Apples"
  7. food-desc="Apples are a type of fruit that grow on trees."
  8. v-bind:is-favorite="true"/>
  9. <food-item
  10. food-name="Pizza"
  11. food-desc="Pizza has a bread base with tomato sauce, cheese, and toppings on top."
  12. v-bind:is-favorite="false"/>
  13. <food-item
  14. food-name="Rice"
  15. food-desc="Rice is a type of grain that people like to eat."
  16. v-bind:is-favorite="false"/>
  17. </div>
  18. </template>

FoodItem.vue:

  1. <script>
  2. export default {
  3. props: {
  4. foodName: {
  5. type: String,
  6. required: true
  7. },
  8. foodDesc: {
  9. type: String,
  10. required: false,
  11. default: 'This is the default description.'
  12. }
  13. isFavorite: {
  14. type: Boolean,
  15. required: false,
  16. default: false
  17. }
  18. }
  19. }
  20. </script>

Props Validator 函数

我们还可以定义一个验证函数来决定 prop 值是否有效。

这样的验证函数必须返回 truefalse。当验证器返回 false 时,表示 prop 值无效。当我们在开发人员模式下运行页面时,无效的 prop 值会在浏览器控制台中生成警告,该警告是确保组件按预期使用的。

假设我们希望食物描述有一定的长度,在 20 到 50 个字符之间。我们可以添加一个验证函数,以确保提供的食物描述具有有效的长度。

FoodItem.vue:

  1. <script>
  2. export default {
  3. props: {
  4. foodName: {
  5. type: String,
  6. required: true
  7. },
  8. foodDesc: {
  9. type: String,
  10. required: false,
  11. default: 'This is the default description.',
  12. validator: function(value) {
  13. if( 20<value.length && value.length<50 ) {
  14. return true;
  15. }
  16. else {
  17. return false;
  18. }
  19. }
  20. }
  21. isFavorite: {
  22. type: Boolean,
  23. required: false,
  24. default: false
  25. }
  26. }
  27. }
  28. </script>
注意:如果您将上面的验证代码添加到本地项目中,您将在开发模式中收到警告,因为披萨的食物描述是 65 个字符,比验证函数允许的长度长了 15 个字符。


修改 Props

当在父元素中创建组件时,我们不允许更改子元素中接收的 prop 的值。因此,在 FoodItem.vue 中,我们无法更改从 App.vue 中获得的 'isFavorite' prop 的值。该 prop 是从父级只读的,在我们的例子中就是 App.vue

但假设我们希望用户能够通过点击按钮来改变什么食物被认为是最喜欢的。现在需要更改 'isFavorite' prop,但我们不能这样做,因为它是只读的。

不允许更改 'isFavorite'。这将产生一个错误。

  1. methods: {
  2. toggleFavorite() {
  3. this.isFavorite = !this.isFavorite;
  4. }
  5. }

为了解决这个问题,我们可以使用 propFoodItem.vue 中初始化一个新的数据值 'foodIsFavorite',如下所示:

  1. data() {
  2. return {
  3. foodIsFavorite: this.isFavorite
  4. }
  5. }

现在我们可以添加一个方法,这样用户就可以切换这个新的数据值:

  1. methods: {
  2. toggleFavorite() {
  3. this.foodIsFavorite = !this.foodIsFavorite;
  4. }
  5. }

我们还必须为每个食物项目添加切换按钮,并在 <img> 标签中更改 v-show 以取决于新的数据属性 'foodIsFavorite'。为了简化我们的实例,我们还将 props 声明精简为一个数组。

实例

FoodItem.vue:

  1. <template>
  2. <div>
  3. <h2>
  4. {{ foodName }}
  5. <img src="/img_quality.svg" v-show="foodIsFavorite">
  6. </h2>
  7. <p>{{ foodDesc }}</p>
  8. <button v-on:click="toggleFavorite">Favorite</button>
  9. </div>
  10. </template>
  11. <script>
  12. export default {
  13. props: ['foodName','foodDesc','isFavorite'],
  14. data() {
  15. return {
  16. foodIsFavorite: this.isFavorite
  17. }
  18. },
  19. methods: {
  20. toggleFavorite() {
  21. this.foodIsFavorite = !this.foodIsFavorite;
  22. }
  23. }
  24. }
  25. </script>
  26. <style>
  27. img {
  28. height: 1.5em;
  29. float: right;
  30. }
  31. </style>