作者选择Open Sourcing Mental Illness接受捐赠,作为Write for DOnations计划的一部分。
介绍
使用Vue.js创建 Web 应用程序时,最佳做法是使用小型模块化代码块构建应用程序。这不仅使您的应用程序的各个部分保持专注,而且随着应用程序的复杂性增加,它也使应用程序更容易更新。由于从Vue CLI生成的应用程序需要构建步骤,因此您可以访问单文件组件(SFC) 以将模块化引入您的应用程序。SFC 具有.vue
扩展名并包含 HTML <template>
、<script>
和<style>
标签,并且可以在其他组件中实现。
SFC 为开发人员提供了一种方法,可以为他们的每个组件创建自己的HTML标签,然后在他们的应用程序中使用它们。与<p>
HTML 标签将在浏览器中呈现段落并保持非呈现功能相同的方式,组件标签将呈现 SFC 放置在 Vue 模板中的任何位置。
在本教程中,您将创建一个 SFC 并用于props
向下传递数据并slots
在标签之间注入内容。在本教程结束时,您将对 SFC 是什么以及如何实现代码重用有一个大致的了解。
先决条件
14.16.0
您的计算机上安装了Node.js版本或更高版本。要在 macOS 或 Ubuntu 20.04 上安装它,请按照如何在 macOS 上安装 Node.js 和创建本地开发环境中的步骤或如何在 Ubuntu 20.04 上安装 Node.js 的使用 PPA部分进行安装- Vue CLI 安装在您的机器上并生成了一个新项目。由于本教程使用 Vue 3 Composition API,因此请确保
3.x (Preview)
在生成应用程序时选择该选项。此项目的名称将是sfc-project
,它将充当根目录。 - 您还需要具备 JavaScript、HTML 和 CSS 的基本知识,您可以在我们的如何使用 HTML 构建网站系列、如何使用 CSS 构建网站系列和如何使用 JavaScript 编码中找到这些知识。
步骤 1 — 设置项目
在本教程中,您将创建一个机场卡组件,该组件在一系列卡中显示多个机场及其代码。遵循先决条件部分后,您将拥有一个名为sfc-project
. 在本节中,您将数据导入到这个生成的应用程序中。此数据将是一个对象数组,其中包含一些用于在浏览器中显示信息的属性。
项目生成后,打开终端cd
或将目录更改为根src
文件夹:
- cd sfc-project/src
从那里,新建一个名为目录data
与mkdir
命令,然后创建一个名为新文件us-airports.js
使用touch
命令:
- mkdir data
- touch data/us-airports.js
在您选择的文本编辑器中,打开这个新的 JavaScript 文件并添加以下本地数据:
export default [
{
name: 'Cincinnati/Northern Kentucky International Airport',
abbreviation: 'CVG',
city: 'Hebron',
state: 'KY'
},
{
name: 'Seattle-Tacoma International Airport',
abbreviation: 'SEA',
city: 'Seattle',
state: 'WA'
},
{
name: 'Minneapolis-Saint Paul International Airport',
abbreviation: 'MSP',
city: 'Bloomington',
state: 'MN'
}
]
该数据是由美国几个机场组成的对象数组。这将在本教程后面的单文件组件中呈现。
保存并退出文件。
接下来,您将创建另一组机场数据。该数据将包括欧洲机场。使用该touch
命令,创建一个名为 的新 JavaScript 文件eu-airports.js
:
- touch data/eu-airports.js
然后打开文件并添加以下数据:
export default [
{
name: 'Paris-Charles de Gaulle Airport',
abbreviation: 'CDG',
city: 'Paris',
state: 'Ile de France'
},
{
name: 'Flughafen München',
abbreviation: 'MUC',
city: 'Munich',
state: 'Bavaria'
},
{
name: 'Fiumicino "Leonardo da Vinci" International Airport',
abbreviation: 'FCO',
city: 'Rome',
state: 'Lazio'
}
]
这组数据分别针对法国、德国和意大利的欧洲机场。
保存并退出文件。
接下来,在根目录中,在终端中运行以下命令以启动在本地开发服务器上运行的 Vue CLI 应用程序:
- npm run serve
这将在浏览器中打开应用程序localhost:8080
。您机器上的端口号可能不同。
在浏览器中访问该地址。您将看到以下启动屏幕:
接下来,启动一个新终端并打开App.vue
文件src
夹中的文件。在这个文件中,删除img
并HelloWorld
在标签<template>
和components
部分和import
在声明<script>
。您App.vue
将类似于以下内容:
<template>
</template>
<script>
export default {
name: 'App',
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
之后,导入us-airports.js
您之前创建的文件。为了使该数据具有反应性的,因此您可以在使用它<template>
,你需要导入ref
功能从vue
。您需要返回airport
引用,以便在 HTML 模板中使用数据。
添加以下突出显示的行:
<template>
<div class="wrapper">
<div v-for="airport in airports" :key="airport.abbreviation" class="card">
<p>{{ airport.abbreviation }}</p>
<p>{{ airport.name }}</p>
<p>{{ airport.city }}, {{ airport.state }}</p>
</div>
</div>
</template>
<script>
import { ref } from 'vue'
import data from '@/data/us-airports.js'
export default {
name: 'App',
setup() {
const airports = ref(data)
return { airports }
}
}
</script>
...
在此代码段中,您导入了数据并使用模板中的<div>
元素和v-for
指令进行渲染。
此时,数据已导入并准备在App.vue
组件中使用。但首先,添加一些样式以使用户更容易阅读数据。在同一个文件中,在<style>
标签中添加以下 CSS :
...
<style>
#app { ... }
.wrapper {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-column-gap: 1rem;
max-width: 960px;
margin: 0 auto;
}
.card {
border: 3px solid;
border-radius: .5rem;
padding: 1rem;
margin-bottom: 1rem;
}
.card p:first-child {
font-weight: bold;
font-size: 2.5rem;
margin: 1rem 0;
}
.card p:last-child {
font-style: italic;
font-size: .8rem;
}
</style>
在本例中,您使用CSS Grid将这些机场代码卡片组合成三个网格。注意这个网格是如何在.wrapper
类中设置的。该.card
班是一个包含各机场代码,名称和位置的卡或部分。如果您想了解有关 CSS 的更多信息,请查看我们的如何使用 CSS 设置 HTML 样式。
打开浏览器并导航到localhost:8080
。您会找到许多带有机场代码和信息的卡片:
现在您已经设置了初始应用程序,您可以在下一步中将数据重构为单文件组件。
步骤 2 — 创建单文件组件
由于 Vue CLI 使用Webpack将您的应用程序构建为浏览器可以读取的内容,因此您的应用程序可以使用 SFC 或.vue
文件而不是纯 JavaScript。这些文件是您创建可扩展和可重用代码的小块的一种方式。如果您要更改一个组件,它将随处更新。
这些.vue
部件通常由这三样东西:<template>
,<script>
,和<style>
元素。SFC 组件可以具有作用域或非作用域样式。当组件具有范围样式时,这意味着<style>
标签之间的 CSS只会影响<template>
同一文件中的 HTML 。如果组件具有非作用域样式,则 CSS 将影响父组件及其子组件。
成功设置您的项目后,您现在将把这些机场卡分解成一个名为AirportCards.vue
. 就目前而言, 中的 HTMLApp.vue
不是很可重用。您将把它分解成它自己的组件,这样您就可以将其导入到此应用程序的任何其他地方,同时保留功能和视觉效果。
在您的终端中,.vue
在components
目录中创建此文件:
- touch src/components/AirportCards.vue
AiportCards.vue
在文本编辑器中打开组件。为了说明如何使用组件重用代码块,请将大部分代码从App.vue
文件移动到AirportCards.vue
组件:
<template>
<div class="wrapper">
<div v-for="airport in airports" :key="airport.abbreviation" class="card">
<p>{{ airport.abbreviation }}</p>
<p>{{ airport.name }}</p>
<p>{{ airport.city }}, {{ airport.state }}</p>
</div>
</div>
</template>
<script>
import { ref } from 'vue'
import data from '@/data/us-airports.js'
export default {
name: 'Airports',
setup() {
const airports = ref(data)
return { airports }
}
}
</script>
<style scoped>
.wrapper {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-column-gap: 1rem;
max-width: 960px;
margin: 0 auto;
}
.card {
border: 3px solid;
border-radius: .5rem;
padding: 1rem;
margin-bottom: 1rem;
}
.card p:first-child {
font-weight: bold;
font-size: 2.5rem;
margin: 1rem 0;
}
.card p:last-child {
font-style: italic;
font-size: .8rem;
}
</style>
保存并关闭文件。
接下来,打开您的App.vue
文件。现在您可以清理App.vue
组件并导入AirportCards.vue
其中:
<template>
<AirportCards />
</template>
<script>
import AirportCards from '@/components/Airports.vue'
export default {
name: 'App',
components: {
AirportCards
}
}
</script>
<style scoped>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
现在这AirportCards
是一个独立的组件,您已经将它<template>
像放置<p>
标签一样放入HTML 中。
当您localhost:8080
在浏览器中打开时,什么都不会改变。仍然会显示相同的三张机场卡,因为您正在<AirportCards />
元素中渲染新的 SFC 。
接下来,再次在模板中添加相同的组件以说明组件的可重用性:
<template>
<AirportCards />
<airport-cards />
</template>
...
您可能会注意到,这个新实例在PascalCase 上AirportCards.vue
使用了kebab -case。在引用组件时,Vue 并不关心你使用的是哪一个。所有大写的单词和字母都用连字符分隔,并且都是小写的。这同样适用于 props,这将在下一节中解释。
注意:您使用的情况取决于个人喜好,但一致性很重要。Vue.js 推荐使用 kebab-case,因为它遵循 HTML 标准。
打开浏览器并访问localhost:8080
。您会发现卡片重复:
这为您的应用程序增加了模块化,但数据仍然是静态的。如果要显示相同的三个机场,卡片行很有用,但更改数据源需要更改硬编码数据。在下一步中,您将通过注册 props 并将数据从父组件向下传递到子组件来进一步扩展此组件。
第 3 步——利用 props 传递数据
在上一步中,您创建了一个AirportCards.vue
组件,该组件根据us-airports.js
文件中的数据呈现许多卡片。除此之外,您还将相同的组件引用加倍,以说明如何通过在<template>
.
但是,让数据保持静态会使以后难以更改数据。在使用 SFC 时,如果您将组件视为功能,它会有所帮助。这些函数是可以接受参数(props)并返回一些东西(HTML)的组件。对于这种情况,您将数据传递到 airport 参数以返回动态 HTML。
AirportCards.vue
在文本编辑器中打开组件。您当前正在data
从us-airports.js
文件导入。去掉这个import语句,以及标签中的setup
函数<script>
:
...
<script>
export default {
name: 'Airports',
}
</script>
...
保存文件。此时,浏览器中不会呈现任何内容。
接下来,通过定义一个道具继续前进。这个道具可以命名为任何东西;它只是描述传入的数据并将其与名称相关联。
要创建道具,请props
在组件中添加属性。它的值是一系列键/值对。键是道具的名称,值是数据的描述。最好提供尽可能多的描述:
...
<script>
export default {
name: 'Airports',
props: {
airports: {
type: Array,
required: true
}
}
}
</script>
...
现在,在 中AirportCard.vue
,该属性airports
指的是传入的数据。保存并退出文件。
接下来,App.vue
在文本编辑器中打开组件。像以前一样,你需要import
从数据us-airports.js
文件,并import
在ref
从Vue的功能,使之成为HTML模板具有反应性:
<template>
<AirportCards :airports="usAirports" />
<airport-cards />
</template>
<script>
import { ref } from 'vue'
import AirportCards from '@/components/Airports.vue'
import usAirportData from '@/data/us-airports.js'
export default {
name: 'App',
components: {
AirportCards
},
setup() {
const usAirports = ref(usAirportData)
return { usAirports }
}
}
</script>
如果您打开浏览器并访问localhost:8080
,您会发现与以前相同的美国机场:
AirportCards.vue
您的模板中有另一个实例。由于您在该组件中定义了道具,因此您可以传递具有相同结构的任何数据来渲染来自不同机场的许多卡片。这是eu-airports.js
来自初始设置的文件的来源。
在 中App.vue
,导入eu-airports.js
文件,将其包装在ref
函数中以使其具有反应性,然后返回它:
<template>
<AirportCards :airports="usAirports" />
<airport-cards :airports="euAirports" />
</template>
<script>
...
import usAirportData from '@/data/us-airports.js'
import euAirportData from '@/data/eu-airports.js'
export default {
...
setup() {
const usAirports = ref(usAirportData)
const euAirports = ref(euAirportData)
return { usAirports, euAirports }
}
}
</script>
...
打开浏览器并访问localhost:8080
。您会在美国机场数据下方找到呈现的欧洲机场数据:
您现在已成功将不同的数据集传递到同一组件中。使用props
,您实际上是将数据重新分配给一个新名称,并使用该新名称来引用子组件中的数据。
在这一点上,这个应用程序开始变得更加动态。但是,您仍然可以做一些其他事情来使这更加集中和可重用。在 Vue.js 中,您可以使用称为slot 的东西。在下一步中,您将创建一个Card.vue
具有默认值的组件,该组件slot
将 HTML 注入到占位符中。
步骤 4 — 使用插槽创建通用卡组件
插槽是创建可重用组件的好方法,尤其是当您不知道该组件中的 HTML 是否相似时。在上一步中,您AirportCards.vue
使用不同的数据创建了另一个实例。在该示例中,每个 HTML 都是相同的。这是一个带有段落标签<div>
的v-for
循环。
打开终端并使用touch
命令创建一个新文件。该文件将被命名为Card.vue
:
- touch src/components/Card.vue
在文本编辑器中,打开新Card.vue
组件。您将从中获取一些 CSSAirportCards.vue
并将其添加到这个新组件中。
创建一个<style>
标签并添加以下 CSS:
<style>
.card {
border: 3px solid;
border-radius: .5rem;
padding: 1rem;
margin-bottom: 1rem;
}
</style>
接下来,为此组件创建 HTML 模板。在<style>
标签之前,添加一个<template>
带有以下内容的标签:
<template>
<div class="card">
</div>
</template>
...
在 之间<div class="card">
,添加<slot />
组件。这是 Vue 提供给你的一个组件。不需要导入这个组件;它由 Vue.js 全局导入:
<template>
<div class="card">
<slot />
</div>
</template>
这slot
是在Card.vue
其他地方引用组件标记之间的 HTML 的占位符。
保存并退出文件。
现在回到AirportCards.vue
组件。首先,导入Card.vue
您刚刚创建的新SFC:
...
<script>
import Card from '@/components/Card.vue'
export default {
name: 'Airports',
props: { ... },
components: {
Card
}
}
</script>
...
现在,所有剩下的就是更换<div>
有<card>
:
<template>
<div class="wrapper">
<card v-for="airport in airports" :key="airport.abbreviation">
<p>{{ airport.abbreviation }}</p>
<p>{{ airport.name }}</p>
<p>{{ airport.city }}, {{ airport.state }}</p>
</card>
</div>
</template>
...
由于您<slot />
的Card.vue
组件中有 ,<card>
标签之间的 HTML被注入到它的位置,同时保留与卡片关联的所有样式。
保存文件。当您在 处打开浏览器时localhost:8080
,您会发现与以前拥有的相同的卡片。现在的区别在于您AirportCards.vue
现在引用了该Card.vue
组件:
要展示插槽的威力,请App.vue
在您的应用程序中打开该组件并导入该Card.vue
组件:
...
<script>
...
import Card from '@/components/Card.vue'
export default {
...
components: {
AirportCards,
Card
},
setup() { ... }
}
</script>
...
在 中<template>
,在<airport-cards />
实例下添加以下内容:
<template>
<AirportCards :airports="usAirports"/>
<airport-cards :airports="euAirports" />
<card>
<p>US Airports</p>
<p>Total: {{ usAirports.length }}</p>
</card>
<card>
<p>EU Airports</p>
<p>Total: {{ euAirports.length }}</p>
</card>
</template>
...
保存文件并localhost:8080
在浏览器中访问。您的浏览器现在将呈现显示数据集中机场数量的其他元素:
<card />
标签之间的 HTML并不完全相同,但它仍然呈现通用卡片。在利用插槽时,您可以使用此功能创建具有多种不同用途的小型、可重复使用的组件。
结论
在本教程中,您创建了单文件组件并使用props
和slots
创建了可重用的代码块。在该项目中,您创建了一个AirportCards.vue
用于渲染许多机场卡片的组件。然后,您将AirportCards.vue
组件进一步分解为Card.vue
具有默认插槽的组件。
您最终得到了许多动态组件,可用于多种不同用途,同时保持代码可维护性并符合DRY软件原则。
要了解更多 Vue 组件,建议通过Vue 文档准备好。有关 Vue 的更多教程,请查看如何使用 Vue.js 系列页面开发网站。