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

Vue Router ——路由基础详解(一)

官网:入门 | Vue Router

一、简介

Vue Router 是 Vue.js 的官方路由。

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

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

二、安装

npm install vue-router@4

三、配置(通常全局配置)

1、App.vue

<template><h1>Hello App!</h1><p><strong>Current route path:</strong> {{ $route.fullPath }}</p><main><RouterView /></main>
</template>

App.vue 是 Vue 应用的根组件,也是整个项目的入口文件

所有页面和组件都基于它进行切换和渲染。通过 main.js 将其挂载到 DOM 中(通常是 index.html 中的 #app 元素),从而启动整个应用

2、创建路由示例-router.js

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

import { createMemoryHistory, createRouter } from 'vue-router'const routes = [{ path: '/', redirect: '/one'},{ path: '/one', component: () => import('/src/components/One.vue') },
]const router = createRouter({history: createMemoryHistory(),routes,
})
export default router

这里的 routes 选项定义了一组路由把 URL 路径映射到组件

component 参数指定的组件就是先前在 App.vue 中被 <RouterView> 渲染的组件。这些路由组件通常被称为视图,但本质上它们只是普通的 Vue 组件。

这里的 history 选项控制了路由和 URL 路径是如何双向映射的。

3、注册路由器插件-main.js

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

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from "./router/router.js";createApp(App).use(router).mount('#app')

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

它的职责包括:

  1. 全局注册 RouterViewRouterLink 组件。
  2. 添加全局 $router$route 属性。
  3. 启用 useRouter()useRoute() 组合式函数。
  4. 触发路由器解析初始路由。

四、访问路由器和当前路由

//组合式
<script setup>
import { useRoute, useRouter } from 'vue-router'const router = useRouter()
const route = useRoute()
const toHellow = () => {//注意:没有this对象了console.log('当前页面路由', route.path)//跳转router.push('/HelloWorld')}
</script>

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

五、<RouterView> 和< RouterLink>

组件 RouterViewRouterLink 都是全局注册的,因此它们不需要在组件模板中导入。

但你也可以通过局部导入它们,例如 import { RouterLink } from 'vue-router'

<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

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

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

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

六、带参数的动态路由匹配

(适用于不同用户进入同一页面展示不同内容)

很多时候,我们需要将给定匹配模式的路由映射到同一个组件。

例如,我们可能有一个 User 组件,它应该对所有用户进行渲染,但用户 ID 不同。

在 Vue Router 中,我们可以在路径中使用一个动态字段来实现,我们称之为 路径参数

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

/users/01/users/02这样的 URL 都会映射到同一个路由。

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

const route = useRoute(); // 使用useRoute()获取$route对象
const userId = ref(route.params.id); // 使用ref将$route.params.id包装成响应式数据
// 监听$route对象的变化,更新doorId的值watchEffect(() => {userId.value = route.params.id;});

1、响应路由变化

使用带有参数的路由时需要注意的是,当用户从 /users/johnny 导航到 /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 导航守卫,它还允许你取消导航:

2、捕获所有路由或404 Not Found路由

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

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

解释:path: '/:pathMatch(.*)*'

● :pathMatch:动态路由参数名,可通过 $route.params.pathMatch 获取未匹配的路径片段 

● (.*)*:正则表达式,匹配任意字符(包括 /),支持多级路径(如 /a/b/c)。

● * 结尾:允许将未匹配的路径分割为数组形式(如 /not/found 转换为 ['not', 'found']) 。

七、路由的匹配语法

1、在参数中自定义正则—(正则)

当定义像 :userId 这样的参数时,我们内部使用以下的正则 ([^/]+) 来从 URL 中提取参数。想象一下,两个路由 /:orderId/:productName,两者会匹配完全相同的 URL,所以我们需要一种方法来区分它们。

①最简单的方法就是在路径中添加一个静态部分来区分它们:

// 匹配 /o/3549 { path: '/o/:orderId' },

// 匹配 /p/books { path: '/p/:productName' },

②在括号中为参数指定一个自定义的正则:

// /:orderId -> 仅匹配数字 { path: '/:orderId(\\d+)' },

// /:productName -> 匹配其他任何内容 { path: '/:productName' },

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

2、可重复的参数—*、+

如果你需要匹配具有多个部分的路由,如 /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*' },]

3、Sensitive 与 strict 路由配置

默认情况下,所有路由是不区分大小写的,并且能匹配带有或不带有尾部斜线的路由。例如,路由 /users 将匹配 /users/users/、甚至 /Users/

这种行为可以通过 strictsensitive 选项来修改,它们既可以应用在整个全局路由上,又可以应用于当前路由上:

默认:不区分大小写,能匹配带有或不带有尾部斜线的路由。

strict:不匹配带有尾部斜线的路由

sensitive:区分大小写

注意:不能写成Sensitive和Strict,大写开头是不起作用的!!!

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
})

