- 利用URL的Hash模式:就是常说的锚点,JavaScript通过 hashChange事件来监听URL的改变。(IE7及以下版本需要使用轮询)。在地址中以’#'分隔页面。
- 利用HTML5的History模式:它使用URL看起来像普通网站一样,在地址中以’/'分隔页面。但是页面并没有跳转。这种模式需要服务器端支持,服务端在接收到所有的请求后,都指向同一个HTML文件,不然会出现页面错误。
路由就是根据一个请求路径选中一个组件进行渲染的决策过程。VueRouter路由是Vue官方推出的路由管理器。
Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。
标签。声明用以提交路由请求的用户接口。import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
//...
],
})
npm install vue-router
// 安装最新版本
npm i vue-router -s
router/index.ts文件
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
// routViewere level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
// 创建 Vue 应用
const app = createApp(App)
app.use(router).mount('#app')
import Vue from 'vue'
import Router from 'vue-router'
import login from "@/views/loginB";
Vue.use(Router)
const originalPush = Router.prototype.push
Router.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err)
}
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
linkActiveClass: 'on',
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return {
x: 0,
y: 0
}
}
},
routes: [
//login
{
path: '/login',
component: login,
name: 'login',
},
{
path: '*',
redirect: '/admin',
},
],
})
main.js文件
import Vue from 'vue'
import App from './App.vue'
//6.引入导出的路由
import router from './router/index'
Vue.config.productionTip = false
new Vue({
//7.注册路由
router,
render: h => h(App),
}).$mount('#app')
<template>
<div class="app">
<h2>App</h2>
<!-- 设置要跳转的url,将来会被渲染成a标签 -->
<!-- 点击首页, url会自动拼接上/#/home, Home组件 就会替换下面的router-view -->
<router-link to="/home">首页</router-link>
<!-- 点击关于, url会自动拼接上/#/about, About组件 就会替换下面的router-view -->
<router-link to="/about">关于</router-link>
<!-- 设置组件要展示的位置 -->
<router-view></router-view>
</div>
</template>
routes :[
{
path: "/",
redirect:'/home',
},
{
path: "/home",
name:'home',
component: Home,
},
{
path: "/about",
name:'about',
component: () => import("../components/About.vue"),
},
]
补充:{ path: ‘*’, component: NotFound } 可在最后加上,表示上面的路由都没有匹配到时会展示此组件{
path: "/home",
component: () => import("../views/Home.vue"),
// children中配置home的二级路由
children: [
// 定义默认展示路由
{
path: "/home",
redirect: "/home/product"
},
{
// 配置HomeProduct组件路由, 二级路径直接写子路径即可
path: "product", // 相当于: /home/product
component: () => import("../views/HomeProduct.vue")
},
{
// 配置HomeMessage组件路由
path: "message",
component: () => import("../views/HomeMessage.vue")
}
]
}
注: 1. 子路由路径以 / 开头代表全路径配置,需要包含父路由路径,如 path:‘/home/product’
2. 子路由可省略 / 开头,自动继承父路由路径,如 path:‘child’ 上面也有代码说明也有介绍
在Home组件中定义router-view展示二级路由
<template>
<div>
<h2>Home</h2>
<!-- router-link切换导航 -->
<router-link to="/home/product">商品</router-link>
<router-link to="/home/message">信息</router-link>
<!-- 使用router-view站位 -->
<router-view></router-view>
</div>
</template>
<router-link :to="{path:'/cat',query:{id:111,name:'小狗'}}">商品</router-link>
//2.0商品组件里取值
this.$route.query
//vue3.0组件里取值
import { useRouter, useRoute } from 'vue-router';
const route = useRoute();
const paramValue = route.query;
// vue3.0实现路由跳转
const userRouter = useRouter();
//路由配置
{
path: "/sale/:id/:type",
name:'S',
component: () => import("../components/Sale.vue"),
},
//使用
<router-link :to="{name:'S',params:{id:111,type:'羽绒服'}}">商品</router-link>
//2.0商品组件里取值
this.$route.params
import { useRouter, useRoute } from 'vue-router';
const route = useRoute();
const paramValue = route.query;
// vue3.0实现路由跳转
const userRouter = useRouter();
路由的页面跳转是通过
标签完成的,称为声明式导航,
但是有时候希望通过代码来完成页面的跳转,比如点击的是一个按钮, 点击一个span等等其他元素也实现页面跳转,这样就叫编程式导航
vue-router 提供了许多编程式导航的 API,其中最常用的导航 API 分别是
this.$router.back()
this.$router.forward()
this.$router.push("地址")
this.$router.replace("地址")
this.$router.go(2)
vue3.0// 导入函数useRouter
import { useRouter } from "vue-router"
// 通过函数useRouter拿到路由对象
const router = useRouter()
function btnClick() {
router.back()
router.forward()
router.push("地址")
router.replace("地址")
router.go(2)
}
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就会更加高效。
// 替换成
const UserDetails = () => import('./views/UserDetails.vue')
const router = createRouter({
// ...
routes: [
{ path: '/users/:id', component: UserDetails }
// 或在路由定义里直接使用它
{ path: '/users/:id', component: () => import('./views/UserDetails.vue') },
],
})
注意
不要在路由中使用异步组件。异步组件仍然可以在路由组件中使用,但路由组件本身就是动态导入的。
把组件按组分块
const UserDetails = () =>
import(/* webpackChunkName: "group-user" */ './UserDetails.vue')
const UserDashboard = () =>
import(/* webpackChunkName: "group-user" */ './UserDashboard.vue')
const UserProfileEdit = () =>
import(/* webpackChunkName: "group-user" */ './UserProfileEdit.vue')
webpack 会将任何一个异步模块与相同的块名称组合到相同的异步块中。// vite.config.js
export default defineConfig({
build: {
rollupOptions: {
// https://rollupjs.org/guide/en/#outputmanualchunks
output: {
manualChunks: {
'group-user': [
'./src/UserDetails',
'./src/UserDashboard',
'./src/UserProfileEdit',
],
},
},
},
},
})
导航守卫可以控制路由的访问权限,在home页面跳转到about页面的这个过程, 我们称之为导航,在跳转这个过程的中间环节, 我们称之为导航守卫, 我们可以在这个环节对跳转进行拦截, 进行逻辑判断
全局前置守卫
每次发生路由的导航跳转前,都会触发全局前置守卫。因此,在全局前置守卫中,程序员可以对每个路由进行访问权限的控制
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
to:将要进入的目标路由对象
from:当前导航正要离开的路由
next():表示放行,允许这次路由导航
next 函数的 3 种调用方式
1. 当前用户拥有后台主页的访问权限,直接放行:next()
2. 当前用户没有后台主页的访问权限,强制其跳转到登录页面:next(‘/login’)
3. 当前用户没有后台主页的访问权限,不允许跳转到后台主页:next(false)
// 全局前置守卫
router.beforeEach((to, from, next) => {
// 要进行导航守卫的路径值 绕过pathArr中设置的路由
const pathArr = ['/home','/home/users', '/home/rights']
if (pathArr.indexOf(to.path) !== -1) {
const token = localStorage.getItem('token')
if (token) {
next()
} else {
next('/login')
}
} else {
next()
}
})
另一种写法就是给每一个路由添加meta配置项,通过meta里的真假值来判断是否需要进行判断
{
path: 'users',
component: Users,
meta: { isAuth: true },
},
// 全局前置守卫
router.beforeEach((to, from, next) => {
if (to.meta.isAuth) { // 判断是否需要进行导航守卫
const token = localStorage.getItem('token')
if (token) {
next()
} else {
next('/login')
}
} else {
next()
}
})
全局后置钩子
{
path: 'users',
component: Users,
meta: { isAuth: true, title: '用户管理' },
},
// 全局后置守卫
router.afterEach(function (to, from) {
document.title = to.meta.title || '管理系统' // 修改页面的title
})
路由独享守卫
路由独享守卫,与全局前置路由守卫没啥区别,只是作用的范围不同
独享路由守卫是没有后置路由守卫的
{
path: 'users',
component: Users,
meta: { isAuth: true, title: '用户管理' },
beforeEnter: (to, from, next)=>{
// ...
}
},
组件内路由守卫
组件中的使用
是没有前置后置可分的,因为beforeRouteLeave是离开该组件时才会被调用,并不是跳转之后调用的
使用场景:判断路由权限的时候,需要当前组件数据源作辅助判断时,可以考虑使用
<template>
<h4 class="text-center">订单管理</h4>
</template>
<script>
export default {
name: 'MyOrders',
// 通过路由规则,进入该组件时被调用,不能获取到组件实例this,因为当守卫执行前,组件实例还没被创建
beforeRouteEnter(to, from, next){
// ...
},
// 通过路由规则,路由改变时被调用,能获取到组件实例this,例如带有动态参数的路径 /foo/:id,在/foo/1和/foo/2切换时
beforeRouteUpdate(to, from, next){
// ...
},
// 通过路由规则,离开该组件时被调用,能获取到组件实例this
beforeRouteLeave(to, from, next){
// ...
}
}
</script>
路由有两种工作模式,分别是:hash和history
对于一个url来说,什么是hash值?——#及其后面的内容就是hash值
hash值不会包含在HTTP请求中,即:hash值不会带给服务器
hash模式:
- 地址中永远带有#号,不美观
- 若以后将地址通过第三方手机app分享,若app效验严格,则地址会被标记为不合法
- 兼容性较好
history模式:
- 地址干净美观
- 兼容性和hash模式相比略差
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题
在服务器端配置Vue Router的history模式,你需要确保所有路由都会指向你的单页面应用的入口文件(index.html)。以下是使用不同服务器配置history模式的方法。
Nginx
服务器配置中添加一个location块来实现相同的功能。
location / {
try_files $uri $uri/ /index.html;
}
Node.js (Express)
使用Express作为服务器,可以使用history中间件来处理路由。
nst express = require('express');
const history = require('connect-history-api-fallback');
const app = express();
app.use(history());
// 其他中间件配置...
app.use(express.static(__dirname + '/public'));
app.listen(3000);
Apache
在Apache服务器配置中,需要使用.htaccess文件来重写所有路由到index.html。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>