如何使用Slots、Mixins和CompositionAPI使您的Vue.js应用程序干燥

来自菜鸟教程
跳转至:导航、​搜索

作者选择 Open Sourcing Mental Illness 作为 Write for DOnations 计划的一部分来接受捐赠。

介绍

DRY 是一种编程策略,代表“不要重复自己”。 它通过模块化架构鼓励代码可重用性,其中代码被重用而不是重复。 这通常会产生动态且可扩展的代码。 简而言之,这个原则指导程序员避免重复代码和硬编码应用程序中的任何值。

Vue.js 包含多种策略来模块化和重用重复的代码片段。 在本教程中,您将通过使示例 Vue.js 应用程序 DRY 来尝试这些策略。 本教程将介绍在 组件 中保持 template 和 script DRY 的方法。 您将使用建立 HTML 结构的布局组件,您可以通过 slots 将内容放入其中。 然后,您将使用 mixins,它们是包含与现有组件选项混合在一起的 data、methods 和 computed 属性的 JavaScript 文件。 最后,您将使用 Vue 3 中引入的新 Composition API。 组合 API 是一种构建组件的不同方式,并促进了组件属性的统一。

先决条件

第 1 步 — 设置示例应用程序

为了说明如何创建可扩展且 DRY 的 Vue.js 代码,您将首先设置一个示例应用程序。 此示例应用程序将是一个显示机场卡列表的主/详细应用程序。 单击后,这些卡片会将您导航到另一个视图,其中包含该机场的更多详细信息。

首先,您需要创建一个新的 Vue.js 应用程序。 通过在终端中运行以下命令来执行此操作:

vue create favorite-airports

出现提示时,选择 Manually select features。 以下可供选择的选项是:Choose Vue version、Babel 和 Router。 选中后,点击【X23X】【X27X】键,继续填写提示如下:

OutputVue CLI v4.5.15
? Please pick a preset: Manually select features
? Check the features needed for your project: Choose Vue version, Babel, Router
? Choose a version of Vue.js that you want to start the project with 3.x
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files

创建 favorite-airports 项目后,打开终端窗口并将 cd(更改目录)到 favroite-airports 根文件夹。 更改目录后,创建一个新目录来保存此项目的所有本地数据:

mkdir src/data

在此文件夹中,创建一个名为 src/data/airports.js 的新 Javascript 文件并在您选择的文本编辑器中打开它。 要为您的应用程序提供示例数据,请将以下内容添加到文件中:

最喜欢的机场/src/data/airports.js

export default [
  {
    name: 'Cincinnati/Northern Kentucky International Airport',
    abbreviation: 'CVG',
    city: 'Hebron',
    state: 'KY',
    destinations: {
      passenger: [ 'Toronto', 'Seattle/Tacoma', 'Austin', 'Charleston', 'Denver', 'Fort Lauderdale', 'Jacksonville', 'Las Vegas', 'Los Angeles', 'Baltimore', 'Chicago', 'Detroit', 'Dallas', 'Tampa' ],
      cargo: [ 'Anchorage', 'Baltimore', ' Chicago' , 'Indianapolis', 'Phoenix', 'San Francisco', 'Seattle', 'Louisville', 'Memphis' ]
    }
  },
  {
    name: 'Seattle-Tacoma International Airport',
    abbreviation: 'SEA',
    city: 'Seattle',
    state: 'WA',
    destinations: {
      passenger: [ 'Dublin', 'Mexico City', 'Vancouver', 'Albuquerque', 'Atlanta', 'Frankfurt', 'Amsterdam', 'Salt Lake City', 'Tokyo', 'Honolulu' ],
      cargo: [ 'Spokane', 'Chicago', 'Dallas', ' Shanghai', 'Cincinnati', 'Luxenbourg', 'Anchorage', 'Juneau', 'Calgary', 'Ontario' ]
    }
  },
  {
    name: 'Minneapolis-Saint Paul International Airport',
    abbreviation: 'MSP',
    city: 'Bloomington',
    state: 'MN',
    destinations: {
      passenger: [ 'Dublin', 'Paris', 'Punta Cana', 'Winnipeg', 'Tokyo', 'Denver', 'Tulsa', 'Washington DC', 'Orlando', 'Mexico City' ],
      cargo: [ 'Cincinnati', 'Omaha', 'Winnipeg', 'Chicago', 'St. Louis', 'Portland', 'Philadelphia', 'Milwaukee', 'Ontario' ]
    }
  }
]