4、可选参数—?

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

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

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

八、嵌套路由

一个被渲染的组件也可以包含自己嵌套的 <router-view>

例如,如果我们在 User 组件的模板内添加一个 <router-view>

<!-- User.vue -->
<template><div class="user"><h2>User {{ $route.params.id }}</h2><router-view /></div>
</template>

要将组件渲染到这个嵌套的 router-view 中,我们需要在路由中配置 children

const routes = [{path: '/user/:id',component: User,children: [{// 当 /user/:id/profile 匹配成功// UserProfile 将被渲染到 User 的 <router-view> 内部path: 'profile',component: UserProfile,},{// 当 /user/:id/posts 匹配成功// UserPosts 将被渲染到 User 的 <router-view> 内部path: 'posts',component: UserPosts,},],},
]

注意,以 / 开头的嵌套路径将被视为根路径。这允许你利用组件嵌套,而不必使用嵌套的 URL。

children 配置只是另一个路由数组,就像 routes 本身一样。因此,你可以根据自己的需要,不断地嵌套视图。

忽略父组件

我们还可以仅利用路由的父子关系,但不嵌套路由组件。

为了实现这一点, 我们在父路由中省略了 componentcomponents 选项

const routes = [{path: '/admin',children: [{ path: '', component: AdminOverview },{ path: 'users', component: AdminUserList },{ path: 'users/:id', component: AdminUserDetails },], },
]

由于父级没有指定路由组件,顶级 <router-view> 将跳过父级并仅使用子路由组件。

九、命名路由

当创建一个路由时,我们可以选择给路由一个 name

const routes = [{path: '/user/:username',name: 'profile', component: User}
]

然后我们可以使用 name 而不是 path 来传递 to 属性给 <router-link>

<router-link :to="{ name: 'profile', params: { username: 'erina' } }"> 注意不要漏掉:

<router-link to="/user/erina">

使用 name 有很多优点:

  • 没有硬编码的 URL。
  • params 的自动编码/解码。
  • 防止你在 URL 中出现打字错误。
  • 绕过路径排序,例如展示一个匹配相同路径但排序较低的路由。

所有路由的命名都必须是唯一的。如果为多条路由添加相同的命名,路由器只会保留最后那一条。

十、重定向

重定向也是通过 routes 配置来完成,下面例子是从 /home 重定向到 /

const routes = [{ path: '/home', redirect: '/' }]

重定向的目标也可以是一个命名的路由:

const routes = [{ path: '/home', redirect: { name: 'homepage' } }]

甚至是一个方法,动态返回重定向目标:

const routes = [{// /search/screens -> /search?q=screenspath: '/search/:searchText',redirect: to => {// 方法接收目标路由作为参数// return 重定向的字符串路径/路径对象return { path: '/search', query: { q: to.params.searchText } }},},{path: '/search',// ...},
]

在写 redirect 的时候,可以省略 component 配置,因为它从来没有被直接访问过,所以没有组件要渲染。唯一的例外是嵌套路由:如果一个路由记录有 childrenredirect 属性,它也应该有 component 属性。

十一、别名

/ 别名为 /home,意味着当用户访问 /home 时,URL 仍然是 /home,但会被匹配为用户正在访问 /

const routes = [{ path: '/', component: Homepage, alias: '/home' }]

通过别名,你可以自由地将 UI 结构映射到一个任意的 URL,而不受配置的嵌套结构的限制。使别名以 / 开头,以使嵌套路径中的路径成为绝对路径。你甚至可以将两者结合起来,用一个数组提供多个别名:

十二、路由组件传参

将 props 传递给路由组件

我们可以通过声明 prop 来在 User.vue 中删除对 $route 的直接依赖:

<template>
传props值:{{id}}
</template><script setup>
const props=defineProps({id:String
})</script><style scoped></style>

1、布尔模式

props 设置为 true 时,route.params 将被设置为组件的 props。

    //props{path:'/props/:id',component: () => import('@/view/six.vue'),props: true},

2、对象模式

props 是一个对象时,它将原样设置为组件 props。当 props 是静态的时候很有用。

    {path:'/props',component: () => import('@/view/six.vue'),props: { id: 1, name: 'lymm'}},
<template>传props值id:{{id}}传props值name:{{name}}
</template><script setup>const props=defineProps({id:String,name:String,
})//或
// const props=defineProps(
// ['id', 'name'] // 必须声明接收的 props
// )
console.log(props)
</script><style scoped></style>

3、函数模式

你可以创建一个返回 props 的函数。这允许你将参数转换为其他类型,将静态值与基于路由的值相结合等等。

const routes = [{///props?q=vuepath:'/props',component: () => import('@/view/six.vue'),props: route => ({ query: route.query.id })},
]

URL props?q=vue 将传递 {query: 'vue'} 作为 props 传给 six 组件。

请尽可能保持 props 函数为无状态的,因为它只会在路由发生变化时起作用。如果你需要状态来定义 props,请使用包装组件,这样 vue 才可以对状态变化做出反应。

<template>传props值id:{{query}}
</template><script setup>const props=defineProps({query:String,})console.log(props)
</script>
<style scoped></style>

十三、编程式导航

1、导航到不同位置

编程式导航:借助 router 的实例方法,通过编写代码来实现。

注:

你可以使用 $router 属性访问路由,例如 this.$router.push(...)

如果使用组合式 API,你可以通过调用 useRouter() 来访问路由器。

import {useRouter} from 'vue-router';
const router = useRouter();

router.push -- 这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,会回到之前的 URL。

<router-link> --内部会调用push这个方法,所以点击 <router-link :to="..."> 相当于调用 router.push(...)

push方法的参数可以是一个字符串路径,或者一个描述地址的对象。例如:

// 字符串路径
router.push('/users/eduardo')// 带有路径的对象
router.push({ path: '/users/eduardo' })// 命名的路由,并加上参数,让路由建立 url
router.push({ name: 'user', params: { username: 'eduardo' } })// 带查询参数,结果是 /register?plan=private
router.push({ path: '/register', query: { plan: 'private' } })// 带 hash,结果是 /about#team
router.push({ path: '/about', hash: '#team' })

注意:如果提供了 pathparams 会被忽略(params只能和name组合)

const username = 'eduardo'
// 我们可以手动建立 url,但我们必须自己处理编码
router.push(`/user/${username}`) // -> /user/eduardo
// 同样
router.push({ path: `/user/${username}` }) // -> /user/eduardo
// 如果可能的话,使用 `name` 和 `params` 从自动 URL 编码中获益
router.push({ name: 'user', params: { username } }) // -> /user/eduardo
// `params` 不能与 `path` 一起使用
router.push({ path: '/user', params: { username } }) // -> /user

当指定 params 时,可提供 stringnumber 参数。任何其他类型(如对象、布尔等)都将被自动字符串化

2、替换当前位置—replace

它的作用类似于 router.push,唯一不同的是,它在导航时不会向 history 添加新记录,正如它的名字所暗示的那样——它取代了当前的条目。

声明式

编程式

<router-link :to="..." replace>

router.replace(...)

也可以直接在传递给 router.pushto 参数中增加一个属性 replace: true

router.push({ path: '/home', replace: true })
// 相当于
router.replace({ path: '/home' })
router.replace('/home' )

3、横跨历史—go

该方法采用一个整数作为参数,表示在历史堆栈中前进或后退多少步,

例子

// 向前移动一条记录,与 router.forward() 相同
router.go(1)// 返回一条记录,与 router.back() 相同
router.go(-1)// 前进 3 条记录
router.go(3)// 如果没有那么多记录,静默失败
router.go(-100)
router.go(100)

十四、不同的历史模式

1、Hash 模式——createWebHashHistory()

hash 模式是用 createWebHashHistory() 创建的:

import { createRouter, createWebHashHistory } from 'vue-router'const router = createRouter({history: createWebHashHistory(),routes: [//...],
})

它在内部传递的实际 URL 之前使用了一个井号(#)。由于这部分 URL 从未被发送到服务器,所以它不需要在服务器层面上进行任何特殊处理。不过,它在 SEO 中确实有不好的影响。如果你担心这个问题,可以使用 HTML5 模式。

使用HTML5:http://localhost:5174/one

使用Hash:http://localhost:5174/#/one

2、Memory 模式——createMemoryHistory(一般不用)

Memory 模式不会假定自己处于浏览器环境,因此不会与 URL 交互也不会自动触发初始导航。它是用 createMemoryHistory() 创建的,并且需要你在调用 app.use(router) 之后手动 push 到初始导航

import { createRouter, createMemoryHistory } from 'vue-router'const router = createRouter({history: createMemoryHistory(),routes: [//...],})

虽然不推荐,你仍可以在浏览器应用程序中使用此模式,但请注意它不会有历史记录,这意味着你无法后退前进

注:

  • Memory 模式不会与浏览器 URL 交互,也不会自动触发初始导航。
  • 它的路由状态完全由 JavaScript 内存管理,因此浏览器地址栏的路径变化不会影响路由跳转 。
  • Memory 模式通常用于 非浏览器环境(如 Node.js、SSR 或移动应用)。
  • 若必须使用 Memory 模式,需通过 <RouterLink> 或 router.push() 主动触发导航

3、HTML5 模式——createWebHistory()

createWebHistory() 创建 HTML5 模式,推荐使用这个模式:

import { createRouter, createWebHistory } from 'vue-router'const router = createRouter({history: createWebHistory(),routes: [//...],
})

当使用这种历史模式时,URL 会看起来很 "正常",例如 https://example.com/user/id。漂亮!

不过,问题来了。由于我们的应用是一个单页的客户端应用,如果没有适当的服务器配置,用户在浏览器中直接访问 https://example.com/user/id,就会得到一个 404 错误。

nginx需要配置:location / { try_files $uri $uri/ /index.html;}

http://www.xdnf.cn/news/2468.html

相关文章:

  • Qt中的全局函数讲解集合(全)
  • 软件模块依赖关系管理与优化
  • 基于 STM32 与 RFID 技术的医院医疗器械数字化精细管理系统设计
  • Redis内存管理三部曲:淘汰、过期与惰性删除的协同哲学
  • 01_Long比较值 类型相同值不同
  • 幂等性处理解决方案实战示例
  • MySQL 表的约束(一)
  • 第一个 servlet请求
  • 【看穿操控的套路】
  • 【记录maven依赖规则-dependencyManagement,dependencies】
  • Matlab 报错:尝试将 SCRIPT vl_sift 作为函数执行:
  • Java学习手册:Spring 框架核心概念
  • 如何通过OKR管理项目目标
  • 四 YARN配置和HBase配置
  • C++ 区分关键字和标识符
  • 职场提效小工具!
  • 【halcon】tuple_sort_index 和 select_obj 配合使用 详解
  • 小白学习python第四天
  • SpringBoot常用注解解析汇总
  • 基础学习:(9)vit -- vision transformer 和其变体调研
  • 03 基于 STM32 的温度控制系统
  • vscode eslint与vue-official冲突,导致点击的时候鼠标不会变成手型,一直在加载,但是不转到相应方法。
  • 二进制补码:给补码求原码
  • vue3中ref在js中为什么需要.value才能获取/修改值?
  • 智能文档抽取技术发展前沿与应用
  • Linux Socket编程:从API到实战
  • 模型删除层后重建
  • Go语言入门:目录与链接
  • EKS环境下服务重启50X错误
  • 企业架构之旅(1):TOGAF 基础入门