路由
- 一、前端路由的理解
- 二、路由实现本质
- 三、几种跳转的对比
-
- [1.location.href = '/url'](#1.location.href = '/url')
- [2. history.pushState('/url')](#2. history.pushState('/url'))
- [3.router.push('/url') -- Vue Router](#3.router.push('/url') -- Vue Router)
- 4.对比
- 四、创建路由
-
- [1.安装 Vue Router](#1.安装 Vue Router)
- 2.创建路由配置文件
- 3.在main.js中注册路由
- 4.在App.vue中显示路由组件
- [五、`route\`和\`router` 的区别](#五、
$route
和$router
的区别) - 六、动态路由匹配
- 七、嵌套路由,命名路由,路由重定向
- 八、路由跳转的两种方式
- 九、路由传递参数写法
- 十、路由元信息
- 十一、路由导航守卫
- 十二、路由懒加载
一、前端路由的理解
1.SPA单页面应用
(1)在前端技术早期,一个url对应一个页面。
eg:早期博客网站(
传统的多页应用MPA
)
- 点击首页的一篇文章
- 浏览器向服务器发送请求
- 服务器收到请求后,生成一个完整的HTML页面
- 服务器把这个完整的HTML文件返回给浏览器
- 浏览器完全刷新页面,丢掉原来的首页内容,加载新的HTML页面
(2)ajax出现,允许在不刷新页面的情况下发请求,随之出现SPA
- 在SPA初期,在内容切换前后,页面的URL是一样的。
问题一:SPA不会记住你的操作
- 在博客网站点击帖子内容,但是URL并没有变,点击刷新,又回到首页,刚才的帖子页
问题二:SEO 不够友好
- 早期爬虫抓取网页内容,但是并不执行js,所以当它们访问SPA时,只能看到那个几乎是空白的HTML文件,而无法获取任何通过JavaScript动态加载的内容。
(3)因此路由出现了
路由的作用就是
将浏览器的URL和用户看到的内容绑定起来
。当用户浏览不同页面时,URL随之更新,但不需要从服务器重新加载。
2.路由
路由要解决的两个问题
- (1)问题一
浏览器默认行为
:看到URL改变就会向服务器发请求。所以必须避开这种行为。- (2)问题二:
感知URL变化,实现"前进/后退"
:让用户看到URL变化,但不刷新页面,同时记录历史
二、路由实现本质
1.浏览器的两种路由模式
模式 | 描述 | 特性 |
---|---|---|
hash模式 |
依赖 URL 的 哈希部分 (# 后面的内容。当哈希值发生变化,触发hashchange事件。前端通过监听该事件,根据哈希值动态渲染页面内容。 |
兼容所有现代浏览器 ;哈希部分不会被发送到服务器 ;url不美观 ;无法利用浏览器的前进、后退功能进行复杂的路由操作 |
history模式 |
基于 HTML5 History API | 功能强大 ,需要服务器支持 |
(1)hash模式
hash模式的主要原理就是
onhashchange()
事件:
- 页面hash值发生改变,无需向后端发起请求,window就可以监听事件的改变
js
window.onhashchange = function(event){
console.log(event.oldURL, event.newURL);
let hash = location.hash.slice(1);
//location.hash返回当前 URL 中以 # 开头的 hash 部分
//slice去掉#
}
(2)history模式
history 路由模式的实现,基于 HTML5 提供的
History 全局对象
,它的方法有:
history模式需要后台配置支持
。如果后台没有正确配置,访问时会返回404- 如果想要切换到history模式,就要进行以下配置(后端也要进行配置)
js
const router = new VueRouter({
mode: 'history',
routes: [...]
})
2.避开浏览器默认行为
- 不使用
<a href>
进行跳转,而是用 JS 控制- 用 history.pushState() 替代直接跳转 → 不触发请求
- 监听页面的 popstate 事件 → 捕捉"前进/后退"
三、几种跳转的对比
1.location.href = '/url'
(1)
刷新页面
,破坏SPA体验(2)等同于点击
<a href="/about">
,是传统的多页跳转方式
2. history.pushState('/url')
(1)
无刷新页面
,(2)浏览器提供的
原生 API
,属于 HTML5 History API 的一部分。(3)只负责改变URL并更新浏览器的历史记录栈,
不会自动处理视图更新
3.router.push('/url') -- Vue Router
(1)
无刷新页面
,(2)不仅可以
改变 URL
,还能够基于 URL自动匹配并渲染相应的组件
(3)提供了诸如导航守卫、懒加载、参数解析等额外功能
4.对比

四、创建路由
1.安装 Vue Router
js
//vue2推荐@3.x
//vue3推荐@4.x
npm install vue-router@4
2.创建路由配置文件
- 可以把路由规则放在routers.js文件,到时候在这引入就行
路由插件
:
- 1.全局注册RouterView 和 RouterLink 组件
- 2.添加全局 router 和 route 属性
- 3.启用 useRouter() 和 useRoute() 组合式函数
- 4.触发路由器解析初始路由。
js
// src/router/index.js
import Vue from 'vue';
import VueRouter from 'vue-router';
// 1. 安装 VueRouter 插件(关键!)
Vue.use(VueRouter);
// 2. 引入需要路由控制的组件
import Home from '@/views/Home.vue';
import About from '@/views/About.vue';
import User from '@/views/User.vue';
// 3. 定义路由规则
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About
},
{
path: '/user/:id', // 动态路由参数
name: 'User',
component: User,
props: true // 将参数作为 props 传入组件
},
{
path: '/login',
redirect: '/auth' // 重定向
},
{
path: '/auth',
component: () => import('@/views/Auth.vue') // 懒加载(推荐)
},
{
path: '*',
component: () => import('@/views/NotFound.vue') // 404 页面
}
];
// 4. 创建路由实例
const router = new VueRouter({
mode: 'history', // 可选:'hash' 或 'history'
base: process.env.BASE_URL,
routes // 相当于 routes: routes
});
// 5. 导出路由实例
export default router;
3.在main.js中注册路由
js
// src/main.js
import Vue from 'vue';
import App from './App.vue';
// ✅ 引入路由
import router from '@/router';
// ✅ 引入并使用 Vuex(可选)
import store from '@/store';
new Vue({
render: (h) => h(App),
// ✅ 注册路由:让所有组件都能访问 $route 和 $router
router,
// ✅ 注册 store(如果使用 Vuex)
store
}).$mount('#app');
4.在App.vue中显示路由组件
使用
<router-view>
显示组件内容
五、$route
和$router
的区别
注册完路由后,不管是
路由组件
还是非路由组件
身上都有这两个属性
项目 | 本质 | 属性 |
---|---|---|
$route |
当前路由的信息对象 | path,params,hash,query,fullPath,matched,name 等路由信息参数 |
$router |
路由实例对象 | 路由的跳转 方法,钩子函数 等 |
六、动态路由匹配
- 指
在定义路由时,允许某些部分是动态变化的
- 大多情况下,我们需要将给定匹配模式的路由映射到同一个组件,例如user组件的渲染会有用户id的不同
js
import User from './User.vue'
// 这些都会传递给 `createRouter`
const routes = [
// 动态字段以冒号开始,称之为路径参数
{ path: '/users/:id', component: User },
]
- 当一个路由被匹配时,它的 params 的值将在每个组件中以
route.params
的形式暴露出来- 通过更新 User 的模板来呈现当前的用户 ID
html
<template>
<div>
<!-- 当前路由可以通过 $route 在模板中访问 -->
User {{ $route.params.id }}
</div>
</template>
七、嵌套路由,命名路由,路由重定向
js
const routes = [
// 重定向:根路径跳转到首页
{ path: '/', redirect: { name: 'home' } },
// 命名路由 + 嵌套路由
{
path: '/admin',
name: 'admin',
component: AdminLayout,
children: [
{
path: 'user',
name: 'admin-user',
component: UserList,
children: [
{
path: 'add',
name: 'admin-user-add',
component: UserAdd
}
]
}
]
}
]
js
<!-- 使用命名路由跳转 -->
<router-link :to="{ name: 'admin-user-add' }">新增用户</router-link>
八、路由跳转的两种方式
- 声明式导航 router-link
- 编程式导航 push | replace
- 声明式导航能做的,编程式导航都能做;但是编程式导航除了能进行路由跳转,还可以做其他业务逻辑。
九、路由传递参数写法
1.params参数
- 路径参数(是URL的一部分)
- 在配置路由时需要占位
- 给params后面加上
?
代表参数可传可不传
- 携带
params参数
时,使用to的对象写法
只能用name配置!
,不能用path配置
2.query参数
- 不属于路径的一部分,
- /home?k=v&k=v,配置路由的时候不需要占位
3.params和query传参的三种写法
使用router.push()
这里我感觉少用
<router-link :to=''>
传参吧,可以用它进行单纯的路由跳转?反正传参emmmm反正我有点绕。等用到了再说吧。
js
goSearch() {
//搜索按钮的回调函数,需要向search路由跳转并传参给search
//第一种传参方式:字符串
// this.$router.push('/search/' + this.keyword + '?k=' + this.keyword.toUpperCase());
//第二种传参方式:模板字符串
// this.$router.push(`/search/${this.keyword}?k=${this.keyword.toUpperCase()}`);
//第三种传参方式:对象写法
this.$router.push({
// path: '/search', 和params一起时不能用path只能name
name: 'sousuo',
params: { keyword: this.keyword },
query: { k: this.keyword.toUpperCase() },
})
}
4.props传递
props可以写成布尔值,对象模式,函数模式
js
{
name: 'search', // 是当前路由的标识名称
path: '/search/:keyword?',
component: Search,
// 将params参数和query参数映射成属性传入路由组件
//写法一:布尔值,只能传递prams参数
// props:true
//写法二:对象写法-->死数据(额外给路由组件传递一些props)
//props:{a:1,b:2}
//写法三:函数 可以传params和query参数
props: ($route)=> {
return {
keyword3: $route.params.keyword,
keyword4: $route.query.keyword2 }
},
js
//子组件接收
prop:[ 'keyword3','keyword4']
十、路由元信息
- 你可能希望
将任意信息附加到路由上
,如过渡名称
、谁可以访问路由等
。- 这些事情可以通过接收属性对象的
meta属性
来实现,并且它可以在路由地址和导航守卫上都被访问到- 暂时没用到多少,后面用多了再说
一个例子:
js
//例如在home首页,我希望显示footer组件,但是在其他页面不想显示footer组件,我就可以写meta元信息
{
path: '/',
component: () => import('@/pages/Home'),
meta: {
isHideFooter: true
}
},
js
<!-- 利用路由元信息解决当前问题好处:一行代码就可以解决 -->
<Footer v-show="$route.meta.isHideFooter" />
十一、路由导航守卫
全局前置/钩子
:beforeEach、beforeResolve、afterEach路由独享的守卫
:beforeEnter组件内的守卫
:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave- 暂时只用过全局前置守卫来进行页面跳转守卫
1.全局前置守卫
js
// GOOD
router.beforeEach((to, from, next) => {
//to:获取到要跳转到的路由信息
//from:获取到从哪个路由跳转过来来的信息
// next();直接调用,代表直接放行
next()
})
十二、路由懒加载
非懒加载
js
import List from '@/components/list.vue'
const router = new VueRouter({
routes: [
{ path: '/list', component: List }
]
})
1.使用箭头函数+import动态加载
方案一(常用):
js
const List = () => import('@/components/list.vue')
const router = new VueRouter({
routes: [
{ path: '/list', component: List }
]
})
2.使用箭头函数+require动态加载
js
const router = new Router({
routes: [
{
path: '/list',
component: resolve => require(['@/components/list'], resolve)
}
]
})
3.使用webpack的require.ensure技术
js
// r就是resolve
const List = r => require.ensure([], () => r(require('@/components/list')), 'list');
// 路由也是正常的写法 这种是官方推荐的写的 按模块划分懒加载
const router = new Router({
routes: [
{
path: '/list',
component: List,
name: 'list'
}
]
}))