这是一个由 objects 组成的 array,由美国的几个机场组成。 在此应用程序的主视图中,您将遍历此数据以生成由 name 、abbreviation、city 和 state 组成的卡片特性。

保存data/airports.js并返回终端。

完成该步骤后,创建一个名为 AirportCard.vue 的单文件组件 (SFC)。 该文件将位于项目的 components 目录中,并将包含机场卡的所有样式和逻辑。 在文本编辑器中打开 AirportCard.vue 并添加以下内容:

最喜欢的机场/src/components/AirportCard.vue

<template>
  <div class="airport">
    <p>{{ airport.abbreviation }}</p>
    <p>{{ airport.name }}</p>
    <p>{{ airport.city }}, {{ airport.state }}</p>
  </div>
</template>

<script>
export default {
  props: {
    airport: {
      type: Object,
      required: true
    }
  }
}
</script>

<style scoped>
.airport {
  border: 3px solid;
  border-radius: .5rem;
  padding: 1rem;
  margin-bottom: 1rem;
}

.airport p:first-child {
  font-weight: bold;
  font-size: 2.5rem;
  margin: 1rem 0;
}

.airport p:last-child {
  font-style: italic;
  font-size: .8rem;
}
</style>

您可能会注意到此代码片段中包含一些 CSS。 在 AirportCard.vue 组件中,包装器 <div> 包含 airport 类。 这个 CSS 通过添加边框为生成的 HTML 添加一些样式,使每个机场看起来像一张“卡片”。 :first-child 和 :last-child 是 伪 选择器,它们对 <div> 内 HTML 中的第一个和最后一个 <p> 标记应用不同的样式类为 airport 的元素。 除此之外,您可能还注意到该组件包含一个 prop,它在 Vue.js 中是一种将数据从父组件向下传递到子组件的方法。

保存并退出文件。

在结束此设置之前,将现有的 views/Home.vue 组件代码替换为以下代码:

最喜欢的机场/src/views/Home.vue

<template>
  <div class="wrapper">
    <div v-for="airport in airports" :key="airport.abbreviation">
      <airport-card :airport="airport" />
    </div>
  </div>
</template>

<script>
import allAirports from '@/data/airports.js'
import AirportCard from '@/components/AirportCard.vue'

export default {
  components: {
    AirportCard
  },
  data() {
    return {
      airports: allAirports
    }
  }
}
</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;
}

.wrapper {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-column-gap: 1rem;
  max-width: 960px;
  margin: 0 auto;
}

p,
h3 {
  grid-column: span 3;
}
</style>

此代码包含一个 v-for 循环,它遍历 airports.js 数据并渲染一系列 AirportCards.vue 组件,其中机场数据通过 prop [X172X ]。 保存此代码并返回命令行。

设置好项目后,在终端中使用 npm run serve 命令运行本地开发服务器:

npm run serve

这将在您的 localhost 上启动一个服务器,通常在端口 8080 上。 打开您选择的网络浏览器并访问 localhost:8080 以查看以下内容:

现在您的示例应用程序已设置完毕,下一步您将创建两个不同的 Vue.js 组件,稍后您可以将它们用作页面布局。

第 2 步 — 使用 slot 创建布局组件

布局组件是使用 slot 元素组成 HTML 模板的组件,这些模板可以与不同的内容重复使用。 当您有多个要重复使用的模板时,这些非常有用,例如两列或三列布局。

要创建布局组件,您首先要为它们创建一个目录。 您可以将它们放在 components 文件夹中,但由于这些组件具有非常特定的工作,因此如果您将它们区分开来,您的项目对于其他程序员来说会更易读。 在 src 目录下创建一个名为 layouts 的目录:

mkdir src/layouts

接下来,在您的 layouts 目录中创建一个名为 DefaultLayout.vue 的文件。 DefaultLayout.vue 组件将是一个包装器,它包含视图的内容并将其置于浏览器窗口的中心。 在文本编辑器中打开 DefaultLayout.vue 并添加以下内容:

最喜欢的机场/src/layouts/DefaultLayout.vue

<template>
  <div class="default-layout">
    <slot />
  </div>
</template>

<style scoped>
  .default-layout {
    max-width: 960px;
    margin: 0 auto;
  }
</style>

