vue插件--路由vue-router(vue.js官方的路由插件,适合用于构建单页面应用。)
可单独安装或脚手架安装
单页应用(全称 single-page application,简称 SPA)
React、 Vue、Angular等前端框架均采用了SPA原则。
它是一种网站应用的模型,可以动态重写当前的页面来与用户交互,而不需要重新加载整个页面。通俗讲就是整个应用只有一个html页面,页面跳转切换就相当于是组件切换,不同哈希值对应不同组件,比多页应用更流畅。
vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。传统的页面应用,是用一些超链接来实现页面切换和跳转的。在vue-router单页面应用中,则是路径之间的切换,也就是组件的切换。
npm install vue-router@4 --save 安装router插件
一、路由基础
- 在根目录下创建路由模块文件夹router->index.js(用于配置路由信息)
1>.导入路由插件 2>.创建路由对象配置路由规则 3>.暴露路由模块
index.js
javascript
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import Login from '../views/Login.vue'
const routes = [
{
path: '/',
redirect: '/login',
},
{
path: '/login',
component: Login,
},
{
path: '/home',//路径路由
name: 'home',//命名路由
component: Home,
redirect: '/user', // 重定向到user路由
children: [
{
path: '/user',
component: () => import('@/views/User.vue'),//懒加载
},
{
path: '/A',
component: () => import('@/views/A.vue'),
},
{
path: '/B',
component: () => import('@/views/B.vue'),
},
],
},
// 404路由不存在匹配,放在路由最下面
{
path: '/:pathMatch(.*)*',
component: () => import('@/views/NotPage.vue'),
},
]
const router = createRouter({
// history: createWebHashHistory(),//路由模式,hash模式
history: createWebHistory(),//路由模式,history模式
routes,//路由规则
})
export default router
- main.js入口文件中集成路由插件
javascript
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')
- App.vue根组件,类似插槽,路由输出组件的位置
xml
<template>
<div>
<!-- 路由跳转,类似a标签 -->
<!-- <router-link to="/home">跳转到首页</router-link> -->
<!-- 路由输出位置 -->
<router-view></router-view>
</div>
</template>
<style scoped></style>
- vite.config.js
javascript
import vue from '@vitejs/plugin-vue' // vite构建工具解析 .vue文件的插件
import {defineConfig} from 'vite'//defineConfig方法,编写代码会有提示
import { fileURLToPath, URL } from "node:url";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],// 集成插件
resolve: {
alias: {
"@": fileURLToPath(new URL("./src", import.meta.url)),// @符号代替./src
},
},
});
二、路由模式、路由导航、路由传参、命名视图、嵌套路由(配置路由规则children);路由元信息(属性)、滚动行为
1.两种路由模式:
- createWebHashHistory,Hash模式(默认)
使用URL的hash来模拟一个完整的URL,当URL改变时,页面不会重新加载。示例:http://ip:port/ #/login ->http://ip:port/ #/main。 #不美观
- createWebHistory,History模式
利用history对象的 history.pushState API来完成 URL 跳转而无须重新加载页面。url地址像正常请求一样,比hash模式美观
2.两个全局组件:
- router-view路由输出组件位置
命名视图:在Vue.js中用于定义具有多个视图的路由配置的一种方式。如:一个路由对应多个组件,如一个页面包含侧边栏和内容区域
sql
<router-view></router-view>
<router-view name="slider/content"></router-view>
index.js
javascript
import { createRouter, createWebHashHistory } from 'vue-router'
import Slider from '@/views/Slider.vue'
import Content from '@/views/Content.vue'
// 路由规则
const routes = [
{
name: 'login',
path: '/login', // http://ip:port/#login
component: Login,
},
{
path: '/main',
components: {//命名视图
slider: Slider,
content: Content,
},
},
]
// 创建路由对象
const router = createRouter({
history: createWebHashHistory(), // 路由模式 使用hash模式 history模式
routes, // router $router.push() route $route.query $route.parmas routes
})
export default router
- router-link路由跳转
ini
<router-link to="/home">跳转到首页</router-link>
<router-link :to="{ name: 'home' }">name跳转到首页</router-link>
<router-link :to="{path:'/home'}">path跳转到首页</router-link>
3.全局属性:
- $route路由信息对象,获取路由参数
- $router路由对象
4.路由元信息(属性)、滚动行为
路由元信息meta:{isok:true}可以自定义属性
滚动行为scrollBehavior 方法
index.js
javascript
import { createRouter, createWebHashHistory } from 'vue-router'
import Login from '@/views/Login.vue'
// 路由规则
const routes = [
{
path: '/',
redirect: '/login',
},
{
name: 'login',
path: '/login', // http://ip:port/#login
component: Login,
},
{
name: 'home',
path: '/home', //http://ip:port/#home
component: () => import('@/views/Home.vue'), // 懒加载, 组件的懒加载, 图片懒加载
meta:{isok:true}//访问这个路由需要isok为真才能访问
},
]
// 创建路由对象
const router = createRouter({
history: createWebHashHistory(), // 路由模式 使用hash模式 history模式
routes,
scrollBehavior(to, from,savedPosition){//滚动行为
//始终滚动到顶部
retuna(top:0}
},
})
//获取meta元信息中的属性
router.beforeEach((to, from,next) => {//前置导航守卫
if (to.matched.some(record-> record.meta.isok)){//matched路由数组
// 此路由需要身份验证,检查是否已登录,如果没有,则重定向到登录页面。
if (!auth.loggedIn()) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
}else {
next()
}
}else{
next()//确保一定要调用next()
})
export default router
5.路由传参
- 路由query传参(router-link query传参,$route.query获取路由参数)
- 动态路由传参(路由配置时,路径后加 /:参数名,router-link路径后加/要传的参数 传参,$route.params接收参数)
- 编程导航( <math xmlns="http://www.w3.org/1998/Math/MathML"> r o u t e r . p u s h ( ) 压栈,返回会回到前一页 ; router.push()压栈,返回会回到前一页; </math>router.push()压栈,返回会回到前一页;router.replace()压栈前先把原来的弹出去,直接把原来的替换掉)
示例:
1.>main.js入口文件/vite.config.js配置文件同上
2>.index.js
javascript
import { createRouter, createWebHashHistory } from 'vue-router'
import Login from '@/views/Login.vue'
import Slider from '@/views/Slider.vue'
import Content from '@/views/Content.vue'
// 路由规则
const routes = [
{
path: '/',
redirect: '/login',
},
{
name: 'login',
path: '/login', // http://ip:port/#login
component: Login,
},
{
name: 'home',
path: '/home', //http://ip:port/#home
component: () => import('@/views/Home.vue'), // 懒加载, 组件的懒加载, 图片懒加载
},
{
path: '/detail/:id',//动态路由传参
component: () => import('@/views/Detail.vue'),
},
{
path: '/nba',//编程导航
component: () => import('@/views/Nba.vue'),
},
{
path: '/main',
components: {//命名视图
slider: Slider,
content: Content,
},
},
{
path: '/music',
component: () => import('@/views/Music.vue'),
// 配置嵌套路由 一个路由组件中嵌套输出另外一个路由
children: [
{
path: '/index',
component: () => import('@/views/Index.vue'),
},
{
path: '/song',
component: () => import('@/views/Song.vue'),
},
],
},
]
// 创建路由对象
const router = createRouter({
history: createWebHashHistory(), // 路由模式 使用hash模式 history模式
routes, // router $router.push() route $route.query $route.parmas routes
})
export default router
3>.App.vue根组件传参
xml
<template>
<div>
<!--<router-link :to="{path:'/home'}">path跳转到首页</router-link> -->
<router-link :to="{ name: 'home' }">name跳转到首页</router-link>
<!--对象形式,通过query传参到登录界面-->
<router-link :to="{ path: '/login', query: { message: 'hello' } }">登录界面</router-link>
<!--动态路由传参-->
<ul>
<li>
<router-link :to="{ path: '/detail/1' }">商品1</router-link>
</li>
<li>
<router-link :to="{ path: '/detail/2' }">商品2</router-link>
</li>
</ul>
<!--编程导航-->
<button @click="bindNavgatorNBA">NBA</button>
<!-- 路由输出位置 -->
<router-view></router-view>
<!-- 命名视图,一个路由对应多个组件(如一个页面包括侧边栏和内容区域) -->
<router-view name="slider"></router-view>
<router-view name="content"></router-view>
</div>
</template>
<script>
export default {
data() {
return { }
},
methods: {
bindNavgatorNBA() {
// 切换NBA路由组件
// $router 路由对象
// $route 路由信息对象
this.$router.push({ path: '/nba' })
},
},
}
</script>
4>.Login.vue获取参数
xml
<template>
<div>
<h2>登录界面</h2>
<p>{{ $route.query.message }}</p>
</div>
</template>
<script>
/**
* $route
* 路由信息对象
* 作用:获取路由参数
*/
export default {
mounted() {
console.log(this.$route.query.message)
},
}
</script>
5>.Detail.vue接收参数
xml
<template>
<div>
<h2>商品详情</h2>
<p>{{ $route.params.id }}</p>
</div>
</template>
6.导航守卫(在进行路由切换/跳转时,执行的一些钩子函数)
分类:
-
全局的,只要路由跳转都会执行(全局前置守卫、全局解析守卫、全局后置守卫)
-
路由独享,只在这个路由触发(路由规则表中写)
-
组件内 (组件中写)
1.全局
- 全局前置守卫
router.beforeEach((to要进入的目标,from当前导航要离开的路由)=>{return false//不放行})
统一登陆身份认证,没有登陆不许访问需登陆的界面。
根目录下封装一个单独的permission.js文件,也可直接在router→index.js文件中写
permission.js
javascript
import router from './router'
router.beforeEach((to, from) => {//to要进入的目标,from当前导航要离开的路由
// console.log('全局前置导航守卫 >>>>')
// 1. 加入白名单: 有些路由是不需要登录身份认证 path: /login , /
if (to.path === '/login') {
return true //放行
}
// 2. 登录认证
let user = localStorage.getItem('USER')
if (user) {
return true // 放行
} else {
// 如果不存在,重定向到登录界面
router.replace({ path: '/login' })
}
})
Login.vue
xml
<!-- eslint-disable vue/multi-word-component-names -->
<template>
<div class="g-container">
<div class="g-wrapper">
<h2>xx平台</h2>
<form @submit.prevent="bindLogin">
<input
type="text"
name="name"
placeholder="请输入用户"
v-model="user.name"
/><br />
<input
type="password"
name="password"
placeholder="请输入密码"
v-model="user.password"
/><br />
<p>{{ message }}</p>
<input type="submit" class="m-login-btn" value="登录" />
</form>
</div>
</div>
</template>
<script>
export default {
data() {
return {
user: {
name: '',
password: '',
},
message: '',
}
},
methods: {
bindLogin() {
if (this.user.name === 'admin' && this.user.password === '123') {
// 1. 保存登录状态
localStorage.setItem('USER', JSON.stringify(this.user))
// 2. 跳转主界面
this.$router.push({ path: '/home' })
} else {
this.message = '用户名或密码出错!'
}
},
},
}
</script>
<style lang="scss" scoped>
.g-container {
width: 100%;
height: 100vh;
background-color: #2b3c4d;
position: relative;
.g-wrapper {
width: 500px;
height: 400px;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
h2 {
text-align: center;
color: white;
margin-bottom: 20px;
}
form {
width: 100%;
background-color: white;
border-radius: 8px;
text-align: center;
padding: 20px 0;
input {
width: 80%;
margin: 20px 0;
outline: none;
height: 30px;
border: 1px solid gray;
border-radius: 5px;
text-indent: 10px;
}
.m-login-btn {
background-color: #409eff;
color: white;
height: 40px;
border: none;
border-radius: 5px;
margin-bottom: 20px;
&:hover {
cursor: pointer;
background-color: #64a5e7;
}
}
}
}
}
</style>
- 全局解析守卫
router.beforeResolve 和 router.beforeEach类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
- 全局后置守卫
router.afterEach((to,from)=>{return false})不会接受next函数也不会改变导航本身。
加载进度条
2.路由独享守卫
只在进入路由时触发,不会在 params、query 或 hash 改变时触发。如,从 /users/2 进入到 /users/3,只有从一个不同的路由导航时,才会被触发。
router.beforeEnter((to,from)=>{return false//不放行})
javascript
const routes=[
{
path: '/users/:id',
component: UserDetails,
beforeEnter:(to, from,next) => {
//reject the navigation
return false
}
}
]
3.组件内守卫(组件里面定义)
通常用来预防用户在还未保存修改前突然离开。该导航可以通过返回 false 来取消。
javascript
beforeRouteEnter(to,from,next){//进入的时候执行
//在渲染该组件的对应路由被验证前调用
//不能获取组件实例this,因为当守卫执行时,组件实例还没被创建
//可以通过传一个回调给 next 来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数
next(vm => { // 通过 `vm` 访问组件实例 })
}
beforeRouteUpdate(to,from){//更新的时候执行
//在当前路由改变,但是该组件被复用时调用
//举例:一个带有动态参数的路径'/users/:id,在'/users/1和'/users/2之间跳转时,由于会渲染同样的"UserDetails组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用
//因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例'this
}
beforeRouteLeave(to,from){//离开的时候执行
//在导航离开渲染该组件的对应路由时调用
//与beforeRouteupdate一样,它可以访问组件实例"this"
const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
if (!answer) return false
}
4.完整的导航解析流程
-
导航被触发。
-
在失活的组件里调用 beforeRouteLeave 守卫。
-
调用全局的 beforeEach 守卫。
-
在重用的组件里调用 beforeRouteUpdate 守卫(2.2+)。
-
在路由配置里调用 beforeEnter。
-
解析异步路由组件。
-
在被激活的组件里调用 beforeRouteEnter。
-
调用全局的 beforeResolve 守卫(2.5+)。
-
导航被确认。
-
调用全局的 afterEach 钩子。
-
触发 DOM 更新。
-
调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。