Vue 路由器入门指南

发布一下 0 0

在本教程中,我们将研究如何使用 Vue Router 在 Vue 应用程序中实现路由。所以我们可以进行动手实践,我们将使用 Vue 和 Vue Router 构建一个简单的 Pokedex 应用程序。

具体来说,我们将介绍以下内容:

  • 设置路由器
  • 路由参数
  • 声明式和程序化导航
  • 嵌套路由
  • 404 页

每个允许创建单页应用程序的 JavaScript UI 框架都需要一种将用户从一个页面导航到另一个页面的方法。所有这些都需要在客户端通过将当前显示在页面上的视图与地址栏中的 URL 同步来进行管理。在 Vue 世界中,管理此类任务的 [官方库] 是 Vue Router。

与以往一样,本教程的代码可以在GitHub上找到。

先决条件

以下是必需的,以便您可以充分利用本教程:

  • HTML、CSS、JavaScript 和 Vue 的基本知识。如果您知道如何使用 Vue 在页面上呈现某些内容,那么您应该能够跟进。对 API 有一点了解也会有所帮助。
  • 您的机器上安装了Node.js和Vue CLI 。我们将在本教程中使用 Vue 3,因此请确保更新 Vue CLI。

应用概览

我们将构建一个 Pokedex 应用程序。它将包含三个页面:

  • 口袋妖怪列表页面。这是列出所有原始 151 口袋妖怪的默认页面。

  • 口袋妖怪页面。这是我们显示基本细节的地方,例如类型和描述。

  • 口袋妖怪详细信息页面。这是我们展示进化技能、能力和动作的地方。

设置应用程序

使用 Vue CLI 启动一个新的 Vue 应用程序:

vue create poke-vue-router

从列出的选项中选择 Vue 3:

Vue 路由器入门指南

完成后,在项目文件夹中导航并安装我们需要的库:

cd poke-vue-routernpm install vue-router@4 axios

请注意,我们使用的是 Vue Router 4 而不是 3,这是您使用 Google 搜索时显示的默认结果。它是在next.router.vuejs.org而不是router.vuejs.org。我们正在使用 Axios 向PokeAPI v2发出请求。

此时,最好运行项目以确保默认的 Vue 应用程序正常工作:

npm run serve

访问http://localhost:8080/您的浏览器并检查默认的 Vue 应用程序是否正在运行。它应该显示如下内容:

Vue 路由器入门指南

接下来,您需要添加sass-loader作为开发依赖项。出于本教程的目的,最好只安装我使用的相同版本。这是因为,在撰写本文时,最新版本与 Vue 3 不兼容:

npm install sass-loader@10.1.1 --save-dev

node-sass出于与上述相同的原因,您还需要安装。最好坚持使用与我相同的版本:

npm install node-sass@4.14.1 --save

注意:如果以这种方式安装 Sass 对您不起作用,您还可以在使用 CLI 创建 Vue 应用程序时选择手动选择功能。然后,选择CSS Preprocessors并选择Sass/SCSS (with dart-sass)

创建应用程序

现在我们准备开始构建应用程序。在您继续操作时,请记住根目录是src文件夹。

从更新main.js文件开始。这是我们导入根组件App.vue和router/index.js声明与路由相关的所有内容的文件的地方:

// main.jsimport { createApp } from "vue";import App from "./App.vue";import router from "./router";const app = createApp(App);app.use(router);app.mount("#app");

设置路由器

在App.vue文件中,使用router-viewVue Router 提供的组件。这是 Vue Router 使用的最顶层组件,它为用户访问的当前路径呈现相应的组件:

// App.vue<template>  <div id="app">    <router-view />  </div></template><script>export default {  name: "App",};</script>

接下来,创建一个新router/index.js文件并添加以下内容。要创建路由器,我们需要从 Vue Router中提取createRouter和。允许我们创建一个新的路由器实例,同时创建一个 HTML5 历史记录,它基本上是History API的包装器。当我们在页面之间导航时,它允许 Vue Router 操作地址栏中的地址:createWebHistorycreateRoutercreateWebHistory

// router/index.jsimport { createRouter, createWebHistory } from "vue-router";

在此之下,导入我们将使用的所有页面:

import PokemonList from "../views/PokemonList.vue";

pathVue Router 需要一个包含, name, 和component作为其属性的对象数组:

  • path: 这是你想要匹配的模式。在下面的代码中,我们正在匹配根路径。因此,如果用户尝试访问http://localhost:8000,则匹配此模式。
  • name:页面的名称。这是页面的唯一标识符,当您想从其他页面导航到此页面时,您将使用该标识符。
  • componentpath:当与用户访问的 URL 匹配时要呈现的组件。
const routes = [  {    path: "/",    name: "PokemonList",    component: PokemonList,  },];

最后,通过提供一个包含 thehistory和routesto的对象来创建路由器实例createRouter:

const router = createRouter({  history: createWebHistory(),  routes,});export default router;

这就是我们现在所需要的。您可能想知道其他页面在哪里。我们将在稍后添加它们。现在,让我们先在默认页面上工作。

创建页面

创建页面实际上并不需要任何特殊代码。所以如果你知道如何在 Vue 中创建自定义组件,你应该能够创建一个页面供 Vue Router 使用。

创建一个views/PokemonList.vue文件并添加下面的代码。在此文件中,我们使用自定义List组件来呈现 Pokemon 列表。我们真正需要做的唯一一件事就是提供数据供List组件使用。安装组件后,我们向 PokeAPI 发出请求。我们不希望列表变得太大,因此我们将结果限制在最初的 151 个 Pokemon。一旦我们得到结果,我们只需将其分配给组件的items数据。这将反过来更新List组件:

<template>  <List :items="items" /></template><script>import axios from "axios";import List from "../components/List.vue";export default {  name: "PokemonList",  data() {    return {      items: null,    };  },  mounted() {    axios.get(`https://pokeapi.co/api/v2/pokemon?limit=151`).then((res) => {      if (res.data && res.data.results) {        this.items = res.data.results;      }    });  },  components: {    List,  },};</script>

这是List组件的代码。组件存储在components目录中,因此创建一个components/List.vue文件并添加以下内容:

<template>  <div v-if="items">    <router-link      :to="{ name: 'Pokemon', params: { name: row.name } }"      class="link"      v-for="row in items"      :key="row.name"    >      <div class="list-item">        {{ row.name }}      </div>    </router-link>  </div></template><script>export default {  name: "List",  props: {    items: {      type: Array,    },  },};</script><style lang="scss" scoped>@import "../styles/list.scss";</style>

您可以在GitHub存储库中查看该styles/list.scss文件的代码。

此时,您现在可以在浏览器中查看更改。除非您收到以下错误:

Vue 路由器入门指南

这是因为 Vue 正在尝试生成到 Pokemon 页面的链接,但还没有。Vue CLI 足够聪明,可以警告你。您可以使用 a<div>代替components/List.vue文件模板来临时解决此问题:

<template>  <div v-if="items">    <div v-for="row in items" :key="row.name">{{ row.name }}</div>  </div></template>

这样,您应该能够看到口袋妖怪的列表。添加 Pokemon 页面后,请记住稍后将其更改回来。

声明式导航

使用 Vue Router,您可以通过两种方式导航:声明式和编程式。声明式导航与我们在 HTML 中使用锚标记所做的几乎相同。您只需声明您希望链接导航到的位置。另一方面,编程导航是通过显式调用 Vue Router 来完成的,以在执行用户操作(例如单击按钮按钮)时导航到特定页面。

让我们快速分解它是如何工作的。要导航,您需要使用该router-link组件。这需要的唯一属性是:to. 这是一个包含name要导航到的页面的对象,以及params用于指定要传递给页面的参数的可选对象。在这种情况下,我们传入 Pokemon 的名称:

<router-link  :to="{ name: 'Pokemon', params: { name: row.name } }"  class="link"  v-for="row in items"  :key="row.name">  <div class="list-item">    {{ row.name }}  </div></router-link>

要可视化其工作原理,您需要了解Pokemon屏幕使用的模式。这是它的样子/pokemon/:name::name表示name您传入的参数。例如,如果用户想查看皮卡丘,则 URL 将如下所示http://localhost:8000/pokemon/pikachu。我们稍后会更详细地讨论这个问题。