该组件是一个 div,其类为 default-layout。 你可以利用这个类来添加一些样式。 您在上面的组件中看到的 CSS 样式将其宽度限制为最大 960px,侧边距是自动的。 这将使 div 在浏览器窗口中水平居中。 slot 元素是默认插槽。 任何放置在两个 <layout-default> 标签之间的东西都会被注入到这个 <slot /> 所在的位置。 您可以通过重构您在上一步中修改的 Home.vue 来尝试一下。

保存您的 DefaultLayout.vue 文件。 在文本编辑器中,打开 src/views/Home.vue 和 import DefaultLayout.vue 组件。

最喜欢的机场/src/views/Home.vue

...
<script>
import allAirports from '@/data/airports.js'
import AirportCard from '@/components/AirportCard.vue'
import DefaultLayout from '@/layouts/DefaultLayout.vue'

export default {
  components: {
    AirportCard,
    DefaultLayout
  },
  data() {
    return {
      airports: allAirports
    }
  }
}
</script>
...

导入 DefaultLayout.vue 组件后,您现在可以将包含的 <div /> 替换为 <default-layout />。

最喜欢的机场/src/views/Home.vue

<template>
  <default-layout class="wrapper">
    <div v-for="airport in airports" :key="airport.abbreviation">
      <airport-card :airport="airport" />
    </div>
  </default-layout>
</template>
...

您现在可以删除包含 div 并居中的 .wrapper 类中的 max-width 和 margin 属性。 保存此文件,然后在浏览器窗口中打开 localhost:8080。 从视觉上看,什么都不会改变,但是您现在有了一个新的布局组件,可以在居中的 div 中包含任何内容。

在继续下一步之前,您将再创建一个布局组件。 这将是一个两列布局。 一列用于补充信息,另一列用于视图的主要内容。 在 src/layouts/TwoColumnLayout.vue 处创建一个新文件。 创建后,在文本编辑器中打开 TwoColumnLayout.vue 组件并添加以下内容:

最喜欢的机场/src/layouts/TwoColumnLayout.vue

<template>
  <div class="two-column-layout">
    <aside>
      <slot name="sideBar" />
    </aside>
    <main>
      <slot name="content" />
    </main>
  </div>
</template>

<style>
  .two-column-layout {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    grid-column-gap: 1rem;
  }

  .two-column-layout aside,
  .two-column-layout main {
    border: 1px solid;
    border-radius: 5px;
  }

  .two-column-layout aside {
    grid-column: span 1;
  }

  .two-column-layout main {
    grid-column: span 2;
  }
</style>

在这个组件中,您有两个命名槽,一个用于侧边栏,另一个用于主要内容。 在包含 <div> 上,您正在使用 CSS 创建一个包含三列的网格,其中一列跨越一列,另一列跨越两列。

要使用此布局,请在 src/views/AirportDetail.vue 处为机场详细视图创建一个新视图,然后将以下代码添加到新文件中:

最喜欢的机场/src/views/AirportDetail.vue

<template>
  <two-column-layout>
    <template v-slot:sideBar>
      <p>Sidebar</p>
    </template>
    <template v-slot:content>
      <p>Main Content</p>
    </template>
  </two-column-layout>
</template>

<script>
import TwoColumnLayout from '@/layouts/TwoColumnLayout.vue'

export default {
  components: {
    TwoColumnLayout
  },
}
</script>

这个新视图导入 TwoColumnLayout.vue 然后使用 v-slot 用正确的内容填充命名槽。

保存此文件。 要使该视图可见,请在 Vue 路由器 文件中添加路由:

最喜欢的机场/src/router/index.js

import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import AirportDetail from '../views/AirportDetail'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/airport/:code',
    name: 'AirportDetail',
    component: AirportDetail
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  }
]
...

在这里,您正在注册一个路由,当访问该路由时,它将加载 AirportDetail.vue 组件。 path 中的 :code 是一个参数,您可以稍后利用它来获取特定机场的数据。

保存文件,然后打开浏览器到 localhost:8080/airport/cvg。 您会发现以下内容:

在此步骤中,您通过利用插槽创建了布局组件。 这些布局组件可以通过在创建网页结构时消除重复代码来帮助保持您的应用程序干燥。 在下一步中,您将尝试使用 mixin 在组件之间共享方法和属性。

第 3 步 — 使用 Mixins 共享方法和属性

Mixin 是一种将可重用组件选项重新分配给任意数量的组件的方法。 导入 mixin 时,mixin 的组件选项会“混入”到当前组件中。 为了说明这一点,这一步将首先运行一个 mixin 语法示例,然后将 mixin 添加到您的示例应用程序中。

