Vue Router的终极指南

571 阅读10分钟

简介

网络开发中的路由是一种机制,HTTP请求被路由到处理它们的代码中。简单地说,通过路由器,你可以决定当用户访问你网站上的某个页面时应该发生什么。

在现代的JavaScript框架中,网络上的路由工作方式有点不同;你可以说它变得更容易实现。

在Vue中,路由是通过Vue Router包处理的。如果你的网络应用程序或网站是用Vue构建的,并且包含用户需要切换的多个页面,你肯定需要Vue Router。

目标

在本教程中,我们将通过构建一个小型演示应用程序,来看看如何在Vue应用程序中使用Vue Router实现路由。

该演示是一个显示啤酒厂相关信息的应用程序:它们的位置、类型和网站。我们将从Open Brewery DB API中获取数据。

请记住,我们将在我们的应用程序中使用最新版本的Vue RouterVue

前提条件

本教程假定读者在他们的机器上安装了以下内容。

  • Node.js 10.6.0或以上版本
  • Yarn / npm
  • Vue CLI
  • VS代码

强烈建议具有JavaScript和Vue的工作知识。

你可以用以下命令安装Vue CLI。

yarn global add @vue/cli
# OR
npm install -g @vue/cli

起步

我们将使用Vue CLI工具来引导一个新的Vue项目。这个工具让我们在开始使用我们的应用程序时不必担心配置问题,因为我们可以手动选择所需的包。

让我们来创建我们的项目!

打开终端,输入以下命令来创建一个新的Vue项目。

vue create breweries-app

会有一个提示,让我们选择一个预设。选择**Default (Vue 3) ([Vue 3] babel, eslint)**迅速建立。

Screenshot of Vue CLI presets

通过Vue CLI,我们可以安装Vue Router包,通过选择手动选择功能选项,然后选择Vue Router作为我们项目配置的一部分,让它自动设置。但这并不有趣,不是吗?

在本教程中,我们将学习如何自己手动设置和配置Vue Router。

现在,用以下命令改变目录到项目文件夹。

cd breweries-app

然后,像这样启动该项目。

yarn serve
#OR
npm run serve

你可以访问运行在https://localhost:8080 上的应用程序。

Screenshot of blank Vue app

创建我们的视图

在我们进行项目的实质内容之前,我们必须先处理好我们的视图。视图是当用户访问我们的网站或应用程序时将显示给他们的页面。

首先,让我们在你喜欢的代码编辑器中打开这个项目。

接下来,在终端上改变目录,移到src 目录。

cd src

然后,运行以下命令,创建一个名为views 的文件夹。

mkdir views

在我们刚刚创建的views 目录内创建以下文件:AllBreweries.vue,BreweryDetail.vue, 和NotFound.vue

打开新创建的文件,在每个文件中粘贴以下特定的代码。

首先,我们的AllBreweries.vue 页面,这是用户访问我们的网站时将提供的第一个页面。

<!-- Vue.Js -->
<!-- AllBreweries.vue -->
<template>
  <div class="container">
    <div class="breweries-grid">
      <div
        class="brewery"
        v-for="brewery in allBreweriesList"
        :key="brewery.id"
      >
        <div class="brewery-info">
          <h3>{{ brewery.name }}</h3>
          <p>{{ brewery.country }}</p>
          <a :href="brewery.website_url">
            {{ brewery.website_url || `Not available`}}
          </a>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import { onMounted, ref } from "vue";
export default {
  setup() {
    const apiUrl = "https://api.openbrewerydb.org/breweries/";
    // We declare our list and make it reactive
    let allBreweriesList = ref([]);
    onMounted(() => {
      // We call our function here when the component is first instantiated
      fetchAllBreweries();
    });
    const fetchAllBreweries = () => {
      // Function to retrieve  a list of all breweries from the API
      fetch(apiUrl)
        .then((response) => response.json())
        .then((data) => {
          // here we set the data gotten from the API to equal our array
          allBreweriesList.value = data;
        });
    };
    return {
      allBreweriesList,
      fetchAllBreweries,
    };
  },
};
</script>
<style>
html {
  padding: 0;
  margin: 0;
}
.container {
  min-height: 100vh;
  height: 100%;
  margin: 0 auto;
  padding: 0 20px;
  margin: 0 auto;
}
.brewery {
  height: 200px;
  min-width: 280px;
  margin: 0 auto;
  border-radius: 10px;
  box-shadow: 1.5px 1.5px 11px hsla(208.4, 23.5%, 31.8%, 0.39),
    -1.5px -1.5px 11px rgba(0, 0, 0, 0);
  background-color: rgba(50, 155, 29, 0.568);
  cursor: pointer;
}
.breweries-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  grid-gap: 3rem;
  grid-auto-rows: minmax(100px, auto);
  padding: 50px 0;
  margin: 0 auto;
}
.brewery-info {
  padding-left: 20px;
}
</style>