路由参数

我们已经看到了如何为我们的路线匹配特定的模式,但我们还没有完成如何传入自定义参数。我们已经通过router-link前面的示例简要地看到了它。

我们将使用下一页 ( Pokemon) 来说明路由参数在 Vue Router 中是如何工作的。为此,您只需在参数名称前加上冒号 ( :)。在下面的例子中,我们想传入 Pokemon 的名字,所以我们添加了:name. 这意味着如果我们想导航到这个特定的路线,我们需要为这个参数传入一个值。正如我们在router-link前面的示例中看到的,这是我们传递口袋妖怪名称的地方:

// router/index.jsimport PokemonList from "../views/PokemonList.vue";import Pokemon from "../views/Pokemon"; // add thisconst routes = [  {    path: "/",    name: "PokemonList",    component: PokemonList,  },  // add this:  {    path: "/pokemon/:name",    name: "Pokemon",    component: Pokemon,  }]

这是Pokemon页面 ( views/Pokemon.vue) 的代码。就像之前的 PokemonList 页面一样,我们将渲染 UI 的任务委托给一个单独的组件BasicDetails。当组件被挂载时,我们向 API 的/pokemon端点发出请求。要获取作为路由参数传入的口袋妖怪名称,我们使用this.$route.params.name. 我们正在访问的属性应该与您在router/index.js文件中为参数指定的名称相同。在这种情况下,它是name. 如果您用于替代,您可以使用以下方式访问/pokemon/:pokemon_name它:paththis.$route.params.pokemon_name

<template>  <BasicDetails :pokemon="pokemon" /></template><script>import axios from "axios";import BasicDetails from "../components/BasicDetails.vue";export default {  name: "Pokemon",  data() {    return {      pokemon: null,    };  },  mounted() {    const pokemon_name = this.$route.params.name;    axios      .get(`https://pokeapi.co/api/v2/pokemon/${pokemon_name}`)      .then((res) => {        const data = res.data;        axios          .get(`https://pokeapi.co/api/v2/pokemon-species/${pokemon_name}`)          .then((res) => {            Object.assign(data, {              description: res.data.flavor_text_entries[0].flavor_text,              specie_id: res.data.evolution_chain.url.split("/")[6],            });            this.pokemon = data;          });      });  },  components: {    BasicDetails,  },};</script>

以下是BasicDetails组件 ( components/BasicDetails.vue) 的代码:

<template>  <div v-if="pokemon">    <img :src="pokemon.sprites.front_default" :alt="pokemon.name" />    <h1>{{ pokemon.name }}</h1>    <div class="types">      <div        class="type-box"        v-for="row in pokemon.types"        :key="row.slot"        v-bind:class="row.type.name.toLowerCase()"      >        {{ row.type.name }}      </div>    </div>    <div class="description">    {{ pokemon.description }}    </div>    <a @click="moreDetails" class="link">More Details</a>  </div></template><script>export default {  name: "BasicDetails",  props: {    pokemon: {      type: Object,    },  },  methods: {    moreDetails() {      this.$router.push({        name: "PokemonDetails",        params: {          name: this.pokemon.name,          specie_id: this.pokemon.specie_id,        },      });    },  },};</script><style lang="scss" scoped>@import "../styles/types.scss";@import "../styles/pokemon.scss";</style>

您可以在GitHub存储库中查看styles/types.scssandstyles/pokemon.scss文件的代码。

此时,您应该能够再次在浏览器中看到更改。您还可以将components/List.vue文件更新回其原始代码,router-link而不是<div>.

程序化导航

您可能已经注意到我们在BasicDetails组件中做了一些不同的事情。我们并没有真正导航到PokemonDetails使用router-link. 相反,我们使用了一个锚元素并拦截了它的点击事件。这就是程序化导航的实现方式。我们可以通过 访问路由器this.$router。然后我们调用该push()方法在历史堆栈顶部推送一个新页面。路由器将显示顶部的任何页面。当用户单击浏览器的后退按钮时,此方法允许导航回上一页,因为单击它只是将当前页面“弹出”到历史堆栈顶部。这个方法接受一个包含name和params属性的对象,所以它和你传递给to中的财产router-link:

methods: {  moreDetails() {    this.$router.push({      name: "PokemonDetails",      params: {        name: this.pokemon.name,        specie_id: this.pokemon.specie_id,      },    });  },},

嵌套路由

接下来,更新路由器文件以包含 Pokemon 详细信息页面的路径。在这里,我们使用嵌套路由来传递多个自定义参数。在这种情况下,我们传入nameand specie_id:

import Pokemon from "../views/Pokemon";import PokemonDetails from "../views/PokemonDetails"; // add thisconst routes = [  // ..  {    path: "/pokemon/:name",    // ..  },  // add these  {    path: "/pokemon/:name/:specie_id/details",    name: "PokemonDetails",    component: PokemonDetails,  },];

这是PokemonDetails页面的代码(views/PokemonDetails.vue):

<template>  <MoreDetails :pokemon="pokemon" /></template><script>import axios from "axios";import MoreDetails from "../components/MoreDetails.vue";export default {  name: "PokemonDetails",  data() {    return {      pokemon: null,    };  },  mounted() {    const pokemon_name = this.$route.params.name;    axios      .get(`https://pokeapi.co/api/v2/pokemon/${pokemon_name}`)      .then((res) => {        const data = res.data;        axios.get(`https://pokeapi.co/api/v2/evolution-chain/${this.$route.params.specie_id}`)        .then((res) => {          let evolution_chain = [res.data.chain.species.name];          if (res.data.chain.evolves_to.length > 0) {            evolution_chain.push(              res.data.chain.evolves_to[0].species.name            );            if (res.data.chain.evolves_to.length > 1) {              const evolutions = res.data.chain.evolves_to.map((item) => {                return item.species.name;              }            );            evolution_chain[1] = evolutions.join(" | ");          }          if (            res.data.chain.evolves_to[0].evolves_to.length >            0          ) {            evolution_chain.push(res.data.chain.evolves_to[0].evolves_to[0].species.name);          }            Object.assign(data, {              evolution_chain,            });          }          this.pokemon = data;        });    });  },  components: {    MoreDetails,  },};</script>

以下是MoreDetails组件的代码 ( components/MoreDetails.vue):

<template>  <div v-if="pokemon">    <h1>{{ pokemon.name }}</h1>    <div v-if="pokemon.evolution_chain" class="section">      <h2>Evolution Chain</h2>      <span v-for="(name, index) in pokemon.evolution_chain" :key="name">        <span v-if="index">-></span>        {{ name }}      </span>    </div>    <div v-if="pokemon.abilities" class="section">      <h2>Abilities</h2>      <div v-for="row in pokemon.abilities" :key="row.ability.name">        {{ row.ability.name }}      </div>    </div>    <div v-if="pokemon.moves" class="section">      <h2>Moves</h2>      <div v-for="row in pokemon.moves" :key="row.move.name">        {{ row.move.name }}      </div>    </div>  </div></template><script>export default {  name: "MoreDetails",  props: {    pokemon: {      type: Object,    },  },};</script><style lang="scss" scoped>@import "../styles/more-details.scss";</style>

您可以在GitHubstyles/more-details.scss存储库上查看文件的内容。

此时,您可以单击任何口袋妖怪名称并查看单个口袋妖怪的详细信息。您可能需要重新启动服务器才能看到更改。

404页

我们已经为所有页面添加了代码。但是如果用户在浏览器的地址栏中输入了一个无效的 URL 会发生什么?在这些情况下,它只会出错或根本不显示任何内容。我们需要添加一种方法来拦截这些请求,以便我们可以显示“404 not found”页面。

为此,请打开路由器文件并导入NotFound页面:

import NotFound from "../views/NotFound";

路由的优先级基于它们在路由数组中添加的顺序。这意味着最先添加的是第一个与用户在地址栏输入的 URL 匹配的。所以必须最后添加 404 页面的模式。

在routes数组中,添加以下内容:

const routes = [  // ..  {    path: "/pokemon/:name/:specie_id/details",    // ..  },  // add this  {    path: "/:pathMatch(.*)*",    name: "NotFound",    component: NotFound,  },];

是不是path很眼熟?我们使用一个自定义参数pathMatch来匹配输入的任何 URL。因此,如果用户输入http://localhost:8000/heyor http://localhost:8000/hey/jude,它将呈现NotFound页面。

这一切都很好。但是,如果在 catch-all 模式之上的模式实际上是匹配的,会发生什么呢?例如:

  • http://localhost:8000/pokemon/someinvalidpokemon
  • http://localhost:8000/pokemon/someinvalidpokemon/99999/details

在这些情况下,catch-all 模式将不匹配,因此我们需要一种方法来拦截此类请求。

这类请求的主要问题是用户假设某个口袋妖怪或物种 ID 存在,但事实并非如此。检查的唯一方法是拥有有效口袋妖怪的列表。在您的路线文件中,导入有效口袋妖怪列表:

import NotFound from "../views/NotFound";import valid_pokemon from "../data/valid-pokemon.json"; // add this

您可以在GitHub存储库中找到此文件。

为了拦截这些类型的请求,Vue Router 提供了导航守卫。将它们视为导航过程的“挂钩”,允许您在 Vue Router 导航到某个页面之前或之后执行某些操作。我们只会在导航完成之前执行一个,因为如果我们导航到该页面的条件不匹配,这允许我们重定向到另一个页面。

为了在导航完成之前挂钩当前请求,我们调用实例beforeEach()上的方法:router

const router = createRouter({  // ..});router.beforeEach(async (to) => {  // next: add the condition for navigating to the 404 page});

Vue Router 传递两个参数给它:

  • to:目标路线位置
  • from:当前路线位置

每一个都包含这些属性。我们感兴趣的是params,因为它包含用户在 URL 中传递的任何参数。

这是我们的情况。我们首先检查我们要检查的参数是否存在。如果是,我们将继续检查它是否有效。第一个条件与Pokemon页面匹配。我们使用valid_pokemon前面的数组。我们将其与 比较to.params.name,其中包含用户传递的 Pokemon 的名称。另一方面,第二个条件匹配PokemonDetails页面。在这里,我们正在检查物种 ID。由于我们只想匹配原始的 101 Pokemon,任何大于该值的 ID 都被视为无效。如果它符合这些条件中的任何一个,我们只需返回 404 页面的路径。如果条件不匹配,它将导航到最初打算导航到的位置:

if (  to.params &&  to.params.name &&  valid_pokemon.indexOf(to.params.name) === -1) {  return "/404";}if (  (to.params &&    to.params.name &&    to.params.specie_id &&    valid_pokemon.indexOf(to.params.name) === -1 &&    to.params.specie_id < 0) ||  to.params.specie_id > 101) {  return "/404";}

这是 404 页面的代码 ( views/NotFound.vue):

<template>  <h1>404 Not Found</h1></template><script>export default {  name: "Not Found",};</script><style lang="scss" scoped>@import "../styles/notfound.scss";</style>

您可以在GitHub存储库上查看该styles/notfound.scss文件的代码。

至此,应用程序就完成了!您可以尝试访问无效页面,它会返回一个 404 页面。

结论

而已!在本教程中,您学习了使用 Vue Router 的基础知识。诸如设置路由器、传递自定义参数、在页面之间导航以及实现 404 页面之类的事情将为您带来很长的路要走。如果您想了解从这里开始的方向,我建议您探索以下主题:

  • 将 props 传递给路由组件:允许您将视图组件与路由参数分离。这提供了一种将路由参数与可以从组件访问的道具交换的方法。这样你就可以在没有$route.params.
  • 过渡:用于动画页面之间的过渡。
  • 延迟加载:这更多是一种性能改进,因此捆绑程序不会在单个文件中编译所有页面的代码。相反,它将延迟加载它,以便浏览器仅在需要时下载特定页面的代码。

如果本文对你有帮助,别忘记给我个3连 ,点赞,转发,评论,,咱们下期见。

收藏 等于白嫖,点赞才是真情。

亲爱的小伙伴们,有需要JAVA面试文档资料请点赞+转发,关注我后,私信我333就可以领取免费资料哦

版权声明:内容来源于互联网和用户投稿 如有侵权请联系删除

本文地址:http://0561fc.cn/70979.html