假设您有两个具有独特属性的文件。 第一个有一个 data 方法和一个计算属性,如下所示:

样品成分

<script>
  export default {
    data() {
      return {
        firstName: 'Dave',
        lastName: 'Berning'
      }
    },
    computed: {
      fullName() {
        return `${this.firstName} ${this.lastName}`
      }
    }
  }
</script>

第二个是包含一些您想要重用的组件选项的文件:

一些混音

export default {
  data() {
    return {
      counter: 0
    }
  },
  methods: {
    increment() {
      this.counter++
    }
  }
}

您可以通过将 mixin (someMixin) 导入组件 (sample-component) 来混合这两个文件。 在这个假设的组件中,您使用 import 关键字导入它并使用 mixin 属性对其进行分配:

样品成分

<script>
import someMixin from '@/mixins/someMixin'
      
export default {
  data() {
    return {
      firstName: 'Dave',
      lastName: 'Berning'
    }
  },
  mixins: [ 'someMixin' ],
  computed: {
    fullName() {
      return `${this.firstName} ${this.lastName}`
    }
  }
}
</script>

导入后,假设组件可以访问所有 methods、data、computed 属性以及它可能包含的任何其他组件选项。

接下来,您将创建一个包含 method 和 data 属性的 mixin。 此函数将结合机场 name 和 abbreviation 并将其存储到 data 属性中。

在您的终端中,使用 mkdir 命令创建一个新目录:

mkdir src/mixins

将名为 src/mixins/airport.js 和 export 的文件创建为包含以下属性的对象:

src/mixins/airport.js

export default {
  data() {
    return {
      airportWithCode: ''
    }
  },
  methods: {
    getAirportWithCode(airport) {
      this.airportWithCode = `${airport.name} - ${airport.abbreviation}`
    }
  }
}

该对象现在将具有 data 方法,并且方法将数据设置为机场的名称和缩写。 保存此文件。

创建完成后,将其导入 Home.vue 视图。 您将利用此 method 和 data 属性来显示用户单击卡片时返回的字符串:

src/views/Home.vue

<template>
  <default-layout class="wrapper">
    <div v-for="airport in airports" :key="airport.abbreviation" @click="getAirportWithCode(airport)">
      <airport-card :airport="airport" />
    </div>
    <p>test: {{ airportWithCode }}</p>
  </default-layout>
</template>

<script>
import allAirports from '@/data/airports.js'
import AirportCard from '@/components/AirportCard.vue'
import DefaultLayout from '@/layouts/DefaultLayout.vue'
import airportMixin from '@/mixins/airport.js'

  export default {
    components: {
      AirportCard,
      DefaultLayout
    },
    mixins: [ airportMixin ],
    data() {
      return {
        airports: allAirports
      }
    }
  }
</script>
...

由于您可以访问该 mixin 中的 methods 和 data,因此您可以像引用任何其他组件选项一样引用它们。 您已在此代码片段中完成此操作,以便在用户单击卡片时存储 airportWithCode 值,然后在段落元素中呈现字符串值。 保存文件。

接下来,在 AirportDetail.vue 中重复使用相同的 mixin。 在文本编辑器中打开 AirportDetail.vue,然后编写 JavaScript filter 以返回 airport 对象,如果 abbreviation 与 :code 参数匹配之前在路由器中定义:

src/views/AirportDetail.vue

...
<script>
import TwoColumnLayout from '@/layouts/TwoColumnLayout.vue'
import allAirports from '@/data/airports.js'
  
export default {
  components: {
    TwoColumnLayout
  },
  data() {
    return {
      airport: ''
    }
  },
  methods: {
  getAirportByCode() {
    return allAirports.filter(airport => airport.abbreviation === this.$route.params.code.toUpperCase())[0]
    }
  },
  mounted() {
    this.airport = this.getAirportByCode()
  }
}
</script>

在此代码段中,您将创建一个名为 getAirportByCode 的新 method,它过滤机场数据并返回其缩写与路线 URL 中的缩写匹配的机场对象。 安装时,您将 airport 数据属性分配给返回的对象。

保存文件。 接下来,导入您之前使用的 mixin。 您将利用与之前相同的 data 和 method 属性:

src/views/AirportDetail.vue

<template>
  <two-column-layout>
    <template v-slot:sideBar>
      <p>Sidebar</p>
    </template>
    <template v-slot:content>
      <p>Main Content</p>
      <p>{{ airportWithCode }}</p>
    </template>
  </two-column-layout>
