当前位置: 首页 > article >正文

Vue学习记录21

Vue Router

Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。功能包括:

  • 嵌套路由映射
  • 动态路由选择
  • 模块化、基于组件的路由选择
  • 路由参数、查询、通配符
  • 展示由Vue.js 的过渡系统提供的过渡效果
  • 细致的导航控制
  • 自动激活 CSS 类的链接
  • HTML5 history 模式或 hash 模式
  • 可定制的滚动行为
  • URL的正确编码

安装

 包管理器

对于一个现有的使用 JavaScript 包管理器的项目,你可以从 npm registry  中安装 Vue Router:

npm install vue-router@4

如果你打算启动一个新项目,你可能会发现使用 create-vue  这个脚手架工具更容易,他能创建一个基于Vite的项目,并包含加入 Vue Router 的选项:

npm create vue@latest

你需要回答一些关于你想创建的项目类型的问题。如果您选择安装 Vue Router,示例应用还将演示Vue Router 的一些核心特性。

使用包管理器的项目通常会使用ES模块来访问 Vue Router,例如 import { createRouter }  from  ‘vue-router'。

直接下载/CDN

https://unpkg.com/vue-router@4

Unpkg.com 提供了基于 npm 的 CDN 链接。上述链接将始终指向 npm 上的最新版本。 也可以通过像 https://unpkg.com/vue-router@4.0.15/dist/vue-router.global.js 这样的 URL 来使用特定的版本或 Tag。

这将把 Vue Router 暴露在一个全局的 VueRouter 对象上,例如 VueRouter.createRouter(...)。

使用

Vue Router 是 Vue 官方的客户端路由解决方案。

客户端路由的作用是在单页应用(SPA)将浏览器的URL和用户看到的内容绑定起来。当用户在应用中浏览不同页面时,URL 会随之更新,但页面不需要从服务器重新加载。

Vue Router 基于Vue 的组件系统构建,你可以通过配置路由来告诉 Vue Router 为每个 URL 路径显示哪些组件。

示例

为了引入一些核心概念,将使用如下的示例:

先来看看根组件,APP.vue。

<template>
  <h1>Hello App!</h1>
  <p>
    <strong>Current route path:</strong> {{ $route.fullPath }}
  </p>
  <nav>
    <RouterLink to="/">Go to Home</RouterLink>
    <RouterLink to="/about">Go to About</RouterLink>
  </nav>
  <main>
    <RouterView />
  </main>
</template>

在这个 template 中使用了两个由 Vue Router 提供的组件:RouterLinkRouterView

不同于常规的 <a> 标签,我们使用组件 RouterLink 来创建链接。这使得 Vue Router 能够在不重新加载页面的情况下改变URL,处理URL的生成、编码和其他功能。

RouterView 组件可以使 Vue Router 知道你想要在哪里渲染当前 URL 路径对应的路由组件。 它不一定要在 App.vue 中,你可以把它放在任何地方,但它需要在某处被导入,否则 Vue Router就不会渲染任何东西。

上述示例中还使用了 {{ $route.fullPath }}。你可以在组件模板中使用 $route 来访问当前路由对象。

创建路由器实例

路由器实例是通过调用 createRouter() 函数创建的:

import { createMemoryHistory, createRouter } from 'vue-router'

import HomeView from './HomeView.vue'
import AboutView from './AboutView.vue'

const routes = [
  { path: '/', component: HomeView },
  { path: '/about', component: AboutView },
]

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

这里的 routes 选项定义了一组路由,把 URL 路径映射到组件。其中,由 component 参数指定的组件就是先前在App.vue 中被 <RouterView> 渲染的组件。 这些路由组件通常被称为视图,但本质上它们只是普通的 Vue 组件。

这里的 history 选项控制了路由和 URL 路径是如何双向映射的。这里使用了 createMemoryHistory(),它会完全忽略浏览器的 URL er使用自己内部的 URL。通常,你应该使用 createWebHistory() createWebHashHistory()

