文章目录
- 路由
- 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.0
复制this.$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>
复制