vue-router(SPA)无刷新改变路由的原理
此文主要是对spa页面的路由原理的探讨
分以下几点来讲
- 路由是什么?
- spa是什么?
- vue-router如何实现无刷新改变路由?
- spa的seo的问题
1. 路由是什么?
背景:路由是后端先提出的,用于模板引擎开发页面。如: http://www.xxx.com/login
用路由访问资源的流程:
- 浏览器发起请求, 如:
http://www.xxx.com/
- 服务器监听80端口(或443),如果有请求过来,解析url路径
- 根据路径(路由配置),返回对应资源(如html,css,js,json字符串)
- 浏览器根据响应头 Content-Type 来决定如何解析数据
特点是:(传统的方式,非SPA)
- 改变路由,页面就会刷新。因为改变路由,就是重新获取资源
路由的本质:
- 是应用(客户端)用来跟后端(服务器)交互的一种方式
- 通过不同的路径,请求不同的资源,展示不同的页面
2. spa是什么?
SPA:single page app 单页面应用
SPA的最大特点
- 无刷新改变路由,且视图也会根据路由对应的展示
- 好处是性能和用户体验都更优秀(不用刷新浏览器,就能实现路由的功能),减轻服务端的压力
3. vue-router(SPA)如何实现无刷新改变路由?
vue-router的工作原理:本质上是,监听路由的变化,加载对应的资源(如js,css,img),然后把路由的对应组件,替换到<router-view></router-view>
上去。然后渲染出对应的html内容
那到底是如何实现无刷新改变路由的呢?
首先看看 vue-router 的 mode
- 类型:
string
- 默认值:
"hash" (浏览器环境) | "abstract" (Node.js 环境)
- 可选值:
"hash" | "history" | "abstract"
路由模式介绍:
hash
: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器。history
: 依赖 HTML5 History API 和服务器配置。查看 HTML5 History 模式abstract
: (如果发现没有浏览器的 API,路由会自动强制进入这个模式,类似于node环境用的)支持所有 JavaScript 运行环境,如 Node.js 服务器端
跟浏览器url有关的主要是前两个 hash 和 history。接下来也是主要讲这2个
首先要知道,如何改变浏览器的url,不会发出请求。有两种方法
- hash 模式(2014前)
- 例如:
http://www.xxx.com/#/login
(加#后,改变url不会发起请求)
- 例如:
- history模式(去掉‘#’,需服务端配合)(14年后HTML5发布,多了pushState和replaceState)
- 通过
pushState和replaceState
2个API可以实现:改变url地址,不会发起请求,同是还有popState事件
- 通过
1. hash 模式介绍
在url后面加#后,改变url不会发起请求,同时可以用 hashchange事件 监听路由的变化
window.addEventListener('hashchange',function(event){
console.log(event);
// 以下可操作 把路由的对应组件,替换到`<router-view></router-view>`上去。
})
2. history 模式介绍
特点是:没有#,但需要服务端配合
场景1:用户在浏览器地址栏 填入url,比如 http://www.xxx.com/xxxx/xx/xx/xxx
- 无论后面写了多长,多复杂,服务端要做的事情只有一个,就是返回根index.html,就像访问
http://www.xxx.com
一样(vue-router内部会去解析完整的url,然后显示路由对应的资源) - 此场景,页面会刷新
场景2:用户在页面内操作,比如点击菜单,然后切换路由,展示菜单对应的内容
- 此场景,页面不会刷新,具体操作如下
监听back/forward/go
如果是this.$router.go(n), this.$router.back(), this.$router.forward()
那么会触发popstate事件
改变url是用原生history的api:history.back(),history.forward()、history.go()
window.addEventListener('popstate', function(event) {
console.log(event);
// 以下可操作 把路由的对应组件,替换到`<router-view></router-view>`上去。
})
监听push/replace
如果是this.$router.push(), this.$router.replace()
不会触发popstate事件,需要自己手动增加事件(new Event()),如下
改变url是用原生api:history.pushState,history.replaceState- 例如:history.pushState(null, null, ‘test’) 官方文档
// 改造
const _historyWrap = function(type) {
const orig = history[type];
const e = new Event(type);
return function() {
const rv = orig.apply(this, arguments);
e.arguments = arguments;
window.dispatchEvent(e);
return rv;
};
};
history.pushState = _historyWrap('pushState');
history.replaceState = _historyWrap('replaceState');
// 监听
window.addEventListener('pushState', function(e) {
console.log('change pushState');
// 以下可操作 把路由的对应组件,替换到`<router-view></router-view>`上去。
});
window.addEventListener('replaceState', function(e) {
console.log('change replaceState');
// 以下可操作 把路由的对应组件,替换到`<router-view></router-view>`上去。
});
另外 vue-router的跳转 和 location.href 有什么区别?
location.href
是直接改变url,会向服务端发起请求,会刷新页面
4. spa的seo的问题
使用nginx + history的路由模式
原理:nginx拦截请求,分爬虫和正常用户,分别处理
- 如果是爬虫,则返回事先预渲染好的html。
- 如果你的spa路由使用的是hash路由,要改造成history路由(vue中的叫法,其他spa可能有所不同),因为hash无法传播,后端拿不到hash参数
- 如果是用户,就正常流程,返回根html
原创整理,有错误可留言,如有用,谢谢点赞~