注册路由器插件

一旦创建了路由器实例,我们就需要为其注册为插件,这一步骤可以通过调用 use() 来完成。

createApp(App)
  .use(router)
  .mount('#app')

或等价地:

const app = createApp(App)
app.use(router)
app.mount('#app')

和大多数的 Vue 插件一样,use() 需要在 mount() 之前调用。

如果你好奇这个插件做了什么,它的职责包括:

  • 全局注册 RouterView 和 RouterLink 组件。
  • 添加全局 $router 和 $route 属性。
  • 启用 useRouter() 和 useRoute() 组合式函数。
  • 触发路由器解析初始路由。

访问路由器和当前路由

你很可能想要在应用的其他地方访问路由器。

如果你是从 ES 模块导出路由实例的,你可以将路由器实例直接导入你需要它的地方。 在一些情况下这是最好的方法,但如果我们在组件内部,那么我们还有其他选择。

在组件模板中,路由器实例将被暴露为 $router。这与同样被暴露的 $route 一样,但注意前者最后有一个额外的 r。

如果我们使用选项式API,我们可以在JavaScript中访问这两个属性:this.$router 和 this.$route。

export default {
  methods: {
    goToAbout() {
      this.$router.push('/about')
    },
  },
}

这里调用了 push(),这是用于编程式导航的方法。

对于组合式API,我们不能通过 this 访问组件实例,所以 Vue Router 给我们提供了一些组合式函数。

