前端路由以及vue路由的hash模式与history模式

什么是路由javascript

路由这个概念最早是后端出现的。在之前用模板引擎开发页面时,常常会看到这样php

http://hometown.xxx.edu.cn/bbs/forum.php
复制代码

有时还会有带.asp或.html的路径,这就是所谓的SSR(Server Side Render),经过服务端渲染,直接返回页面。html

其响应过程是这样的前端

  1. 浏览器发出请求
  2. 服务器监听到80端口(或443)有请求过来,并解析url路径
  3. 根据服务器的路由配置,返回相应信息(能够是 html 字串,也能够是 json 数据,图片等)
  4. 浏览器根据数据包的Content-Type来决定如何解析数据

简单来讲路由就是用来跟后端服务器进行交互的一种方式,经过不一样的路径,来请求不一样的资源,请求不一样的页面是路由的其中一种功能。vue

前端路由的诞生的原因java

前端路由的出现要从 ajax 开始,为何?且听下面分析react

Ajax,全称 Asynchronous JavaScript And XML,是浏览器用来实现异步加载的一种技术方案。在 90s 年代初,大多数的网页都是经过直接返回 HTML 的,用户的每次更新操做都须要从新刷新页面。及其影响交互体验,随着网络的发展,迫切须要一种方案来改善这种状况。nginx

1996,微软首先提出 iframe 标签,iframe 带来了异步加载和请求元素的概念,随后在 1998 年,微软的 Outloook Web App 团队提出 Ajax 的基本概念(XMLHttpRequest的前身),并在 IE5 经过 ActiveX 来实现了这项技术。在微软实现这个概念后,其余浏览器好比 Mozilia,Safari,Opera 相继以 XMLHttpRequest 来实现 Ajax。不过在 IE7 发布时,微软选择了妥协,兼容了 XMLHttpRequest 的实现。ajax

有了 Ajax 后,用户交互就不用每次都刷新页面,体验带来了极大的提高。vue-router

但真正让这项技术发扬光大的,(。・∀・)ノ゙仍是后来的 Google Map,它的出现向人们展示了 Ajax 的真正魅力,释放了众多开发人员的想象力,让其不只仅局限于简单的数据和页面交互,为后来异步交互体验方式的繁荣发展带来了根基。

单页应用。单页应用不只仅是在页面交互是无刷新的,连页面跳转都是无刷新的,为了实现单页应用,因此就有了前端路由。

单页应用的概念是伴随着 MVVM 出现的。最先由微软提出,而后他们在浏览器端用 Knockoutjs 实现。但这项技术的强大之处并未当时的开发者体会到,多是由于 Knockoutjs 实现过于复杂,致使没有大面积的扩散。

一样,此次接力的选手依然是 Google。Google 经过 Angularjs 将 MVVM 及单页应用发扬光大,让前端开发者可以开发出更加大型的应用,职能变得更大了。(不得不感慨,微软 跟 Google 都是伟大的公司)。随后都是你们都知道的故事,前端圈开始获得了爆发式的发展,陆续出现了不少优秀的框架。

从 vue-router 来看前端路由实现原理

前端路由的实现其实很简单。

本质上就是检测 url 的变化,截获 url 地址,而后解析来匹配路由规则。

可是这样有人就会问:url 每次变化都会刷新页面啊?页面都刷新了,JavaScript 怎么检测和截获 url?

在 2014 年以前,你们是经过 hash 来实现路由,url hash 就是相似于

https://segmentfault.com/a/1190000011956628#articleHeader2
复制代码

这种 #。后面 hash 值的变化,并不会致使浏览器向服务器发出请求,浏览器不发出请求,也就不会刷新页面。另外每次 hash 值的变化,还会触发 hashchange 这个事件,经过这个事件咱们就能够知道 hash 值发生了哪些变化。

让咱们来整理思路,假如咱们要用 hash 的模式实现一个路由,那么流程应该是这样的。

在这里插入图片描述
vue-router hash

/** * 添加 url hash 变化的监听器 */
setupListeners () {
  const router = this.router

  /** * 每当 hash 变化时就解析路径 * 匹配路由 */
  window.addEventListener('hashchange', () => {
    const current = this.current
    /** * transitionTo: * 匹配路由 * 并经过路由配置,把新的页面 render 到 ui-view 的节点 */
    this.transitionTo(getHash(), route => {
      replaceHash(route.fullPath)
    })
  })
}
复制代码

检测到 hash 的变化后,就能够经过替换 DOM 的方式来实现页面的更换。

14年后,由于HTML5标准发布。多了两个 API,pushState 和 replaceState,

经过这两个 API 能够改变 url 地址且不会发送请求。同时还有 onpopstate 事件。经过这些就能用另外一种方式来实现前端路由了,但原理都是跟 hash 实现相同的。用了 HTML5 的实现,单页路由的 url 就不会多出一个#,变得更加美观。但由于没有 # 号,因此当用户刷新页面之类的操做时,浏览器仍是会给服务器发送请求。为了不出现这种状况,因此这个实现须要服务器的支持,须要把全部路由都重定向到根页面。

在这里插入图片描述
vue-router history

