如何使用 Vue 单文件组件创建可重用的代码块

作者选择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 是什么以及如何实现代码重用有一个大致的了解。

先决条件

步骤 1 — 设置项目

在本教程中,您将创建一个机场卡组件,该组件在一系列卡中显示多个机场及其代码。遵循先决条件部分后,您将拥有一个名为sfc-project. 在本节中,您将数据导入到这个生成的应用程序中。此数据将是一个对象数组,其中包含一些用于在浏览器中显示信息的属性。

项目生成后,打开终端cd或将目录更改为根src文件夹:

  • cd sfc-project/src

从那里,新建一个名为目录datamkdir命令,然后创建一个名为新文件us-airports.js使用touch命令:

  • mkdir data
  • touch data/us-airports.js

在您选择的文本编辑器中,打开这个新的 JavaScript 文件并添加以下本地数据:

证监会项目/数据/us-airports.js
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

然后打开文件并添加以下数据:

证监会项目/数据/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您机器上的端口号可能不同。

在浏览器中访问该地址。您将看到以下启动屏幕:

Vue默认模板页面

接下来,启动一个新终端并打开App.vue文件src中的文件。在这个文件中,删除imgHelloWorld在标签<template>components部分和import在声明<script>App.vue将类似于以下内容:

证监会项目/src/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 模板中使用数据。

添加以下突出显示的行:

证监会项目/src/App.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: 'App',
  setup() {
    const airports = ref(data)

    return { airports }
  }
}
</script>
...

在此代码段中,您导入了数据并使用模板中的<div>元素和v-for指令进行渲染

此时,数据已导入并准备在App.vue组件中使用。但首先,添加一些样式以使用户更容易阅读数据。在同一个文件中,在<style>标签中添加以下 CSS

证监会项目/src/App.vue
...
<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不是很可重用。您将把它分解成它自己的组件,这样您就可以将其导入到此应用程序的任何其他地方,同时保留功能和视觉效果。

在您的终端中,.vuecomponents目录中创建此文件

  • touch src/components/AirportCards.vue

AiportCards.vue在文本编辑器中打开组件。为了说明如何使用组件重用代码块,请将大部分代码从App.vue文件移动到AirportCards.vue组件:

sfc-project/src/components/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其中:

证监会项目/src/App.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

接下来,再次在模板中添加相同的组件以说明组件的可重用性:

/src/App.vue
<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在文本编辑器中打开组件。您当前正在dataus-airports.js文件导入去掉这个import语句,以及标签中setup函数<script>

sfc-project/src/components/AirportCards.vue
...
<script>

export default {
  name: 'Airports',
}
</script>
...

保存文件。此时,浏览器中不会呈现任何内容。

接下来,通过定义一个道具继续前进。这个道具可以命名为任何东西;它只是描述传入的数据并将其与名称相关联。

要创建道具,请props在组件中添加属性。它的值是一系列键/值对。键是道具的名称,值是数据的描述。最好提供尽可能多的描述:

sfc-project/src/components/AirportCards.vue
...
<script>

export default {
  name: 'Airports',
  props: {
    airports: {
      type: Array,
      required: true
    }
  }
}
</script>
...

现在,在 中AirportCard.vue,该属性airports指的是传入的数据。保存并退出文件。

接下来,App.vue在文本编辑器中打开组件。像以前一样,你需要import从数据us-airports.js文件,并importref从Vue的功能,使之成为HTML模板具有反应性:

证监会项目/src/App.vue
<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函数中以使其具有反应性,然后返回它:

/src/App.vue
<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:

证监会项目/src/components/Card.vue
<style>
.card {
  border: 3px solid;
  border-radius: .5rem;
  padding: 1rem;
  margin-bottom: 1rem;
}
</style>

接下来,为此组件创建 HTML 模板。<style>标签之前,添加一个<template>带有以下内容标签:

证监会项目/src/components/Card.vue
<template>
  <div class="card">

  </div>
</template>
...

在 之间<div class="card">,添加<slot />组件。这是 Vue 提供给你的一个组件。不需要导入这个组件;它由 Vue.js 全局导入:

证监会项目/src/components/Card.vue
<template>
  <div class="card">
    <slot />
  </div>
</template>

slot是在Card.vue其他地方引用组件标记之间的 HTML 的占位符

保存并退出文件。

现在回到AirportCards.vue组件。首先,导入Card.vue您刚刚创建的新SFC:

sfc-project/src/components/AirportCards.vue
...
<script>
import Card from '@/components/Card.vue'

export default {
  name: 'Airports',
  props: { ... },
  components: {
    Card
  }
}
</script>
...

现在,所有剩下的就是更换<div><card>

/src/components/AirportCards.vue
<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组件:

证监会项目/src/App.vue
...
<script>
...
import Card from '@/components/Card.vue'

export default {
  ...
  components: {
    AirportCards,
    Card
  },
  setup() { ... }
}
</script>
...

在 中<template>,在<airport-cards />实例下添加以下内容

证监会项目/src/App.vue
<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并不完全相同,但它仍然呈现通用卡片。在利用插槽时,您可以使用此功能创建具有多种不同用途的小型、可重复使用的组件。

结论

在本教程中,您创建了单文件组件并使用propsslots创建了可重用的代码块。在该项目中,您创建了一个AirportCards.vue用于渲染许多机场卡片组件。然后,您将AirportCards.vue组件进一步分解为Card.vue具有默认插槽组件。

您最终得到了许多动态组件,可用于多种不同用途,同时保持代码可维护性并符合DRY软件原则。

要了解更多 Vue 组件,建议通过Vue 文档准备好有关 Vue 的更多教程,请查看如何使用 Vue.js 系列页面开发网站

觉得文章有用?

点个广告表达一下你的爱意吧 !😁