</template>

<script>
import TwoColumnLayout from '@/layouts/TwoColumnLayout.vue'
import allAirports from '@/data/airports.js'
import airportMixin from '@/mixins/airport.js'

export default {
  components: {
    TwoColumnLayout
  },
  mixins: [ airportMixin ],
  data() { ... },
  methods: { ... },
  mounted() {
    this.airport = this.getAirportByCode()
    this.getAirportWithCode(this.airport)
  }
}
</script>

由于您已经用 getAirportByCode() 确定了具体机场并将其设置为 this.airport,您现在可以使用 getAirportWithCode 方法从混音。 然后,您可以通过将该变量添加到模板来显示该值。

保存此文件,然后在浏览器窗口中打开 localhost:8080/airport/cvg。 您会在主内容部分找到 airportWithCode 的字符串值,如下图所示:

在这一步中,您使用 mixin 在组件之间共享方法和计算属性。 Mixin 是组织组件和在整个应用程序中利用可重用代码的好方法。 接下来,您将了解新的 Composition API、创建它的原因以及它如何改进您的下一个 Vue 应用程序的组合。

第 4 步 — 使用组合 API

到目前为止,您在本教程中尝试过的 mixin 和布局组件可用于所有主要版本的 Vue,包括 Vue 2 和更早版本。 这些构成了 Options API。 但是在 Vue 3 中,您可以使用另一个 API 来使您的应用程序 DRY:Composition API。

组合 API 是一种设置组件的新方法。 无需为 data、computed、methods 和 props 分别设置部分,而是有一个 setup 钩子,一切都在其中. 在这个 setup 属性中,组件在创建之前需要操作的所有内容都将放在此处。 此外,需要导入在 Options API 中定义选项所需的所有内容。 但对于 Composition API 来说并非如此。

在这一步中,您将重构您的一个组件,从使用 Options API 到新的 Composition API。

在您的文本编辑器中,打开 AirportDetail.vue 组件。 现在,你有一个 mixin 被导入到这个组件中。 该 mixin 提供了一些函数和数据属性。 但是在 Composition API 中,您的组件需要渲染的所有内容都将存在于 setup 方法中; 无需导入此方法。

删除你的 mixin 导入,并在 mounted 下添加 setup 方法:

最喜欢的机场/src/views/AirportDetail.vue

...
<script>
import TwoColumnLayout from '@/layouts/TwoColumnLayout.vue'
import allAirports from '@/data/airports.js'

export default {
  components: { ... },
  methods: {
    getAirportByCode() {
      return allAirports.filter(airport => airport.abbreviation === this.$route.params.code.toUpperCase())[0]
    }
  },
  mounted() { ... },
  setup() {
  
  }
}
</script>

创建 setup 方法后,通过添加新的 onMounted 生命周期钩子开始重构它。 onMounted 函数接受一个参数,通常是匿名函数:

最喜欢的机场/src/views/AirportDetail.vue

...
<script>
import TwoColumnLayout from '@/layouts/TwoColumnLayout.vue'
import allAirports from '@/data/airports.js'
import { onMounted } from 'vue'

export default {
  components: { ... },
  methods: {
    getAirportByCode() {
      return allAirports.filter(airport => airport.abbreviation === this.$route.params.code.toUpperCase())[0]
    }
  },
  mounted() { ... },
  setup() {
    onMounted(() => {
      
    })
  }
}
</script>

安装此组件后,您会将当前机场对象存储到响应式数据属性中。 要使变量或常量反应,您需要将值包装在 ref 函数中,您将从 vue 导入该函数。 常量的名称将是您在组件的 setup 和 template 部分中引用的名称。 此外,从 methods 中删除 getAirportByCode 并将其定义为 setup 中的常规 JavaScript 函数:

最喜欢的机场/src/views/AirportDetail.vue

...
<script>
import TwoColumnLayout from '@/layouts/TwoColumnLayout.vue'
import allAirports from '@/data/airports.js'
import { onMounted, ref } from 'vue'

export default {
  components: { ... },
  mounted() { ... },
  setup() {
    function getAirportByCode() {
      return allAirports.filter(airport => airport.abbreviation === this.$route.params.code.toUpperCase())[0]
    }
      
    onMounted(() => {
      const airport = ref(getAirportByCode())
    })
  }
}
</script>

