一. 路由入门
1. 相关理解
1.1 对SPA应用的理解
- 单页 Web 应用 SPA(single page web application)
- 整个应用只有一个完整的页面
- 点击页面中的导航链接不会刷新页面,只会做页面的局部更新
- 数据需要通过ajax请求获取。
1.1.1 单页面应用 VS 多页面应用
1.1.2 总结
-
什么是单页面应用程序?
所有功能在一个html页面上实现
-
单页面应用优缺点?
优点:按需更新性能高,开发效率高,用户体验好
缺点:学习成本,首屏加载慢,不利于SEO
-
应用场景?
1.2 路由的理解
单页面应用程序,之所以开发效率高,性能高,用户体验好,最大的原因就是:页面按需更新
要按需更新,首先就需要明确:访问路径 和 组件的对应关系!
访问路径 和 组件的对应关系如何确定呢? 路由
1.2.1 什么是路由?
一个路由(route)就是一组映射关系(key - value),多个路由需要路由器(router)进行管理。
key为路径,value可能是function或component
(前端路由:key是路径,value是组件。)
Vue中路由:路径 和 组件 的 映射 关系,根据路由就能知道不同路径的,应该匹配渲染哪个组件
1.2.2 路由分类
- 后端路由
2. 前端路由
2. VueRouter 的介绍
Vue 官方的一个路由插件,是一个第三方包,专门用来实现SPA应用
VueRouter作用:修改地址栏路径时,切换显示匹配的组件
2.1 VueRouter 基本使用
5个基础步骤:
-
下载:下载 VueRouter 模块到当前工程,命令:
npm i vue-router@3注意:2022年2月7日以后,vue-router的默认版本为4版本,vue-router4只能在vue3中使用,vue-router3才能在vue2中使用,推荐3.6.5
Vue2 VueRouter3 vuex3
Vue3 VueRouter4 vuex4
-
引入
import VueRouter from 'vue-router' -
安装注册(应用插件):
Vue.use(VueRouter) -
创建路由对象
const router = new VueRouter() -
注入,注入Vue实例,将路由对象注入到new Vue实例中,建立关联
new Vue({ render: h => h(App), router }).$mount('#app')通过注入路由器,我们可以在任何组件内通过
this.$router访问路由器,也可以通过this.$route访问当前路由router 和 route 的区别
route 路由,只是一个规则,别人应用这个规则
router 路由器,有绝对的指挥权,让你去哪就得去哪
2 个核心步骤
- 创建组件 (views目录),配置路由规则(路径组件的匹配关系)
import About from '../components/About' import Home from '../components/Home' const router = new VueRouter({ routes:[{ path:'/about', component:About }, { path:'/home', component:Home }] }) - 配置导航,配置路由出口 router-view (组件展示的位置)(路径匹配的组件显示的位置)
<div class="footer_wrap"> <a href="#/find">发现音乐</a> <a href="#/my">我的音乐</a> <a href="#/friend">朋友</a> </div> <div class="top"> <router-view></router-view> </div>
2.2 组件存放目录问题
注意:.vue文件 本质无区别。
路由相关的组件,为什么放在 views 目录呢? 组件分类
组件分类: .vue文件分2类; 页面组件 & 复用组件
注意:都是 .vue文件 (本质无区别)
分类开来 更易维护
-
页面组件 src/views文件夹
配合路由,页面展示
-
复用组件 src/components文件夹
封装复用
2.3 几个注意点
-
通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
-
每个组件都有自己的
$route属性,里面存储着自己的路由信息。 -
整个应用只有一个 router,可以通过组件的
$router属性获取到。 -
router 和 route 的区别
route 路由,只是一个规则,别人应用这个规则
router 路由器,有绝对的指挥权,让你去哪就得去哪
2.4 多级路由(嵌套路由)
-
配置路由规则,使用children配置项:
routes:[ { path:'/about', component:About, }, { path:'/home', component:Home, //通过children配置子级路由 children:[{ path:'news', //此处一定不要写:/news component:News }, { path:'message',//此处一定不要写:/message component:Message }] } ]注意,以
/开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径。 -
跳转(要写完整路径):
<router-link to="/home/news">News</router-link> -
指定展示位置
<router-view></router-view>
二. 路由进阶
1. 路由模块封装
问题:所有的路由配置都堆在main.js中合适么?
目标:将路由模块抽离出来。
路由模块的封装抽离的好处是什么?
- 拆分模块,利于维护
以后如何快速引入组件?
- 基于 @ 指代 src 目录,从 src 目录出发找组件
- 一般放在 @/src/router
2. 声明式导航
2.1 导航链接/导航高亮
router-link是什么?
- vue-router 提供的全局组件, 用于替换 a 标签
router-link怎么用?
<router-link to="/路径值"></router-link>- 必须传入to属性, 指定路由路径值
router-link好处?
能跳转,配置 to 属性指定路径(必须) 。本质还是 a 标签 ,to 无需 #能高亮,默认就会提供高亮类名,可以直接设置高亮样式
2.2 两个类名/精确匹配&模糊匹配
我们发现 router-link 自动给当前导航添加了 两个高亮类名
-
router-link-active 模糊匹配 (用的多)to="/my" 可以匹配 /my /my/a /my/b ....
-
router-link-exact-active 精确匹配to="/my" 仅可以匹配 /my
2.3 自定义高亮类名
router-link 的 两个高亮类名太长了,我们希望能定制怎么办?
const router = new VueRouter({
routes: [...],
linkActiveClass: "类名1",
linkExactActiveClass: "类名2"
})
如何自定义router-link 的 两个高亮类名?
- linkActiveClass 模糊匹配 类名自定义
- linkExactActiveClass 精确匹配 类名自定义
2.4 跳转传参
在跳转路由时, 进行传值
-
查询参数传参 - query传参
- 跳转:
to="/path?参数名=值"或 :to={path:路由,query:{参数名:参数值}}<!-- to的字符串写法 --> <router-link :to="/home/detail?id=666&title=你好">跳转</router-link> <!-- to的对象写法 path --> <router-link :to="{ path:'/home/detail?id=666&title=你好', }" >跳转</router-link> > <!-- to的对象写法 path --> <router-link :to="{ path:'/home/message/detail', query:{ id:666, title:'你好' } }" >跳转</router-link> > <!-- to的对象写法 name --> <router-link :to="{ name:'xiangqing', query: { id:666, title:'你好' } }" >跳转</router-link> - 获取(对应页面组件接收传递过来的值)
$route.query.参数名
- 跳转:
-
动态路由传参-params参数
- 配置动态路由
path: '/search/:words'
const router = new VueRouter({ routes: [ ..., { path: '/search/:words', component: Search } ] })-
跳转(配置导航链接)
to="/path/参数值"或者对象写法(不常用)<!-- 跳转并携带params参数,to的字符串写法 --> <router-link :to="/home/message/detail/666/你好">跳转</router-link> <!-- 跳转并携带params参数,to的对象写法,字符串的复杂化写法 --> <router-link :to="{ path:'/home/message/detail/666/你好', }" >跳转</router-link> <!-- 跳转并携带params参数,to的对象写法 --> <router-link :to="{ name:'xiangqing', params: { id:666, title:'你好' } }" >跳转</router-link>注意:如果提供了
path,params会被忽略,所以路由携带 params 参数时,若使用 to 的对象写法,则不能使用 path 配置项,必须使用 name 配置! -
接收(对应页面组件接收传递过来的值)
$route.params.参数名
- 配置动态路由
两种传参方式的区别
-
查询参数传参 (比较适合传多个参数)
① 跳转:to="/path
?参数名=值&参数名2=值"② 获取:$route.query.参数名
-
动态路由传参 (优雅简洁,传单个参数比较方便)
① 配置动态路由:path: "/path/:参数名"
② 跳转:to="/path/
参数值"③ 获取:$route.params.参数名
2.5 动态路由参数可选符
/search/:words 表示必须要传参数。如果不传参数,也希望匹配,可以加个可选符 "?" path: '/search/:words?'
const router = new VueRouter({
routes: [
...,
{
path: '/search/:words?',
component: Search
}
]
})
2.6 <router-link>的replace属性
作用:控制路由跳转时操作浏览器历史记录的模式
浏览器的历史记录有两种写入方式:分别为push和replace,push是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
如何开启replace模式:<router-link replace .......>News</router-link>
3. 路由重定向/404/路由模式
3.1 重定向
问题:网页打开,url 默认是 / 路径,未匹配到组件时,会出现空白
解决:重定向 → 匹配 path后,强制跳转 path 路径
语法: { path: 匹配路径, redirect: 重定向到的路径 },
const router = new VueRouter({
routes: [
{ path: '/', redirect: '/home'},
{ path: '/home', component: Home },
{
path: '/search/:words?',
component: Search
}
]
})
3.2 404
作用:当路径找不到匹配时,给个提示页面
位置:配在路由最后
语法:path: "*" (任意路径) – 前面不匹配就命中最后这个
const router = new VueRouter({
routes: [
{ path: '/', redirect: '/home'},
{ path: '/home', component: Home },
{
path: '/search/:words?',
component: Search
},
{` path: '*', component: NotFind` }
]
})
3.3 模式设置
对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
问题:路由的路径看起来不自然, 有#,能否切成真正路径形式?
路由器的两种工作模式
- hash路由(默认) 例如:
http://localhost:8080/#/home - history路由(常用) 例如:
http://localhost:8080/home(以后上线需要服务器端支持)
const router = new VueRouter({
routes,
//不写mode默认是hash
mode: "history"
})
两种模式的比较
-
hash模式:
- 地址中永远带着#号,不美观。
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
- 兼容性较好。
-
history模式:
- 地址干净,美观。
- 兼容性和hash模式相比略差。
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
4. 编程式导航
问题:点击按钮跳转如何实现?
在 Vue 实例内部,你可以通过 $router 访问路由实例。因此你可以调用 this.$router.push。
编程式导航:不借助<router-link>,用JS代码来实现路由跳转,让路由跳转更加灵活
router 和 route 的区别
route 路由,只是一个规则,别人应用这个规则
router 路由器,有绝对的指挥权,让你去哪就得去哪
4.1 基本跳转
两种语法:
-
path 路径跳转(简易方便)
this.$router.push('路由路径') this.$router.push({ path: '路由路径' }) -
name 命名路由跳转(适合 path 路径长的场景)
this.$router.push({ name: '路由名' })注意:使用name命名路由跳转时,定义路由的时候,需要有name {
name: '路由名', path: '/path/xxx', component: XXX }
//$router的两个API除了push还有replace
this.$router.push({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})
this.$router.replace({
name:'xiangqing',
params:{
id:xxx,
title:xxx
}
})
this.$router.forward() //前进
this.$router.back() //后退
this.$router.go() //可前进也可后退
4.2 编程式导航传参
问题:点击搜索按钮,跳转需要传参如何实现?
两种传参方式:查询参数 + 动态路由传参
两种跳转方式,对于两种传参方式都支持:
- path 路径跳转传参
-
query传参
this.$router.push('/路径?参数名1=参数值1&参数2=参数值2') this.$router.push({ path: '/路径?参数名1=参数值1&参数2=参数值2', }) //完整写法,更适合传参 this.$router.push({ path: '/路径', query: { 参数名1: '参数值1', 参数名2: '参数值2' } }) //query传参,接收参数是:$route.query.参数名 -
动态路由传参(需要配动态路由)
this.$router.push('/路径/参数值') this.$router.push({ path: '/路径/参数值' })
注意:如果提供了
path,params会被忽略 -
- name 命名路由跳转传参
-
query传参
this.$router.push({ name: '路由名字', query: { 参数名1: '参数值1', 参数名2: '参数值2' } }) -
动态路由传参
this.$router.push({ name: '路由名字', params: { 参数名: '参数值', } })
-
4.3 声明式导航和编程式导航
想要导航到不同的 URL,则使用 router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。
当你点击 <router-link> 时,这个方法会在内部调用,所以说,点击 <router-link :to="..."> 等同于调用 router.push(...)。
5. 路由传参 & 路由name
5.1 命名路由 name
作用:可以简化路由的跳转。
如何使用
-
给路由命名:
{ path:'/demo', component:Demo, children:[{ path:'test', component:Test, children:[{ name:'hello' //给路由命名 path:'welcome', component:Hello, }] }] } -
简化跳转:
<!--简化前,需要写完整的路径 --> <router-link to="/demo/test/welcome">跳转</router-link> <!--简化后,直接通过名字跳转 --> <router-link :to="{name:'hello'}">跳转</router-link> <!--简化写法配合传递参数 --> <router-link :to="{ name:'hello', query:{ id:666, title:'你好' } }" >跳转</router-link>
5.2 路由的query参数
-
query传参
<!-- to的字符串写法 --> <router-link :to="/home/detail?id=666&title=你好">跳转</router-link> <!-- to的对象写法 path,一般参数拼接在地址后面用字符串写法,这种不怎么用,复杂化了,了解即可 --> <router-link :to="{ path:'/home/detail?id=666&title=你好', }" <!-- to的对象写法 path --> <router-link :to="{ path:'/home/message/detail', query:{ id:666, title:'你好' } }" >跳转</router-link> <!-- to的对象写法 name --> <router-link :to="{ name:'detail', query:{ id:666, title:'你好' } }" >跳转</router-link> -
获取(对应页面组件接收传递过来的值)
$route.query.参数名
5.3 路由的params参数
-
配置路由,声明接收params参数
{ path:'/home', component:Home, children:[{ path:'news', component:News },{ component:Message, children:[{ name:'xiangqing', path:'detail/:id/:title', //使用占位符声明接收params参数 component:Detail }] }] } -
传递参数
<!-- 跳转并携带params参数,to的字符串写法 --> <router-link :to="/home/message/detail/666/你好">跳转</router-link> <!-- 跳转并携带params参数,to的对象写法,一般参数拼接在地址后面用字符串写法,这种不怎么用,复杂化了,了解即可 --> <router-link :to="{ path:'/home/message/detail/666/你好', }" >跳转</router-link> <!-- 跳转并携带params参数,to的对象写法 --> <router-link :to="{ name:'xiangqing', params:{ id:666, title:'你好' } }" >跳转</router-link>注意:如果提供了
path,params会被忽略,所以路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置! -
接收参数:
$route.params.id $route.params.title
5.4 路由传参注意
-
如果提供了
path,params会被忽略 -
想要导航到不同的 URL,使用
router.push方法。当点击<router-link>时,这个方法会在内部调用,所以说,点击<router-link :to="...">等同于调用router.push(...) -
<router-link :to="...">和router.push(...)他们传参都是一致的,这一节代码里用的是<router-link>举例,实际换成router.push也可以实现
5.5 路由的props配置
作用:让路由组件更方便的收到参数
{
name:'xiangqing',
path:'detail/:id',
component:Detail,
//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
// props:{a:900}
//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
// props:true
//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
props($route) {
return {
id: $route.query.id,
title:$route.query.title,
a: 1,
b: 'hello'
}
}
}
方便在要跳转去的组件里更简便的写法
跳转去组件的具体代码
<template>
<ul>
<h1>Detail</h1>
<li>消息编号:{{id}}</li>
<li>消息标题:{{title}}</li>
<li>a:{{a}}</li>
<li>b:{{b}}</li>
</ul>
</template>
<script>
export default {
name: 'Detail',
props: ['id', 'title', 'a', 'b'],
mounted () {
console.log(this.$route);
}
}
</script>
6. 组件缓存 keep-alive
问题:从面经 点到 详情页,又点返回,数据重新加载了 → 希望回到原来的位置
原因:路由跳转后,组件被销毁了,返回回来组件又被重建了,所以数据重新被加载了
解决:利用 keep-alive 将组件缓存下来
6.1 keep-alive是什么
-
keep-alive 是 Vue 的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
-
keep-alive 是一个抽象组件:它自身不会渲染成一个 DOM 元素,也不会出现在父组件链中。
<template>
<div class="h5-wrapper">
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
</template>
作用:让不展示的路由组件保持挂载,不被销毁。
6.2 keep-alive的优点
在组件切换过程中 把切换出去的组件保留在内存中,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性。
6.3 keep-alive的三个属性
问题:缓存了所有被切换的组件
keep-alive的三个属性
① include:组件名数组,只有匹配的组件会被缓存
② exclude:组件名数组,任何匹配的组件都不会被缓存
③ max:最多可以缓存多少组件实例
组件名,是组件name的值,如果没有配置name,才会找文件名做为组件名
<!-- 这个 include 指的是组件名 -->
<template>
<div class="h5-wrapper" :include="['LayoutPage']>
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
</template>
<!--缓存一个路由组件 -->
<keep-alive include="News">
<router-view></router-view>
</keep-alive>
<!--缓存多个路由组件 -->
<keep-alive :include="['News','Message']">
<router-view></router-view>
</keep-alive>
6.4 两个生命周期函数
keep-alive的使用会触发的两个生命周期函数
作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
-
activated激活时,组件被看到时触发。当组件被激活(使用)的时候触发 → 进入这个页面的时候触发 -
deactivated失活时,离开页面,组件看不见时触发。当组件不被使用的时候触发 → 离开这个页面的时候触发
组件缓存后就不会执行组件的 created, mounted, destroyed 等钩子了
所以其提供了actived 和 deactived钩子,帮我们实现业务需求。
这两个生命周期钩子需要配合前面的缓存路由组件使用(没有缓存路由组件不起效果)
activated () {
console.log('actived 激活 → 进入页面');
},
deactivated() {
console.log('deactived 失活 → 离开页面');
}
6.5 keep-alive 小结
-
keep-alive是什么
Vue 的内置组件,包裹动态组件时,可以缓存
-
keep-alive的优点
组件切换过程中 把切换出去的组件保留在内存中(提升性能)
-
keep-alive的三个属性 (了解)
① include:组件名数组,只有匹配的组件会被缓存
② exclude:组件名数组,任何匹配的组件都不会被缓存
③ max:最多可以缓存多少组件实例
-
keep-alive的使用会触发两个生命周期函数 (了解)
activated 当组件被激活(使用)的时候触发 → 进入页面触发
deactivated 当组件不被使用的时候触发 → 离开页面触发
-
缓存组件可以用哪个内置组件?
keep-alive
三个属性: include exclude max
两个钩子: activated deactivated
7. 路由守卫
7.1 作用
对路由进行权限控制
7.2 分类
全局守卫、路由独享守卫、组件内守卫
7.3 全局守卫
//全局前置守卫:初始化时执行、每次路由切换前执行
router.beforeEach((to,from,next)=>{
console.log('beforeEach',to,from)
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'zhejiang'){ //权限控制的具体规则
next() //放行
}else{
alert('暂无权限查看')
// next({name:'guanyu'})
}
}else{
next() //放行
}
})
//全局后置守卫:初始化时执行、每次路由切换后执行
router.afterEach((to,from)=>{
console.log('afterEach',to,from)
if(to.meta.title){
document.title = to.meta.title //修改网页的title
}else{
document.title = 'vue_test'
}
})
完整代码
// 这个文件专门用于创建整个应用的路由器
import VueRouter from 'vue-router'
// 引入组件
import About from '../pages/About.vue'
import Home from '../pages/Home.vue'
import Message from '../pages/Message.vue'
import News from '../pages/News.vue'
import Detail from '../pages/Detail.vue'
// 创建并暴露一个路由器
const router = new VueRouter({
routes: [
{
path: '/home',
component: Home,
meta:{title:'主页'},
children: [
{
path: 'news',
component: News,
meta:{isAuth:true,title:'新闻'}
},
{
path: 'message',
name: 'mess',
component: Message,
meta:{isAuth:true,title:'消息'},
children: [{
path: 'detail/:id/:title',
name: 'xiangqing',
component: Detail,
meta:{isAuth:true,title:'详情'},
props($route) {
return {
id: $route.query.id,
title:$route.query.title,
a: 1,
b: 'hello'
}
}
}]
}
]
},
{
path: '/about',
component: About,
meta:{ title: '关于' }
}
]
})
// 全局前置路由守卫————初始化的时候被调用、每次路由切换之前被调用
router.beforeEach((to, from, next) => {
console.log('前置路由守卫', to, from);
if(to.meta.isAuth) {
if(localStorage.getItem('school') === 'zhejiang') {
// 放行
next()
} else {
alert('学校名不对,无权查看')
}
} else {
next()
}
})
// 全局后置路由守卫————初始化的时候被调用、每次路由切换之后被调用
router.afterEach((to, from) => {
console.log('后置路由守卫', to, from)
document.title = to.meta.title || '我的系统'
})
export default router
7.4 路由独享守卫
就是在 routes 子路由内写守卫
beforeEnter(to,from,next){
console.log('beforeEnter',to,from)
if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
if(localStorage.getItem('school') === 'atguigu'){
next()
}else{
alert('暂无权限查看')
// next({name:'guanyu'})
}
}else{
next()
}
}
7.5 组件内守卫
在具体组件内写守卫
//进入守卫:通过路由规则,进入该组件时被调用
beforeRouteEnter (to, from, next) {
},
//离开守卫:通过路由规则,离开该组件时被调用
beforeRouteLeave (to, from, next) {
}
8. 路由底层 history
你也许注意到 router.push、 router.replace 和 router.go 跟 window.history.pushState、 window.history.replaceState 和 window.history.go (opens new window)好像, 实际上它们确实是效仿 window.history API 的。
因此,如果你已经熟悉 Browser History APIs (opens new window),那么在 Vue Router 中操作 history 就是超级简单的。
9. 基于 VueCli 自定义创建项目
9.1 ESlint 代码规范
认识代码规范
代码规范:一套写代码的约定规则。例如:"赋值符号的左右是否需要空格" "一句结束是否是要加;" ...
老话说:"没有规矩不成方圆" → 正规的团队 需要 统一的编码风格
JavaScript Standard Style 规范说明 standardjs.com/rules-zhcn.…
下面是这份规则中的一小部分:
- 字符串使用单引号 'abc'
- 无分号 const name = 'zs'
- 关键字后加空格 if (name = 'ls') { ... }
- 函数名后加空格 function name (arg) { ... }
- 坚持使用全等 === 摒弃 ==
- ...
9.1.1 代码规范错误
如果你的代码不符合 standard 的要求,ESlint 会跳出来刀子嘴,豆腐心地提示你。
比如:在main.js中随意做一些改动,添加一些分号,空行。
两种解决方案:
-
手动修正
根据错误提示来一项一项
手动修改纠正。如果你不认识命令行中的语法报错是什么意思,根据错误代码去
[[ESLint 规则表]](https://zh-hans.eslint.org/docs/latest/rules/)中查找其具体含义。 -
自动修正
基于 vscode 插件 ESLint
高亮错误,并通过配置 自动帮助我们修复错误。- vscode安装ESLint
- 配置:
增加以下配置内容
//当保存到时候,eslint自动帮我们修复错误 "editor.codeActionsOnSave": { "source.fixAll": true }, //保存代码,不自动格式化 "editor.formatOnSave": false
10. 面试常见问题
10.1 vue-router 是干什么的,原理是什么?
vue-router 是 Vue.js 官方的路由插件,它和 vue.js 是深度集成的,适合用于构建单页面应用。vue 的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。传统的页面应用,是用一些超链接来实现页面切换和跳转的。在 vue-router 单页面应用中,则是路径之间的切换,也就是组件的切换。路由模块的本质 就是建立起 url 和页面之间的映射关系。
“更新视图但不重新请求页面”是前端路由原理的核心之一,目前在浏览器环境中这一功能的实现主要有两种方式:
-
利用 URL 中的 hash(“#”)
这种方式主要利用了浏览器对 hash 的天然支持,url 中hash 值的改变,不会重新加载页面,也就是说 URL 的 hash 永远不会发送到服务器,因此它不会被包含在 HTTP 请求中。这使得它非常适合用来做前端路由,而不需要服务器端的任何配置。
通过 window.onhashchange(hashchange事件) 的监听, 匹配不同的url路径,进行解析,加载不同的组件,然后动态的渲染出区域内的html内容。
注意:事件 hashchange 只会在 hash 发生变化时才能触发,而第一次加载页面时并不会触发这个事件,因此还需要监听 load 事件
-
利用 History interface 在 HTML5 中新增的 pushState() 和replaceState() 方法。需要特定浏览器支持
history模式,会出现404 的情况,需要后台配置。