Vue Router 路由实现原理
目录
一、概念
通过改变URL,在不重新请求页面的情况下,更新页面视图。
二、两种实现方式
更新视图但不重新请求页面,是前端路由原理的核心之一,目前在浏览器环境中这一功能的实现主要有2种方式:
- Hash—利用URL中的hash(“#”);
- 利用History interface在HTML5中新增的方法。
mode参数
Vue中,通过mode这一参数来决定采用哪一种方式,选择流程如下:
-
默认为hash
-
使用history时,进行如下设置(如果浏览器比支持history新特性,则采用hash方式)
const router=new VueRouter({
mode:'history',//设置路由实现方式为history
routes:[...]
})
- 如果不在浏览器环境下则使用abstract(node环境下)
创建VueRouter的实例对象时,mode以构造参数的形式传入,如下代码:
src/index.js
export default class VueRouter{
mode: string; // 传入的字符串参数,指示history类别
history: HashHistory | HTML5History | AbstractHistory; // 实际起作用的对象属性,必须是以上三个类的枚举
fallback: boolean; // 如浏览器不支持,'history'模式需回滚为'hash'模式
constructor (options: RouterOptions = {}) {
let mode = options.mode || 'hash' // 默认为'hash'模式
this.fallback = mode === 'history' && !supportsPushState // 通过supportsPushState判断浏览器是否支持'history'模式
if (this.fallback) {
mode = 'hash'
}
if (!inBrowser) {
mode = 'abstract' // 不在浏览器环境下运行需强制为'abstract'模式
}
this.mode = mode
// 根据mode确定history实际的类并实例化
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback)
break
case 'abstract':
this.history = new AbstractHistory(this, options.base)
break
default:
if (process.env.NODE_ENV !== 'production') {
assert(false, `invalid mode: ${mode}`)
}
}
}
init (app: any /* Vue component instance */) {
const history = this.history
// 根据history的类别执行相应的初始化操作和监听
if (history instanceof HTML5History) {
history.transitionTo(history.getCurrentLocation())
} else if (history instanceof HashHistory) {
const setupHashListener = () => {
history.setupListeners()
}
history.transitionTo(
history.getCurrentLocation(),
setupHashListener,
setupHashListener
)
}
history.listen(route => {
this.apps.forEach((app) => {
app._route = route
})
})
}
// VueRouter类暴露的以下方法实际是调用具体history对象的方法
push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
this.history.push(location, onComplete, onAbort)
}
replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
this.history.replace(location, onComplete, onAbort)
}
}
源码
HashHistory
简介
hash(“#”)的作用是加载URL中指示的网页的位置。
#
本身以及它后面的字符,可以通过window.location.hash
获取。
特点
- 通过hash方式的,路由地址会有
#
举例:
http://localhost:8080/#/a
- #后面的内容不会传给服务端,也就是说不会重新刷新页面。并且路由切换的时候也不会重新加载页面。
因此改变hash不会重新加载页面
- 每一次改变
hash(window.localtion.hash)
,都会在浏览器访问历史中增加一个记录。
举例:
http://localhost:8080/#/a
//变为 如下地址,浏览器访问历史中会增加一个记录
http://localhost:8080/#/b
- 可以为 hash 的改变添加监听事件:
window.addEventListener("hashchange",funcRef,false)
利用hash的以上特点,就可以来实现“前端路由”更新视图但不重新请求页面的功能了。
方法
HashHistory 拥有两个方法,一个是HashHistory.push(), 一个是HashHistory.replace()
HashHistory.push()
将路由添加到浏览器访问历史的栈顶
如图:
从设置路由改变到视图更新的流程:
$router.push() --> HashHistory.push() --> History.transitionTo() --> History.updateRoute() --> {app._route = route} --> vm.render()
详解:
$router.push() //调用方法
HashHistory.push() //根据hash模式调用,设置hash并添加到浏览器历史记录(添加到栈顶)(window.location.hash = XXX)
History.transitionTo() //监测更新,更新则调用History.updateRoute()
History.updateRoute() //更新路由
{app._route= route} //
vm.render() //更新视图
HashHistory.replace()
replace()方法与push()方法不同之处在于,他并不是将新路由添加到浏览器访问历史的栈顶,而是替换掉当前路由。
如图:
HTML5History
简介
History interface是浏览器历史记录栈提供的接口,通过back()
,forward()
,go()
等方法,我们可以读取浏览器历史记录栈的信息,进行各种跳转操作。
从Html5开始,History interface提供了两个新的方法:pushState()
,replaceState()
提供了2个新方法:pushState(),replaceState()使得我们可以对浏览器历史记录栈进行修改:
window.history.pushState(stateObject,title,url)
window.history,replaceState(stateObject,title,url)
参数分析:
- stateObject:当浏览器跳转到新的状态时,将触发
Popstate
事件,该事件将携带这个stateObject参数的副本 - title:所添加记录的标题
- url:所添加记录的url
特点
- 通过history模式,实现单页路由的URL没有
#
,所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求。
http://localhost:8080/a
-
为了避免这种情况,所以history路由实现方式需要服务器的支持,需要把所有的路由都定向到根页面。
-
在HTML5History的构造函数中监听使用
popState(window.onpopstate)
方法
history.pushState()
与hash模式类似,只是将window.hash()
改为history.pushState()
history.replaceState()
与hash模式类似,只是将window.replace()
改为history.replaceState()
三、两种模式比较
-
pushState设置的新的URL可以是与当前URL同源的任意URL;而hash只可修改
#
后边的部分,故只可设置与当前文档同文档的URL -
pushState通过stateObject可以添加任意类型的数据到记录中;而hash只可添加短字符串
-
pushState可额外设置title属性后供后续使用
-
history模式则会将URL修改得就和正常请求后端的URL一样,如后端没有配置对应/user/id的路由处理,则会返回404错误
相关文章
- Vue - 项目配置 ( element , 安装路由 , 创建路由 )
- vue状态管理模式之vuex
- vue 使用路由重复跳转同一页面
- vue-router动态路由控制
- thinkphp6:前后端分离使用表单令牌(php 8.1.1 / thinkphp v6.0.10LTS/vue 3.2.26)
- vue-router动态路由控制
- vue.js3.2.20: 在打包时取消生成.map文件
- ubuntu21.04(linux):安装node(14.17.1)和vue.js3.1环境
- vue-router路由示例
- vue路由报错export ‘default‘ (imported as ‘VueRouter‘) was not found in ‘vue-router‘
- 新来的前端小姐姐问:Vue路由history模式刷新页面出现404问题
- 使用Vue-Router 2实现路由功能
- vue-router(路由嵌套)
- Vue中router路由的使用、router-link的使用(在项目中的实际运用方式)
- vue-router2路由参数注意问题
- vue-cli搭建及项目目录结构
- Vue中vue-i18n结合vant-ui实现国际化
- 034:vue+openlayers上传KMZ文件并在map上解析(代码示例)
- 测试开发之Vue学习笔记-Vue路由
- 基于Java+SpringBoot+Vue前后端分离图书借阅系统设计与实现(有视频讲解)
- Vue: vue-router路由
- vue路由传递参数的几种方式详解