文章目录
- 路由
- vue-router路由
- 1. vue-router 功能
- 2. vue-router 组成
- 3. vue-router 常用的函数
- vue-router使用
- 1.安装
- 2. vue3.0配置
- 3. vue2.0配置
- 4. 基本用法
- 5. 编程式导航
- 6. 路由懒加载
- 导航守卫
- 路由的两种工作模式
路由
- 路由(routing)
路由是指从源页面到目的页面时,决定端到端路径的决策过程。 - 前端路由
前端路由即由前端来维护一个路由规则。实现模式有两种:
- 利用URL的Hash模式:就是常说的锚点,JavaScript通过 hashChange事件来监听URL的改变。(IE7及以下版本需要使用轮询)。在地址中以’#'分隔页面。
- 利用HTML5的History模式:它使用URL看起来像普通网站一样,在地址中以’/'分隔页面。但是页面并没有跳转。这种模式需要服务器端支持,服务端在接收到所有的请求后,都指向同一个HTML文件,不然会出现页面错误。
vue-router路由
路由就是根据一个请求路径选中一个组件进行渲染的决策过程。VueRouter路由是Vue官方推出的路由管理器。
Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。
1. vue-router 功能
- 嵌套路由映射
- 动态路由选择
- 模块化、基于组件的路由配置
- 路由参数、查询、通配符
- 展示由 Vue.js 的过渡系统提供的过渡效果
- 细致的导航控制
- 自动激活 CSS 类的链接
- HTML5 history 模式或 hash 模式
- 可定制的滚动行为
- URL 的正确编码
- route:表示单个路由。
- routes:表示多个路由的集合。是一个数组,在这个数组中包含了多个route。
- router:译为路由器,是route、routes的管理者。
路由的查找过程是: router --> routes --> route(当用户在页面上点击按钮的时候,router就会去routes中去查找route,就是说路由器会去路由集合中找对应的路由)
2. vue-router 组成
- VueRouter类:路由器类,维护的一种路由表,根据路由请求在路由视图中动态渲染选中的组件。
- router-link:路由的链接组件,类似于
<a>
标签。声明用以提交路由请求的用户接口。 - router-view:路由视图组件,路由出口,负责动态渲染路由选中的组件。
3. vue-router 常用的函数
- createRouter函数:创建路由器
- createWebHashHistory函数:创建Hash模式的路由
- createWebHistory函数:创建History模式路由
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
//...
],
})
vue-router使用
1.安装
npm install vue-router
// 安装最新版本
npm i vue-router -s
2. vue3.0配置
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')
3. vue2.0配置
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')
4. 基本用法
- 声明路由链接和占位符
在 src/App.vue 组件中,使用 vue-router 提供的和声明路由链接和占位符<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>
- 路由重定向
指的是:用户在访问地址A的时候,强制用户跳转到地址 C ,从而展示特定的组件页面
通过路由规则的redirect属性,指定一个新的路由地址,可以很方便地设置路由的重定向
补充:{ path: ‘*’, component: NotFound } 可在最后加上,表示上面的路由都没有匹配到时会展示此组件routes :[ { path: "/", redirect:'/home', }, { path: "/home", name:'home', component: Home, }, { path: "/about", name:'about', component: () => import("../components/About.vue"), }, ]
- 嵌套路由
通过路由实现组件的的嵌套展示,叫做嵌套路由,
- 比如Home中包括Product、Message,它们可以在Home内部来回切换,这个时候我们就需要使用嵌套路由,在Home中也使用 router-view 来占位之后需要渲染的组件
- 使用嵌套路由, 首先我们需要配置嵌套路由:在我们要配置嵌套路由的映射关系中, 添加children属性
{ 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>
- 动态路由匹配
路由的参数根据需求是动态可变的,就涉及到路由的传值,分为两种
- query查询参数形式传值(键值对)
- params路径参数形式传值
- 通过实例的$route对象获取路由传来的值
- query传值
to属性的值由字符串改为对象,路由由path字段负责,传递的值由query字段负责
<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();
- params传值
to属性的值由字符串改为对象,路由由name字段负责,传递的值由params字段负责
//路由配置 { 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();
- query传值
5. 编程式导航
路由的页面跳转是通过<router-link>
标签完成的,称为声明式导航,
但是有时候希望通过代码来完成页面的跳转,比如点击的是一个按钮, 点击一个span等等其他元素也实现页面跳转,这样就叫编程式导航
vue-router 提供了许多编程式导航的 API,其中最常用的导航 API 分别是
- push():跳转到指定 路由,并增加一条历史记录
- replace():跳转到指定的 路由,并替换掉当前的历史记录,不会增加历史记录
- go(数值 n):实现导航历史前进、后退
- back():相当于 router.go(-1)
- forward():相当于 router.go(1)
vue2.0
vue3.0this.$router.back() this.$router.forward() this.$router.push("地址") this.$router.replace("地址") this.$router.go(2)
// 导入函数useRouter import { useRouter } from "vue-router" // 通过函数useRouter拿到路由对象 const router = useRouter() function btnClick() { router.back() router.forward() router.push("地址") router.replace("地址") router.go(2) }
6. 路由懒加载
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就会更加高效。
// 替换成
const UserDetails = () => import('./views/UserDetails.vue')
const router = createRouter({
// ...
routes: [
{ path: '/users/:id', component: UserDetails }
// 或在路由定义里直接使用它
{ path: '/users/:id', component: () => import('./views/UserDetails.vue') },
],
})
注意
不要在路由中使用异步组件。异步组件仍然可以在路由组件中使用,但路由组件本身就是动态导入的。
把组件按组分块
- 使用 webpack
有时候想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用命名 chunk,一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4):
webpack 会将任何一个异步模块与相同的块名称组合到相同的异步块中。const UserDetails = () => import(/* webpackChunkName: "group-user" */ './UserDetails.vue') const UserDashboard = () => import(/* webpackChunkName: "group-user" */ './UserDashboard.vue') const UserProfileEdit = () => import(/* webpackChunkName: "group-user" */ './UserProfileEdit.vue')
- 使用 vite
在Vite中,你可以在rollupOptions下定义分块:// 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() } })
-
全局后置钩子
- 每次发生路由的跳转之后,都会触发全局后置钩子,用的比较少
- 但是可能会有这样如下这样的需求,就是当点击某路由链接时,改变 document.title
- 全局后置钩子是没有第三个参数中的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
- hash模式
- 默认开启的就是 hash 的工作模式
- #就是代表 hash ,后面就是 hash 值
- #后面的值都是不发给服务器的
- history模式
- 是没有#号的
- 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>