完成此操作后,您可以继续从组件文件中删除旧的 mounted 和 methods 属性。

由于您现在没有使用 mixin,因此您将在 setup 函数中定义 getAirportWithCode 并将其分配给变量 airportWithCode 以便您可以在视图中使用它:

最喜欢的机场/src/views/AirportDetail.vue

...
<script>
import TwoColumnLayout from '@/layouts/TwoColumnLayout.vue'
import allAirports from '@/data/airports.js'
import { onMounted, ref } from 'vue'

export default {
  components: {
    TwoColumnLayout
  },
  setup() {
    const airportWithCode = ref('')
    
    function getAirportByCode() {
      return allAirports.filter(airport => airport.abbreviation === this.$route.params.code.toUpperCase())[0]
    }
      
    function getAirportWithCode(airport) {
      return `${airport.name} - ${airport.abbreviation}`
    }

      onMounted(() => {
        const airport = ref(getAirportByCode())
      })
    }
  }
</script>

使用 Composition API 的反应式数据属性的一件非常重要的事情是 ref 返回一个对象。 要访问这些值,您需要访问其 .value 属性:

最喜欢的机场/src/views/AirportDetail.vue

...
<script>
import TwoColumnLayout from '@/layouts/TwoColumnLayout.vue'
import allAirports from '@/data/airports.js'
import { onMounted, ref } from 'vue'

export default {
  components: {
    TwoColumnLayout
  },
  setup() {
    const airportWithCode = ref('')

    function getAirportByCode() {
      return allAirports.filter(airport => airport.abbreviation === this.$route.params.code.toUpperCase())[0]
    }

    function getAirportWithCode(airport) {
      return `${airport.name} - ${airport.abbreviation}`
    }

    onMounted(() => {
      const airport = ref(getAirportByCode())
      airportWithCode.value = getAirportWithCode(airport.value)
    })
  }
}
</script>

现在,您需要做两件事才能将其完全转换为使用 Composition API。 您需要更改的第一件事是 getAirportByCode 函数中的 this.$route。 在 Composition API 中,您无法分别使用 this.$route 和 this.$router 访问路由或路由器。

要访问路由,请从 vue-router 包中导入 useRoute。 最好将其保存到 const 中,您可以在整个应用程序中引用它:

最喜欢的机场/src/views/AirportDetail.vue

...
<script>
import TwoColumnLayout from '@/layouts/TwoColumnLayout.vue'
import allAirports from '@/data/airports.js'
import { onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'

export default {
  components: {
    TwoColumnLayout
  },
  setup() {
    const route = useRoute()
    const airportWithCode = ref('')

    function getAirportByCode() {
      return allAirports.filter(airport => airport.abbreviation === route.params.code.toUpperCase())[0]
    }

    function getAirportWithCode(airport) {
      return `${airport.name} - ${airport.abbreviation}`
    }

    onMounted(() => {
      const airport = ref(getAirportByCode())
      airportWithCode.value = getAirportWithCode(airport.value)
    })
  }
}
</script>

完成后,在 setup 函数中返回对象。 此对象中返回的属性可以在 template 中使用:

最喜欢的机场/src/views/AirportDetail.vue

...
  setup() {
    const route = useRoute()
    const airportWithCode = ref('')

    function getAirportByCode() {
      return allAirports.filter(airport => airport.abbreviation === route.params.code.toUpperCase())[0]
    }

    function getAirportWithCode(airport) {
      return `${airport.name} - ${airport.abbreviation}`
    }

    onMounted(() => {
      const airport = ref(getAirportByCode())
      airportWithCode.value = getAirportWithCode(airport.value)
    })
    
    return { airportWithCode }
  }
}
</script>

保存您的代码并在浏览器中重新加载 localhost:8080/airport/cvg。 重构代码后,渲染的内容不会有任何变化。 但是,您现在正在利用 Composition API。

结论

在本教程中,您尝试了一些策略来使您的应用程序 DRY。 具体来说,您在多个视图中重用布局组件,然后使用 mixins 模块化属性和方法。 最后,您重构了您的应用程序以使用 Vue 3 中引入的新 Composition API。 此 API 是一种在创建组件之前设置组件的新方法,可在更多情况下发挥作用。

如果您想了解有关 Mixins 或 Composition API 的更多信息,强烈建议您查看官方 Vue 文档。 有关 Vue 的更多教程,请查看 如何使用 Vue.js 开发网站系列页面。