从前端路由到hash和history模式

从前端路由到hash和history模式

  • 路由
    • 前端路由的诞生
    • 前端路由的实现
    • hash和history两种模式的区别
    • 本文是个人在了解hash和history模式中所做调查的一些总结以及本人的一些理解,如有错误欢迎指出。

路由

网络原理中,路由指的是根据上一接口的数据包中的IP地址,查询路由表转发到另一个接口,它决定的是一个端到端的网络路径。
web里的话,路由概念也是类似的,解析一个‘端’URL(地址,可能包含了一些参数等)来将请求分配到指定的一个‘端’(控制器、方法等一切处理程序),客户端的请求是以URL的形式传递给服务器的(示例get方法),传统WEB开发中,URL对应服务器上某个目录下的某个文件。
而在MVC开发中,WEB 服务器会截获所有请求,不做资源存在性检查,直接转发给网站的路由程序。路由器再调用相关的控制器。控制器调用相关的服务,并返回视图对象。路由器再从视图对象中提取生成好的网页代码返回给Web服务器,最终返回给客户端。

  1. 后端路由
    后端路由又可称之为服务器端路由,因为对于服务器来说,当接收到客户端发来的HTTP请求,就会根据所请求的相应URL,来找到相应的映射函数,然后执行该函数,并将函数的返回值发送给客户端。对于最简单的静态资源服务器,可以认为,所有URL的映射函数就是一个文件读取操作。对于动态资源,映射函数可能是一个数据库读取操作,也可能是进行一些数据的处理,等等。然后根据这些读取的数据,在服务器端就使用相应的模板来对页面进行渲染后,再返回渲染完毕的页面。这种方式在早期的前端开发中非常普遍,它的好处与坏处都很明显:
    好处:安全性好,SEO好。
    缺点:加大服务器的压力,不利于用户体验,代码冗合。
    作者:Srtian
    链接:https://www.jianshu.com/p/d2aa8fb951e4
  2. 前端路由
    也正是由于后端路由还存在着自己的不足,前端路由才有了属于自 己的一片天地与发展的空间。
    对于前端路由来说,路由的映射函数通常是进行一些DOM的显示和隐藏操作。这样,当访问不同的路径的时候,会显示不同的页面组件。前端路由主要有以下两种实现方案:
    :hash
    :history API
    当然,前端路由也存在缺陷:
    使用浏览器的前进,后退键时会重新发送请求,来获取数据,没有合理地利用缓存。但总的来说,现在前端路由已经是实现路由的主要方式了,我们常用的诸如vue-router等前端框架的路由控制都是基于前端路由进行开发的,因此将前端路由进行一个了解
    还是很有必要的。
    作者:Srtian
    链接:https://www.jianshu.com/p/d2aa8fb951e4

前端路由的诞生

前端路由的出现要从 ajax 开始,Ajax,全称 Asynchronous JavaScript And XML(异步的js和xml),是浏览器用来实现异步加载的一种技术方案。
在 90s 年代初,大多数的网页都是通过直接返回 HTML 的,用户的每次更新操作都需要重新刷新页面。及其影响交互体验,随着网络的发展,迫切需要一种方案来改善这种情况。
1996,微软首先提出 iframe 标签,iframe 带来了异步加载和请求元素的概念,随后在 1998 年,微软的 Outloook Web App 团队提出 Ajax 的基本概念(XMLHttpRequest的前身),并在 IE5 通过 ActiveX 来实现了这项技术。在微软实现这个概念后,其他浏览器比如 Mozilia,Safari,Opera 相继以 XMLHttpRequest 来实现 Ajax。不过在 IE7 发布时,微软选择了妥协,兼容了 XMLHttpRequest 的实现。

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

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

而异步交互体验的更高级版本就是 SPA—— 单页应用。

(异步交互最高级的体验是什么?PWA(渐进式WEB应用)
什么是PWA
2019前端必会黑科技之PWA)

单页应用不仅仅是在页面交互是无刷新的,连页面跳转(如标签)都是无刷新的,为了实现单页应用,所以就有了前端路由。

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

