Let’s look at a simple HelloWorld single-file component. You can find this at ./src/components/HelloWorld.vue, generated automatically when you create a Vue project with Vite.
让我们来看一个简单的 HelloWorld 单文件组件。您可以在 ./src/components/HelloWorld.vue 中找到该组件,它是在您使用 Vite 创建 Vue 项目时自动生成的。
Note how the msg value is set in the props array and that it is interpolated as a value using {{ msg }}.
请注意 msg 值是如何在道具数组中设置的,以及它是如何使用 {{ msg }} 插值的。
The props property of a Vue component can be an array of strings or an object literal, each property field of which is a component’s prop definition.
Vue 组件的 props 属性可以是字符串数组或对象字面,其中的每个属性字段都是组件的 prop 定义。
When a value is defined in props, it is then accessible as an instance variable in the template section of the Vue component:
在道具中定义值后,就可以在 Vue 组件的模板部分将其作为实例变量访问:
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: ['msg']
}
</script>
What follows is a demonstration of how to use the HelloWorld component in our Vue application.
下面将演示如何在 Vue 应用程序中使用 HelloWorld 组件。
First, we need to import HelloWorld in our App.vue file using <script setup>
:
首先,我们需要使用 <script setup>
在 App.vue 文件中导入 HelloWorld:
<script setup>
import HelloWorld from "./components/HelloWorld.vue";
</script>
Then, in the template section, we need to render <HelloWorld>
with the msg attribute set to “Vue.js”, as follows:
然后,在模板部分,我们需要呈现 msg 属性设置为 "Vue.js "的 <HelloWorld>
,如下所示:
<template>
<div id="app">
<HelloWorld msg="Vue.js"/>
</div>
</template>
We have seen how to use a component with props from its parent. This is useful for code reuse and for abstracting application behavior into component-sized chunks.
我们已经了解了如何使用来自父组件的道具。这对于代码重用和将应用程序行为抽象为组件大小的块非常有用。
Next, we will look at a practical example of a Greeting component.
接下来,我们将看一个问候语组件的实际例子。
In this exercise, we will create a component that lets you customize both the greeting (for example, Hello, Hey, or Hola) and who to greet (for example, World, Vue.js, or JavaScript developers) using what we’ve just learned about passing props from parent to child.
在本练习中,我们将创建一个组件,让您可以利用刚刚学到的父代向子代传递道具的知识,自定义问候语(例如 Hello、Hey 或 Hola)和问候对象(例如 World、Vue.js 或 JavaScript 开发人员)。
Create a new file named Greeting.vue in the ./src/components directory. This will be our single-file component.
在 ./src/components 目录中创建一个名为 Greeting.vue 的新文件。这将是我们的单文件组件。
Start by scaffolding the component with empty template and script tags:
首先用空模板和脚本标签为组件搭建脚手架:
<template>
<div>Empty</div>
</template>
<script>
export default {}
</script>
Next, we need to tell Vue that our component expects props. For this, we will add a props property to our component options object as an array with two fields, greeting and who, as shown in the following code block:
接下来,我们需要告诉 Vue 我们的组件需要道具。为此,我们将在组件选项对象中添加一个道具属性,该属性是一个数组,包含两个字段:greeting(问候语)和 who(谁),如以下代码块所示:
<script>
export default {
props: ['greeting', 'who']
}
</script>
Now, we want to render greeting and who in the template as follows:
现在,我们要在模板中呈现问候语和谁的信息,如下所示:
<template>
<div>{{ greeting }} {{ who }}</div>
</template>
The Greeting component is now ready to be used from App.vue.
问候组件现在可以在 App.vue 中使用了。
Open the src/App.vue file and import the Greeting component from ./src/components/Greeting.vue into the script section:
打开 src/App.vue 文件,将 ./src/components/Greeting.vue 中的 Greeting 组件导入脚本部分:
<script setup>
import Greeting from './components/Greeting.vue'
</script>
Now we can use Greeting in template:
现在,我们可以在模板中使用问候语了:
<template>
<div id="app">
<Greeting greeting="Hey" who="JavaScript"/>
</div>
</template>
In this exercise, we have learned how we can use props to enable communication between parent and child while keeping the component reusable. Instead of the component rendering static data, its parent passes it the data to render.
在本练习中,我们学习了如何使用prop属性实现父类和子类之间的通信,同时保持组件的可重用性。在组件渲染静态数据时,它的父组件会向它传递要渲染的数据。
In the next section, we will learn how to set prop values dynamically.
下一节,我们将学习如何动态设置prop属性值。
In this exercise, we will implement a component that allows users to change the name of the person to greet and pass it to the Greeting component we built in Exercise 4.01.
在本练习中,我们将实现一个组件,允许用户更改要问候的人的姓名,并将其传递给我们在练习 4.01 中创建的 "问候 "组件。
In the script section of App.vue, let’s remove the setup attribute from the script tag, and register Greeting as a child component in the components field as follows:
在 App.vue 的脚本部分,让我们移除脚本标签中的 setup 属性,并在 components 字段中将 Greeting 注册为子组件,如下所示:
<script>
import Greeting from './components/Greeting.vue'
export default {
components: {
Greeting
},
}
</script>
Then create a data top-level method that returns an initial greeting and who:
然后创建一个数据顶级方法,返回初始问候语和谁:
<script>
import Greeting from './components/Greeting.vue'
export default {
components: {
Greeting
},
data() {
return {
greeting: 'Hello',
who: 'Vue.js'
}
}
}
</script>
The browser should display the same output as in Exercise 4.01. We will now create an input field that accepts a string value for who from users and binds the who data to the who prop of Greeting:
浏览器应显示与练习 4.01 相同的输出结果。现在,我们将创建一个输入字段,该字段接受来自用户的 who 字符串值,并将 who 数据绑定到 Greeting 的 who 属性:
<template>
<div id="app">
<input placeholder="What is your name" v-model="who">
<Greeting greeting="Hi" :who="who"/>
</div>
</template>
We use props to define the interfaces of Vue components and ensure other developers use our components correctly. We need to define their interfaces with types and validation. Vue offers that capability out of the box by changing how we pass the props as string elements to the props property in an object form.
我们使用道具来定义 Vue 组件的接口,确保其他开发人员正确使用我们的组件。我们需要通过类型和验证来定义它们的接口。Vue 通过改变我们将道具作为字符串元素传递给对象表单中道具属性的方式,提供了开箱即用的功能。
Assume we want a Repeat.vue component that takes a times prop and a content prop and then calculates the array of repetitions using computed based on the times value. We can define the following:
假设我们需要一个 Repeat.vue 组件,它接收一个 times prop 和一个 content prop,然后根据 times 值使用计算方法计算重复数组。我们可以定义如下内容:
<template>
<div>
<span v-for="r in repetitions" :key="r">
{{ content }}
</span>
</div>
</template>
<script>
export default {
props: ['times', 'content'],
computed: {
repetitions() {
return Array.from({length: this.times});
}
}
}
</script>
In App.vue, we can consume our Repeat component as follows:
在 App.vue 中,我们可以如下使用 Repeat 组件:
<template>
<div id="app">
<Repeat :times="count" content="Repeat"/>
<button @click="increment()">Repeat</button>
</div>
</template>
<script>
import Repeat from './components/Repeat.vue'
export default {
components: {
Repeat
},
data() {
return {count: 1}
},
methods: {
increment() {
this.count += 1
}
}
}
</script>
Now, for this component to work properly, we need times to be a Number type and, ideally, content to be a String type.
现在,要使该组件正常工作,我们需要时间为数字类型,而内容最好为字符串类型。
Let’s define the times prop as a Number and the content props as a String:
让我们把时间道具定义为数字,把内容道具定义为字符串:
<script>
export default {
props: {
times: {
type: Number
},
content: {
type: String
}
},
computed: {
repetitions() {
return Array.from({length: this.times});
}
}
}
</script>
Vue supports union types. A union type is a type that is one of many other types, and is represented by an array of types, such as [String, Number]. We declare a prop to accept a union type by using the type field of that data prop object. For example, we set content to accept a union type that will be either a number or a string:
Vue 支持联合类型。联合类型是许多其他类型中的一种类型,由类型数组表示,如[字符串、数字]。我们通过使用数据道具对象的类型字段来声明道具接受联合类型。例如,我们设置 content 接受的 union 类型为数字或字符串:
<script>
export default {
props: {
times: {
type: Number
},
// 组合类型
content: {
type: [String, Number]
}
},
computed: {
repetitions() {
return Array.from({length: this.times});
}
}
}
</script>
Vue allows custom validators to be used as props using the validator property. This allows us to implement deep checks regarding object and collection types.
Vue 允许使用 validator 属性将自定义验证器用作道具。这样,我们就可以对对象和集合类型进行深度检查。
Let’s look at a CustomSelect component.
让我们来看看自定义选择组件。
On a basic level, the prop interface for select comprises an array of options and a selected option.
从根本上说,"选择 "的道具界面包括一个选项阵列和一个选定的选项。
Each option should have a label that represents what is displayed in select, and a value representing its actual value. The selected option’s value can be empty or be equal to the value field for one of our options.
每个选项都应该有一个标签,代表在选择时显示的内容,以及一个代表其实际值的值。所选选项的值可以是空的,也可以等于某个选项的值域。
We can implement CustomSelect in a naive way (no validation of the inputs) as follows:
我们可以用一种简单的方式(不对输入进行验证)实现 CustomSelect,具体如下:
<template>
<select>
<option
:selected="selected === o.value"
v-for="o in options"
:key="o.value"
>
{{ o.label }}
</option>
</select>
</template>
<script>
export default {
props: {
selected: {
type: String
},
options: {
type: Array
}
}
}
</script>
Then use CustomSelect to display a list of British crisp flavors (in src/App.vue):
然后使用 CustomSelect 显示英式酥脆口味列表(在 src/App.vue 中):
<template>
<div id="app">
<CustomSelect :selected="selected" :options="options"/>
</div>
</template>
<script>
import CustomSelect from './components/CustomSelect.vue'
export default {
components: {
CustomSelect
},
data() {
return {
selected: 'salt-vinegar',
options: [
{
value: 'ready-salted',
label: 'Ready Salted'
},
{
value: 'cheese-onion',
label: 'Cheese & Onion'
},
{
value: 'salt-vinegar',
label: 'Salt & Vinegar'
},
]
}
}
}
</script>
Now we can implement a prop validator method to enable further validation for our component’s logic, as follows:
现在,我们可以实现一个道具验证器方法,以进一步验证组件的逻辑,如下所示:
<script>
export default {
props: {
selected: {
type: String
},
options: {
type: Array,
// 自定义验证其
validator(options) {
return options.every(o => Boolean(o.value && o.label))
}
}
}
}
</script>
To mark a prop as required, we can use the required prop type property.
要将一个道具标记为必填,我们可以使用必填prop类型属性。
In the CustomSelect example, we can make selected a required prop by adding required: true to its prop definition, as follows:
在 CustomSelect 示例中,我们可以在其prop定义中添加 required: true,使 selected 成为必选prop,如下所示:
<script>
export default {
props: {
selected: {
type: String,
// 声明为必传属性
required: true
},
options: {
type: Array,
// 自定义验证其
validator(options) {
return options.every(o => Boolean(o.value && o.label))
}
}
}
}
</script>
With that, we have learned how to mark props as required and saw what happens when we don’t pass a value to a required prop. Next, we will learn how to set the default value for a prop and see why it is a good practice to do so.
至此,我们已经学会了如何将道具标记为必填项,并了解了不为必填项传递值会发生什么情况。接下来,我们将学习如何为道具设置默认值,并了解为什么这样做是一种好的做法。
There are situations where setting the default value for a prop is good practice to follow.
在某些情况下,为道具设置默认值是很好的做法。
Take a PaginatedList component, for instance. This component takes a list of items, a limit number of items to display, and an offset number. Then it will display a subset of items – currentWindow – based on the limit and offset values:
以 PaginatedList 组件为例。该组件接收一个项目列表、一个要显示的项目限制数和一个偏移量。然后,它将根据限制值和偏移值显示项目的子集–currentWindow:
<template>
<ul>
<li
v-for="el in currentWindow"
:key="el.id"
>
{{ el.content }}
</li>
</ul>
</template>
<script>
export default {
props: {
items: {
type: Array,
required: true,
},
limit: {
type: Number
},
offset: {
type: Number
}
},
computed: {
currentWindow() {
return this.items.slice(this.offset, this.limit)
}
}
}
</script>
Instead of passing the values of limit and offset every time, it might be better to set limit to a default value (like and offset to 0 (this means that by default, we show the first page, which contains 2 results).
与其每次都传递 limit 和 offset 的值,不如将 limit 设置为默认值,比如,将 offset 设置为 0(这意味着默认情况下,我们会显示包含 2 个结果的第一页)。
We can implement this change using the default property for each prop definition object as follows:
我们可以使用每个道具定义对象的默认属性来实现这一更改,如下所示:
<script>
export default {
props: {
items: {
type: Array,
required: true,
},
limit: {
type: Number,
default: 2, // 默认值
},
offset: {
type: Number,
default: 0,
}
},
computed: {
currentWindow() {
return this.items.slice(this.offset, this.limit)
}
}
}
</script>
Then in App.vue, we can use PaginatedList without passing limit and offset. Vue automatically falls back to the default values in case no value is passed:
这样,我们就可以在 App.vue 中使用 PaginatedList,而无需传递 limit 和 offset。如果没有传递值,Vue 会自动返回默认值:
<template>
<main>
<PaginatedList :items="snacks" />
</main>
</template>
<script>
import PaginatedList from './components/PaginatedList.vue'
export default {
components: {
PaginatedList
},
data() {
return {
snacks: [
{
id: 'ready-salted',
content: 'Ready Salted'
},
{
id: 'cheese-onion',
content: 'Cheese & Onion'
},
{
id: 'salt-vinegar',
content: 'Salt & Vinegar'
},
]
}
}
}
</script>
When you pass values for offset or limit, Vue will use these values instead of the defaults and render the component accordingly.
当您传递偏移或限制值时,Vue 将使用这些值而不是默认值,并相应地渲染组件。
In cases where a prop is an array or an object, we can’t assign its default value with a static array or object.
如果prop是一个数组或对象,我们就不能用静态数组或对象来指定其默认值。
Instead, we need to assign it a function that returns the desired default value. We can set the default value of items from the PaginatedList component to an empty array, as follows:
相反,我们需要为其分配一个函数,返回所需的默认值。我们可以将 PaginatedList 组件中项的默认值设置为空数组,如下所示:
<script>
export default {
props: {
items: {
type: Array,
default() {
return []
}
},
limit: {
type: Number,
default: 2, // 默认值
},
offset: {
type: Number,
default: 0,
}
},
computed: {
currentWindow() {
return this.items.slice(this.offset, this.limit)
}
}
}
</script>
At this point, we have learned how to set default values for component props. Note here that once you set a default value, you don’t need to set required field anymore. We can use a default value to ensure our props are always with values, regardless of whether this is required or optional.
至此,我们已经学会了如何为组件道具设置默认值。请注意,一旦设置了默认值,就不再需要设置必填字段了。我们可以使用默认值来确保我们的道具始终带有值,而不管它是必填还是可选的。
If you use <script setup>
, since there is no options object, we can’t define the component’s props using the props field. Instead, we use the defineProps() function from the vue package and pass all the relevant props’ definitions to it, just as we did with the props field.
如果使用"<script setup>
",由于没有选项对象,我们就无法使用 props 字段定义组件的道具。取而代之的是,我们使用 vue 软件包中的 defineProps() 函数,并将所有相关的道具定义传递给它,就像使用prop字段一样。
<script setup>
import {defineProps, computed} from 'vue'
const props = defineProps({
items: {
type: Array,
required: true,
},
limit: {
type: Number
},
offset: {
type: Number
}
});
const currentWindow = computed(() => {
return props.items.slice(props.offset, props.limit)
})
</script>
defineProps() returns an object containing all the props’ values. We can then access a prop such as items using props.items instead within the script section, and items as usual in the template section.
defineProps() 返回一个包含所有道具值的对象。这样,我们就可以在脚本部分使用 props.items 来访问 items 等道具,而在模板部分则可以像往常一样访问 items。
In this exercise, we will write a Repeat component that accepts a config data prop for passing times, which is a Number, and content, which is a String.
在本练习中,我们将编写一个 Repeat 组件,该组件接受用于传递次数(Number)和内容(String)的配置数据prop属性。
Create a new file named Repeat.vue in the ./src/components directory.
在 ./src/components 目录中新建一个名为 Repeat.vue 的文件。
Define a prop config for Repeat.vue. This prop will be of the Object type, as follows:
为 Repeat.vue 定义一个prop配置。该prop将是对象类型,如下所示:
<script>
export default {
props: {
config: {
type: Object,
required: true,
}
}
}
</script>
Config contains two fields – times and content. We compute a reactive data array called repetitions for the Repeat component with its length based on config.times:
Config 包含两个字段–时间和内容。我们为 Repeat 组件计算一个名为 repetitions 的反应数据数组,其长度基于 config.times:
<script>
export default {
props: {
config: {
type: Object,
required: true,
}
},
computed: {
repetitions() {
return Array.from({ length: this.config.times })
}
}
}
</script>
Set up <template>
so that it renders config.content for each of the repetitions items:
设置 <template>
以便为每个重复项渲染 config.content:
<template>
<div>
<span v-for="r in repetitions" :key="r">
{{ config.content }}
</span>
</div>
</template>
We need to ensure that content and times will receive the correct value by implementing a validator for config. The validator will check whether the received value’s times and content typeof are of number and string, respectively:
我们需要为配置实施一个验证器,以确保内容和时间接收到正确的值。验证器将检查接收值的 times 和 content typeof 是否分别为数字和字符串:
<script>
export default {
props: {
config: {
type: Object,
required: true,
// 校验器
validator(value) {
return typeof value.times === 'number' &&
typeof value.content === 'string'
}
}
},
computed: {
repetitions() {
return Array.from({length: this.config.times})
}
}
}
</script>
Next, we import and use Repeat in src/App.vue:
接下来,我们在 src/App.vue 中导入并使用 Repeat:
<template>
<main>
<Repeat :config="{times: 3,content: 'Repeat me.'}"/>
</main>
</template>
<script>
import Repeat from './components/Repeat.vue'
export default {
components: {
Repeat
}
}
</script>
We have demonstrated how we use a validator to better define components with props.
我们已经演示了如何使用验证器来更好地使用道具定义组件。
In the next section, we will have a deep dive into slots, a mechanism we use to compose our components by deferring template logic.
在下一节中,我们将深入探讨槽,这是一种通过延迟模板逻辑来组成组件的机制。