AllBreweries.vue ,我们已经成功地从API中获取了数据,然后对这些数据进行迭代,将其显示在模板中。

接下来,让我们做我们的BreweryDetail.vue 页面,当点击时,它包含关于特定啤酒厂的额外信息。

<!-- Vue.Js -->
<!-- BreweryDetail.vue -->
<template>
  <div class="brewery-detail">
     <ul>
        <li><strong>Name: </strong></li>
        <li><strong>Type: </strong></li>
        <li><strong>Country: </strong></li>
        <li><strong>State: </strong></li>
        <li><strong>Street: </strong>
        </li>
        <li>
          <strong>Website: </strong> 
            <a href=""></a>
        </li>
      </ul>
    </div>
</template>

现在,在我们的BreweryDetail.vue 页面中,除了一些基本的HTML之外,并没有太多的事情发生。这是因为我们将随着我们的进展一点一点地完成这个组件。

最后,让我们为我们的NotFound.vue 组件做一些标记。

<!-- Vue.Js -->
<!-- NotFound.vue -->
<template>
  <div>
    <h1>
  Sorry, this page doesn't exist or has been moved to another location
    </h1>
  </div>
</template>

我们的NotFound.vue ,当用户访问一个不存在的路由时,我们将向他们提供这个页面。我们将在后面的章节中学习更多关于这方面的知识。

路由器的设置和配置

现在,我们到了安装和配置Vue Router的有趣部分了!

首先,用这个命令把目录改回我们项目的根目录。

cd ..

要在我们项目的根目录下安装Vue Router,请运行以下程序。

yarn add vue-router@4
#OR
npm install vue-router@4

接下来,改变目录回到src 文件夹中。

cd src

src 目录中创建一个文件夹,称为router

mkdir router

再次改变目录,移入新创建的router 文件夹。

cd router

最后,在这个目录下创建一个名为index.js 的文件。

touch index.js

这个文件将作为我们的路由器配置文件。

在新创建的index.js 文件中,粘贴以下代码。

// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import AllBreweries from "@/views/AllBreweries.vue"
const routes = [
    {
        path: "/",
        name: "AllBreweries",
        component: AllBreweries,
    },
]

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

export default router

让我们深入了解我们这里的代码是什么意思。

首先,我们需要从Vue Router导入函数createRoutercreateWebHistory 。这些函数分别创建了一个用户可以返回的历史,并为Vue构建了一个路由器对象。

注意我们是如何在一个数组中创建我们的routes ,其中每个路由是一个具有以下属性的对象。

  • Path, 可以找到这个路由的URL路径
  • Name, 当我们链接到这个路由时,可以使用一个可选的名字
  • Component, 当这个路由被调用时要加载哪个组件

然后我们创建router 对象来调用createRouter 函数,然后传入history 的键值和routes 数组作为参数,然后导出。

接下来,打开src 文件夹中的main.js 文件,在import App from './App.vue' 之后添加import router from "./router" ,在createApp(App).mount('#app') 之间添加.use(router)

上面的代码导入Vue Router,并将其实例化到全局Vue实例上。

接下来,打开src 目录中的App.vue 文件,删除其所有内容,只留下以下内容。

<!-- Vue.Js -->
<!--App.vue -->
<template>
<!-- This serves all our pages using vue router -->
 <router-view />
</template>
<script>
export default {
  name: 'App',
}
</script>

我们在第四行添加的router-view 组件是一个功能组件,它为给定路径渲染匹配的组件。

懒惰的加载路线

让我们倒退一下,学习一个有趣的路由技巧,叫做懒惰加载。

因为我们的应用程序往往会随着它们的增长而变得复杂,我们的包的大小会增加,然后减缓加载时间。值得庆幸的是,Vue Router有懒惰加载功能,它使我们能够推迟加载特定的路由,直到它们被用户访问。

让我们试着在我们的路由器配置文件中实现这一点。在router/index.js ,在name: "AllBreweries", 之后添加以下代码。

         component: () => import(
    /* webpackChunkName: "AllBreweries" */ '../views/AllBrewries.vue')

这样做之后,文件应该是这样的。

// router/index.js
import { createRouter, createWebHistory } from 'vue-router';

const routes = [
    {
        path: "/",
        name: "AllBreweries",
         component: () => import(
    /* webpackChunkName: "AllBreweries" */ '../views/AllBrewries.vue')
    }
]
const router = createRouter({
    history: createWebHistory(process.env.BASE_URL),
    routes
})
export default router