同样,这次接力的选手依然是 Google。Google 通过 Angular.js 将 MVVM 及单页应用发扬光大,让前端开发者能够开发出更加大型的应用,职能变得更大了。随后都是大家都知道的故事,前端圈开始得到了爆发式的发展,陆续出现了很多优秀的框架如vue,react。

https://github.com/hwen/blogSome/issues/2

前端路由的实现

前端路由的实现其实很简单。
从用户的角度看,前端路由主要实现了两个功能(使用ajax更新页面状态的情况下):

记录当前页面的状态(保存或分享当前页的url,再次打开该url时,网页还是保存(分享)时的状态);

可以使用浏览器的前进后退功能(如点击后退按钮,可以使页面回到使用ajax更新页面之前的状态,url也回到之前的状态);

本质上就是监测 url 的变化,截获 url 地址,然后解析来匹配路由规则。
而url 每次变化都会刷新页面,页面都刷新了,JavaScript 怎么检测和截获 url?

  1. 基于hash
    在 2014 年之前,大家是通过 hash 来实现路由,url hash 就是类似于

    >https://www.xxxxx.com/x/#articleHeader2
    

    这种 ,#后面 hash 值的变化,并不会导致浏览器向服务器发出请求,浏览器不发出请求,也就不会刷新页面。另外每次 hash 值的变化,还会触发 hashchange 这个事件,通过这个事件我们就可以知道 hash 值发生了哪些变化。
    让我们来整理思路,假如我们要用 hash 的模式实现一个路由,那么流程应该是这样的。
    从前端路由到hash和history模式_第1张图片
    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路由,看起来就是这个样子:
http://music.163.com/
https://pan.baidu.com/

借鉴: https://github.com/hwen/blogSome/issues/2

  1. 基于history
    14年后,因为HTML5标准发布。多了两个 API,pushStatereplaceState,通过这两个 API 可以改变 url 地址且不会发送请求。同时还有 onpopstate 事件。详见MDN:https://developer.mozilla.org/en-US/docs/Web/API/History
    通过这些就能用另一种方式来实现前端路由了,但原理都是跟 hash 实现相同的。用了 HTML5 的实现,单页路由的 url 就不会多出一个#,变得更加美观。
    但因为没有 # 号,所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求。为了避免出现这种情况,所以这个实现需要服务器的支持,需要把所有路由都重定向到根页面。(即如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。具体可视情况而定)
    具体可以见:
    HTML5 histroy 模式
    从前端路由到hash和history模式_第2张图片
    vue-router源码
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)
  }
}

借鉴:https://github.com/hwen/blogSome/issues/2

hash和history两种模式的区别

1、hash 就是指 url 尾巴后的 # 号以及后面的字符,history没有底带#,外观上比hash 模式好看些;
2、原理的区别(原理)
3、hash 能兼容到IE8, history 只能兼容到 IE10;
4、hash 值变化不会导致浏览器向服务器发出请求,而且 hash 改变会触发 hashchange 事件(hashchange只能改变 # 后面的url片段);虽然hash路径出现在URL中,但是不会出现在HTTP请求中,对后端完全没有影响,因此改变hash值不会重新加载页面。而基于History API的路由,会在页面刷新时向服务器发送请求,会重新加载界面所以需要对服务器做一些改造,需要对不同的路由进行相应的设置才行。
5、history的pushState()设置新的URL可以是任意与当前URL同源的URL,而hash只能改变#后面的内容,因此只能设置与当前URL同文档的URL
6、history的pushState()设置的URL与当前URL一模一样时也会被添加到历史记录栈中,而hash模式中,#后面的内容必须被修改才会被添加到新的记录栈中
7、history的pushState()可以通过stateObject参数添加任意类型的数据到记录中,而hash只能添加短字符串
8、history的pushState()可额外设置title属性供后续使用

本文是个人在了解hash和history模式中所做调查的一些总结以及本人的一些理解,如有错误欢迎指出。

你可能感兴趣的:(vue.js,javascript,html5)