vue-router(SPA)无刷新改变路由的原理

此文主要是对spa页面的路由原理的探讨

分以下几点来讲

  1. 路由是什么?
  2. spa是什么?
  3. vue-router如何实现无刷新改变路由?
  4. spa的seo的问题

1. 路由是什么?

背景:路由是后端先提出的,用于模板引擎开发页面。如: http://www.xxx.com/login

用路由访问资源的流程:

  1. 浏览器发起请求, 如: http://www.xxx.com/
  2. 服务器监听80端口(或443),如果有请求过来,解析url路径
    • 根据路径(路由配置),返回对应资源(如html,css,js,json字符串)
  1. 浏览器根据响应头 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,不会发出请求。有两种方法

  1. hash 模式(2014前)
    • 例如:http://www.xxx.com/#/login(加#后,改变url不会发起请求)
  1. history模式(去掉‘#’,需服务端配合)(14年后HTML5发布,多了pushState和replaceState)
    • 通过pushState和replaceState 2个API可以实现:改变url地址,不会发起请求,同是还有popState事件

1. hash 模式介绍

在url后面加#后,改变url不会发起请求,同时可以用 hashchange事件 监听路由的变化

  1. window.addEventListener('hashchange',function(event){
  2. console.log(event);
  3. // 以下可操作 把路由的对应组件,替换到`<router-view></router-view>`上去。
  4. })

2. history 模式介绍

特点是:没有#,但需要服务端配合

场景1:用户在浏览器地址栏 填入url,比如 http://www.xxx.com/xxxx/xx/xx/xxx

  • 无论后面写了多长,多复杂,服务端要做的事情只有一个,就是返回根index.html,就像访问http://www.xxx.com一样(vue-router内部会去解析完整的url,然后显示路由对应的资源)
  • 此场景,页面会刷新

场景2:用户在页面内操作,比如点击菜单,然后切换路由,展示菜单对应的内容

  • 此场景,页面不会刷新,具体操作如下
  1. 监听back/forward/go
    如果是
    this.$router.go(n), this.$router.back(), this.$router.forward()
    那么会触发popstate事件
    改变url是用原生history的api:history.back(),history.forward()、history.go()

    1. window.addEventListener('popstate', function(event) {
    2. console.log(event);
    3. // 以下可操作 把路由的对应组件,替换到`<router-view></router-view>`上去。
    4. })
  2. 监听push/replace
    如果是
    this.$router.push(), this.$router.replace()
    不会触发popstate事件,需要自己手动增加事件(new Event()),如下
    改变url是用原生api:history.pushState,history.replaceState

    • 例如:history.pushState(null, null, ‘test’) 官方文档
  1. // 改造
  2. const _historyWrap = function(type) {
  3. const orig = history[type];
  4. const e = new Event(type);
  5. return function() {
  6. const rv = orig.apply(this, arguments);
  7. e.arguments = arguments;
  8. window.dispatchEvent(e);
  9. return rv;
  10. };
  11. };
  12. history.pushState = _historyWrap('pushState');
  13. history.replaceState = _historyWrap('replaceState');
  14. // 监听
  15. window.addEventListener('pushState', function(e) {
  16. console.log('change pushState');
  17. // 以下可操作 把路由的对应组件,替换到`<router-view></router-view>`上去。
  18. });
  19. window.addEventListener('replaceState', function(e) {
  20. console.log('change replaceState');
  21. // 以下可操作 把路由的对应组件,替换到`<router-view></router-view>`上去。
  22. });


另外 vue-router的跳转 和 location.href 有什么区别?

  • location.href 是直接改变url,会向服务端发起请求,会刷新页面

4. spa的seo的问题

使用nginx + history的路由模式

原理:nginx拦截请求,分爬虫和正常用户,分别处理

  1. 如果是爬虫,则返回事先预渲染好的html
    1. 如果你的spa路由使用的是hash路由,要改造成history路由(vue中的叫法,其他spa可能有所不同),因为hash无法传播,后端拿不到hash参数
  1. 如果是用户,就正常流程,返回根html

原创整理,有错误可留言,如有用,谢谢点赞~

部分参考:https://segmentfault.com/a/1190000022822185