注意,我们已经删除了之前在顶部的导入语句,用动态导入语句替换了路由的component 属性。当路由被访问时,这个语句会获取所需的文件。这就是全部内容;我们已经实现了懒惰加载功能。

动态路由

对于我们正在构建的应用程序,当用户点击一个特定的啤酒厂时,它会把他们带到一个包含有关该啤酒厂更详细的信息的页面。现在,我们不能为API中列出的每个啤酒厂手动创建一个路由。我们如何解决这个问题呢?

幸运的是,Vue Router有一个叫做动态路由的功能,它使我们能够通过我们的路由加载动态数据。让我们来看看我们如何利用这个优势。

首先,我们需要修改我们的router/index.js 文件,为BreweryDetail.vue 页面创建路由。

//router/index.js
...
const routes = [
    {
        path: "/",
        name: "AllBreweries",
        component: () => import(
    /* webpackChunkName: "AllBreweries" */ '../views/AllBrewries.vue')
    },
    {
        path: "/brewery/:id",
        name: "BreweryDetail",
        component: () => import(
  /* webpackChunkName: "BreweryDetail" */ '../views/BreweryDetail.vue')
    }
...

路由的路径有一个动态的:id 段,称为 "param"。param用于使用通过:id 传递的任何值来操作路由的组件的状态,并且可以在我们的模板中使用$route.params ,从而使路由变得动态。

接下来,让我们为我们的BreweryDetail.vue 页面的路由背后的逻辑工作。

<!-- Vue.Js -->
<!-- BreweryDetail.vue -->
...
<script>
import { onMounted, ref } from "vue";
import { useRoute } from "vue-router";
export default {
  setup() {
      // create object that brewery information from API will be stored in
    let breweryDetails = ref({});
    const apiUrl = "https://api.openbrewerydb.org/breweries/";
    // here we instantiate the useRoute method in our component
    const route = useRoute();
    onMounted(() => {
        // invoke the function when our component is mounted on DOM
      fetchAllBreweryDetail();
    });
    // function to fetch all brewery information
    const fetchAllBreweryDetail = () => {
     // append the route params to the url to get information on the specific brewery clicked by user
      fetch(apiUrl + route.params.id)
        .then((response) => response.json())
        .then((data) => {
          //set data gotten from API call to our breweryDetails Object
          breweryDetails.value = data;
        });
    };
    return {
      fetchAllBreweryDetail,
      breweryDetails,
    };
  },
};
</script>
...

而我们可以更新模板部分,如下所示。

<!-- Vue.Js -->
<!-- BreweryDetail.vue -->
<template>
  <div class="brewery-detail">
      <!-- Button users can click to back to the previous route  -->
    <button class="back-btn" @click="$router.back()">Go back</button>
      <ul>
          <!-- Up here in the template, we access the data gotten from the API -->
        <li><strong>Name: </strong>
          {{ breweryDetails.name }}
        </li>
        <li><strong>Type: </strong>
          {{ breweryDetails.type || `Not available` }}
        </li>
        <li><strong>Country: </strong>
          {{ breweryDetails.country }}
        </li>
        <li><strong>State: </strong>
          {{ breweryDetails.state }}
        </li>
        <li><strong>Street: </strong>
          {{ breweryDetails.street || `Not available` }}
        </li>
        <li>
          <strong>Website: </strong> 
            <a :href="breweryDetails.website_url">
            {{ breweryDetails.website_url || `Not Available` }}
            </a>
        </li>
      </ul>
    </div>
</template>

在这里,我们的代码从breweryDetails 对象中获取数据,并在我们的模板上更新它。

如果你注意到了,我们已经添加了一个漂亮的小按钮,我们的用户可以点击它回到之前访问过的路线。它的作用就像在浏览器上按下返回按钮一样。

路线之间的导航

为了使用户有可能导航到一个啤酒厂的特定页面,我们将修改AllBreweries.vue 的模板部分的代码。

Vue Router有自己的router-link 组件,而不是我们常规的HTMLanchor 标签,我们可以将to 道具传递给它,它接受一个带有一堆键或值对的对象。

有两种方法可以使用router-link 组件在路由之间进行导航:通过路径属性和命名路由。

路径属性

在使用路径属性方法时,我们只需要传入我们希望用户在点击链接时访问的URL路径。然后,追加从API检索到的ID,并将其设置为参数值。

URL路径总是与路由器配置文件中定义的一样。你的代码应该如下(注意第12和14行)。

<!-- Vue.Js -->
<!-- AllBreweries.vue -->
<template>
  <div class="container">
    <div class="breweries-grid">
      <div
        class="brewery"
        v-for="brewery in allBreweriesList"
        :key="brewery.id"
      >
        <div class="brewery-info">
          <router-link :to="'/brewery/' + brewery.id">
          <h3>{{ brewery.name }}</h3>
          </router-link>
          <p>{{ brewery.country }}</p>
          <a :href="brewery.website_url">
            {{ brewery.website_url || `Not available`}}
          </a>
        </div>
      </div>
    </div>
  </div>
</template>

命名的路由

通过命名路由方法,我们传入可以访问name 属性的name 密钥,作为我们创建的每个路由的值。

我们还传入了params 键,它接受一个对象。在这个对象中,我们有id ,我们用它来设置参数的值,等于从API检索的ID。

这就是你的代码应该是这样的(注意第12、13和15行)。

<!-- Vue.Js -->
<!-- AllBreweries.vue -->
<template>
  <div class="container">
    <div class="breweries-grid">
      <div
        class="brewery"
        v-for="brewery in allBreweriesList"
        :key="brewery.id"
      >
        <div class="brewery-info">
          <router-link 
            :to="{name: 'BreweryDetail', params: {id: brewery.id}}">
            <h3>{{ brewery.name }}</h3>
          </router-link>
          <p>{{ brewery.country }}</p>
          <a :href="brewery.website_url">
            {{ brewery.website_url || `Not available`}}
          </a>
        </div>
      </div>
    </div>
  </div>
</template>

这两种方法都可以工作,但在我们必须改变我们希望用户访问的URL路径的名称的情况下,我们必须在每个实例中手动改变它。

现在,这不是什么大问题,因为它只用在一个地方,但想想看,如果我们在一千个实例中使用这个URL路径,我们将不得不承受所有的压力。

然而,如果我们采用命名路由的方法,我们只需要在一个地方改变URL路径:我们的路由器文件配置。听起来很容易吧?

现在我们已经介绍了命名路由方法的好处,我们将在本教程中继续介绍。

处理404错误

假设一个用户访问了一个我们先前在路由器配置文件中没有创建的路由。我们的应用程序将被加载,但没有一个组件。我们需要一种方法来告诉我们的路由器在这种情况发生时该怎么做。

为了实现这个功能,我们需要修改我们的路由器配置文件,在我们的index.js 文件中加入以下代码。

    {
        path: "/:catchAll(.*)",
        component: () => import(
        /* webpackChunkName: "NotFound" */ '../views/NotFound.vue')
    },
...

Vue Router使用一个自定义的正则表达式来实现这一点。:catchAll 是动态段,(.*) 是Vue Router用来评估被访问的路由是否已经在我们的路由器配置文件中定义。如果它不存在,我们的NotFound.vue 组件就会被呈现在屏幕上。

路由转换

一个有趣的方法是通过 路由转换为我们的用户体验添加一个漂亮的触摸。

为了在我们的应用程序中添加这个功能,我们只需要在Vue Router的自定义transition 元素标签周围用v-slot 属性包裹<router-view/> 元素标签。

接下来,在transition 标签之间,我们添加Vue的component 元素,并将其is 属性与我们从Vue插槽中获得的组件的值绑定。

我们还向它传递了一个key 属性,其值为$route.path (当前路径),以确保过渡工作的正确性,并且当路由改变时,它仍然会销毁并重新创建所有的页面。

transition 元素中,我们给name 属性赋值为fade ,这决定了将应用于该元素的 CSS 类的名称。transition 元素有一个mode 属性,指示Vue Router如何处理页面之间的过渡。

接下来,在我们的样式部分,我们可以定义CSS属性,这些属性将在路由改变时应用于页面。

下面是我们如何将其添加到我们的应用程序中。

<!-- Vue.Js -->
<!-- App.vue -->
<template>
<!-- This serves all our pages using vue router -->
<router-view v-slot="{ Component }">
  <transition name="fade" mode="out-in">
    <component :is="Component" :key="$route.path"/> 
  </transition>
</router-view>
</template>

<style>
.fade-enter-active, .fade-leave-active {
  transition: opacity .4s;
}
.fade-enter, .fade-leave-to {
  opacity: 0;
}
</style>

总结

在我们的Vue应用程序中使用Vue Router是相当容易和直接的。在这篇文章中,我们已经学会了如何添加页面路由功能,也利用了Vue Router的一些功能,这些功能可以让我们的用户在浏览我们的应用程序时获得美妙的体验。

关于Vue Router的更多信息,我建议阅读他们的文档

你可以在这里找到本教程的源代码

Vue Router的终极指南》一文首次出现在LogRocket博客上。