<script setup>
import { computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'

const router = useRouter()
const route = useRoute()

const search = computed({
  get() {
    return route.query.search ?? ''
  },
  set(search) {
    router.replace({ query: { search } })
  }
})
</script>

这里的关键是要知道可以通过 useRouter() 和 useRoute() 来访问路由器实例和当前路由。

单文件组件

Vue Router 经常在配合打包工具 (如 Vite)和单文件组件(即.vue文件)的应用中使用。但是 Vue Router 本身并不要求你使用构建工具或单文件组件。

router和route

router常常作为路由器实例提及。即由 createRouter() 返回的对象。 在应用中,访问该对象的方式取决于上下文。例如,在组合式API中,它可以通过调用 useRouter() 来访问。 在选项式API中,它可以通过 this.$router 来访问。

类似地,当前路由会以 route 被提及。基于不同 API 风格的组件,它可以通过 useRoute() 或 this.$route 来访问。

RouterView 和 RouterLink

组件 RouterView 和 RouterLink 都是全局注册的,因此它们不需要在组件模板中导入。但你也可以通过局部导入它们,例如 import { RouterLink } from 'vue-router'。

在模板中,组件的名字可以式 PascalCase 风格或 kebab-case 风格的。 Vue的模板编译器支持两种格式,因此 <RouterView> 和 <router-view> 通常是等效的。此时应该遵循你自己项目中使用的约定。

如果使用 DOM 内模板,那么需要注意:组件名字必须使用 kebab-case 风格且不支持自闭合标签。因此你不能直接写<RouterView>,而需要使用<router-view></router-view>。

带参数的动态路由匹配

很多时候,我们需要将给定匹配模式的路由映射到同一个组件。例如,我们可能有一个 User 组件,它应该对所有用户进行渲染,但用户ID不同。 在 Vue Router 中,我们可以在路径中使用一个动态字段来实现,我们称之为 路径参数:

import User from './User.vue'

// 这些都会传递给 `createRouter`
const routes = [
  // 动态字段以冒号开始
  { path: '/users/:id', component: User },
]

现在像 /user/johnny 和 /users/jolyne 这样的 URL 都会映射到同一个路由。

路径参数用冒号 : 表示。当一个路由被匹配时,它的 params 的值将在每个组件中以 route.params 的形式暴露出来。因此,我们可以通过更新 User 的模板来呈现当前的用户 ID:

<template>
  <div>
    <!-- 当前路由可以通过 $route 在模板中访问 -->
    User {{ $route.params.id }}
  </div>
</template>

你可以在同一个路由中设置有多个 路径参数,它们会映射到 $route.params 上的相应字段。例如:

匹配模式匹配路径route.params
/users/:username/users/eduardo{ username: 'eduardo' }
/users/:username/posts/:postId/users/eduardo/posts/123{ username: 'eduardo', postId: '123' }

除了 route.params 之外,route 对象还公开了其他有用的信息,如 route.query (如果 URL中存在参数)、route.hash等。

 

响应路由参数的变化

使用带有参数的路由时需要注意的是,当用户从 /users/johny 导航到 /users/jolyne 时,相同的组件实例将被重复使用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会被调用。

要对同一个组件中参数的变化做出响应的话,你可以简单地 watch $route 对象上地任意属性,在这个场景中,就是 $route.params:

<script setup>
import { watch } from 'vue'
import { useRoute } from 'vue-router'

const route = useRoute()

watch(() => route.params.id, (newId, oldId) => {
  // 对路由变化做出响应...
})
</script>

或者,使用 beforeRouteUpdate 导航守卫,它还允许你取消导航:

<script setup>
import { onBeforeRouteUpdate } from 'vue-router'
// ...

onBeforeRouteUpdate(async (to, from) => {
  // 对路由变化做出响应...
  userData.value = await fetchUser(to.params.id)
})
</script>

捕获所有路由或 404 Not found 路由

常规参数只匹配 url 片段之间的字符,用 / 分隔。如果我们想匹配任意路径,我们可以使用自定义的路径参数正则表达式,在路径参数后面的括号中加入正则表达式:

const routes = [
  // 将匹配所有内容并将其放在 `route.params.pathMatch` 下
  { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
  // 将匹配以 `/user-` 开头的所有内容,并将其放在 `route.params.afterUser` 下
  { path: '/user-:afterUser(.*)', component: UserGeneric },
]

在这个特定的场景中,我们在括号之间使用了自定义正则表达式,并将 pathMatch 参数标记为可选可重复。这样做是为了让我们在需要的时候,可以通过将 path 拆分成一个数组,直接导航到路由:

router.push({
  name: 'NotFound',
  // 保留当前路径并删除第一个字符,以避免目标 URL 以 `//` 开头。
  params: { pathMatch: this.$route.path.substring(1).split('/') },
  // 保留现有的查询和 hash 值,如果有的话
  query: route.query,
  hash: route.hash,
})

路由的匹配语法

大多数应用都会使用 /about 这样的静态路由和 /user/:userId 这样的动态路由,但是Vue Router 可以提供更多的方式!

Tip

为了简单起见,所有的路由都省略了 component 属性,只关注 path 值。

在参数中自定义正则 

当定义像 :userId 这样的参数时,我们内部使用以下的正则 ([^/]+) (至少一个不是斜杠 / 的字符)来从 URL 中提取参数。 这很好用,除非你需要根据参数的内容来区分两个路由。 想想一下,两个路由 /:orderId 和 /:productName, 两者会匹配完全相同的 URL,所以我们需要一种方法来区分它们。最简单的方法就是在路径中添加一个静态部分来区分它们:

const routes = [
  // 匹配 /o/3549
  { path: '/o/:orderId' },
  // 匹配 /p/books
  { path: '/p/:productName' },
]

但在某些情况下,我们并不想添加静态的 /o/p 部分。 由于,orderId 总是一个数字,而 productName 可以是任何东西,所以我们可以在括号中为参数指定一个自定义的正则:

const routes = [
  // /:orderId -> 仅匹配数字
  { path: '/:orderId(\\d+)' },
  // /:productName -> 匹配其他任何内容
  { path: '/:productName' },
]

现在,转到 /25 将匹配 /:orderId ,其他情况将会匹配 /:productname。routes 数组的顺序并不重要!

Tip

确保转义反斜杠(\),就像我们对 \d (变成 \\d)所做的那样,在 JavaScript 中实际传递字符串中的反斜杠字符。

可重复的参数 

如果你需要匹配具有多个部分的路由,如 /first/second/third,你应该 *(0个或多个)和 + (1个或多个)将参数标记为可重复:

const routes = [
  // /:chapters ->  匹配 /one, /one/two, /one/two/three, 等
  { path: '/:chapters+' },
  // /:chapters -> 匹配 /, /one, /one/two, /one/two/three, 等
  { path: '/:chapters*' },
]

这将为你提供一个参数数组,而不是一个字符串,并且在使用命名路由时也需要你传递一个数组:

// 给定 { path: '/:chapters*', name: 'chapters' },
router.resolve({ name: 'chapters', params: { chapters: [] } }).href
// 产生 /
router.resolve({ name: 'chapters', params: { chapters: ['a', 'b'] } }).href
// 产生 /a/b

// 给定 { path: '/:chapters+', name: 'chapters' },
router.resolve({ name: 'chapters', params: { chapters: [] } }).href
// 抛出错误,因为 `chapters` 为空

这些也可以通过在右括号后添加它们与自定义正则结合使用:

const routes = [
  // 仅匹配数字
  // 匹配 /1, /1/2, 等
  { path: '/:chapters(\\d+)+' },
  // 匹配 /, /1, /1/2, 等
  { path: '/:chapters(\\d+)*' },
]

Sensitive 与 strict 路由配置

默认情况下,所有路由是不区分大小写的,并且能匹配带有或不带有尾部斜线的路由。例如,路由 /users 将匹配 /users、/users/、甚至 /Users/。这种行为可以通过 strict 和 sensitive 选项来修改,它们既可以应用在整个全局路由上,又可以应用于当前路由上:

const router = createRouter({
  history: createWebHistory(),
  routes: [
    // 将匹配 /users/posva 而非:
    // - /users/posva/ 当 strict: true
    // - /Users/posva 当 sensitive: true
    { path: '/users/:id', sensitive: true },
    // 将匹配 /users, /Users, 以及 /users/42 而非 /users/ 或 /users/42/
    { path: '/users/:id?' },
  ],
  strict: true, // applies to all routes
})

可选参数

你也可以通过使用 ? 修饰符(0个或1个)将一个参数标记为可选:

const routes = [
  // 匹配 /users 和 /users/posva
  { path: '/users/:userId?' },
  // 匹配 /users 和 /users/42
  { path: '/users/:userId(\\d+)?' },
]

请注意,*在技术上也标志着一个参数是可选的,但 ?参数不能重复。

调试

如果你需要探究你的路由是如何转化为正则的,以了解为什么一个路由没有被匹配,或者,报告一个bug,你可以使用路径排名工具。它支持通过URL分享你的路由。


http://www.kler.cn/a/554451.html

相关文章:

  • 求矩阵对角线元素的最大值
  • js解析后端传来的如图示的list集合,怎么获取每个map的key和value
  • spring中aop
  • LLC谐振变换器原理
  • 2026考研趋势深度解析:政策变化+高效工具指南
  • Spring Boot 实现 DeepSeek API 调用
  • mysql查看binlog日志
  • 云计算如何解决延迟问题?
  • OceanBase 初探学习历程之——安装部署
  • ctfshow web入门 web11-web24
  • 基于 Stanford CoreNLP 的中文自然语言处理
  • 推理优化技术
  • Python爬虫requests(详细)
  • rman 备份恢复1
  • 探索飞鹤奶粉奥秘,领会科技魅力
  • 11.编写前端内容|vscode链接Linux|html|css|js(C++)
  • 如何在 macOS 上配置 MySQL 环境变量
  • Linux sftp 使用详解
  • Qt的QToolButton的使用
  • 【Canvas】基础