export class HTML5History extends History {
  constructor (router, base) {
    super(router, base)
    /** * 原理仍是跟 hash 实现同样 * 经过监听 popstate 事件 * 匹配路由,而后更新页面 DOM */
    window.addEventListener('popstate', e => {
      const current = this.current

      // Avoiding first `popstate` event dispatched in some browsers but first
      // history route not updated since async guard at the same time.
      const location = getLocation(this.base)
      if (this.current === START && location === initLocation) {
        return
      }

      this.transitionTo(location, route => {
        if (supportsScroll) {
          handleScroll(router, route, current, true)
        }
      })
    })
  }

  go (n) {
    window.history.go(n)
  }

  push (location, onComplete, onAbort) {
    const { current: fromRoute } = this
    this.transitionTo(location, route => {
      // 使用 pushState 更新 url,不会致使浏览器发送请求,从而不会刷新页面
      pushState(cleanPath(this.base + route.fullPath))
      onComplete && onComplete(route)
    }, onAbort)
  }

  replace (location, onComplete, onAbort) {
    const { current: fromRoute } = this
    this.transitionTo(location, route => {
      // replaceState 跟 pushState 的区别在于,不会记录到历史栈
      replaceState(cleanPath(this.base + route.fullPath))
      onComplete && onComplete(route)
    }, onAbort)
  }
}
复制代码

对于vue这类渐进式前端开发框架,为了构建 SPA(单页面应用),须要引入前端路由系统,这也就是 Vue-Router 存在的意义。前端路由的核心,就在于 —— 改变视图的同时不会向后端发出请求。

区别

  • hash --- 即地址栏 URL 中的 # 符号(此 hash 不是密码学里的散列运算)。好比这个 URL:www.abc.com/#/hello,hash 的值为 #/hello。
    • 它的特色在于:hash 虽然出如今 URL 中,但不会被包括在 HTTP 请求中,对后端彻底没有影响,所以改变 hash 不会从新加载页面。
  • history --- 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。(须要特定浏览器支持)这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会当即向后端发送请求

所以能够说,hash 模式和 history 模式都属于浏览器自身的特性,Vue-Router 只是利用了这两个特性(经过调用浏览器提供的接口)来实现前端路由. 使用场景 通常场景下,hash 和 history 均可以,除非你更在乎颜值,# 符号夹杂在 URL 里看起来确实有些不太美丽。

若是不想要很丑的 hash,咱们能够用路由的 history 模式,这种模式充分利用 history.pushState API 来完成URL 跳转而无须从新加载页面。

另外,根据 Mozilla Develop Network 的介绍,调用 history.pushState() 相比于直接修改 hash,存在如下优点:

  • pushState() 设置的新 URL 能够是与当前 URL 同源的任意 URL;而 hash 只可修改 # 后面的部分,所以只能设置与当前 URL 同文档的 URL;
  • pushState() 设置的新 URL 能够与当前 URL 如出一辙,这样也会把记录添加到栈中;而 hash 设置的新值必须与原来不同才会触发动做将记录添加到栈中;
  • pushState() 经过 stateObject 参数能够添加任意类型的数据到记录中;而 hash 只可添加短字符串;
  • pushState() 可额外设置 title 属性供后续使用。

固然啦,history 也不是样样都好。SPA 虽然在浏览器里游刃有余,但真要经过 URL 向后端发起 HTTP 请求时,二者的差别就来了。尤为在用户手动输入 URL 后回车,或者刷新(重启)浏览器的时候,会报一个404的错误,须要后端配置重定向到正确的路由便可解决该问题。

总结

  1. hash 模式下,仅 hash 符号以前的内容会被包含在请求中,如 www.abc.com,所以对于后端来讲,即便没有作到对路由的全覆盖,也不会返回 404 错误。
  2. history 模式下,前端的 URL 必须和实际向后端发起请求的 URL 一致,如 www.abc.com/book/id。若是后段没有对 /book/id 的路由作处理,将返回 404 错误。Vue-Router 官网里如此描述:“不过这种模式要玩好,还须要后台配置支持……因此呢,你要在服务端增长一个覆盖全部状况的候选资源:若是 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。”
  3. 结合自身例子,对于通常的 Vue + Vue-Router + Webpack + XXX 形式的 Web 开发场景,用 history 模式便可,只需在后端(Apache 或 Nginx)进行简单的路由配置,同时搭配前端路由的 404 页面支持。

模板回复

大牛回答:hash模式url里面永远带着#号,咱们在开发当中默认使用这个模式。那么何时要用history模式呢?若是用户考虑url的规范那么就须要使用history模式,由于history模式没有#号,是个正常的url适合推广宣传。固然其功能也有区别,好比咱们在开发app的时候有分享页面,那么这个分享出去的页面就是用vue或是react作的,我们把这个页面分享到第三方的app里,有的app里面url是不容许带有#号的,因此要将#号去除那么就要使用history模式,可是使用history模式还有一个问题就是,在访问二级页面的时候,作刷新操做,会出现404错误,那么就须要和后端人员配合让他配置一下apache或是nginx的url重定向,重定向到你的首页路由上就ok啦。

相关文章
相关标签/搜索