什么是路由

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

http://hometown.xxx.edu.cn/bbs/forum.php

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

其响应过程是这样的

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

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

前端路由的诞生的缘由

前端路由的出现要从 ajax 开始,为什么?且听下面分析 (ˉ▽ ̄~)

Ajax,全称 Asynchronous JavaScript And 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(那么问个问题,异步交互最高级的体验是什么?会在文末揭晓)—— 单页应用。单页应用不仅仅是在页面交互是无刷新的,连页面跳转都是无刷新的,为了实现单页应用,所以就有了前端路由。

单页应用的概念是伴随着 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 } = thisthis.transitionTo(location, route => {// 使用 pushState 更新 url,不会导致浏览器发送请求,从而不会刷新页面pushState(cleanPath(this.base + route.fullPath))onComplete && onComplete(route)}, onAbort)}replace (location, onComplete, onAbort) {const { current: fromRoute } = thisthis.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路由与history路由 Orz相关推荐

  1. Vue 的路由实现 Hash模式 和 History模式

    Hash 模式: Hash 模式的工作原理是onhashchange事件,Window对象可以监听这个事件... 可以通过改变路径的哈希值,来实现历史记录的保存,发生变化的hash 都会被浏览器给保存 ...

  2. 单页面应用(SPA)前端路由hash 模式 VS history 模式

    文章目录 单页面应用(SPA) 前端路由的由来 前端路由 hash 模式 history 模式 hash.history优缺点 单页面应用(SPA) 简单的说 SPA 就是一个WEB项目只有一个 HT ...

  3. 【前端路由】Vue-router 中hash模式和history模式的区别 a标签跳转

    [前端路由]Vue-router 中hash模式和history模式的区别 咱们今天说说VUE路由的hash模式与history模式的区别,这个也是面试常问的问题,不要小看这道题其实问到这里的时候那个 ...

  4. 【前端路由】Vue-router 中hash模式和history模式的区别

    咱们今天说说VUE路由的hash模式与history模式的区别,这个也是面试常问的问题,不要小看这道题其实问到这里的时候那个面试官应该是个大牛,开发经验丰富,这个题其实就是考验你的开发经验是否属实. ...

  5. 浅谈前端路由原理hash和history

    浅谈前端路由原理hash和history

  6. history 路由 vs hash 路由 vs location.href 实现跳转

    location.href 直接页面跳转了 => 重新请求页面了 -> 刷新 history => 改变 url 但是页面不刷新 ,没有重新请求后端的页面,用到了 history a ...

  7. hash路由模式与history路由模式

    1. hash路由模式 hash模式 :使用 URL 的 hash 来模拟一个完整的 URL, 其显示的网络路径中会有 "#" 号 hash 虽然出现URL中,但不会被包含在HTT ...

  8. Vue(小码哥王洪元)笔记06路由,url的hash,history,router-linke,路由跳转,动态路由,懒加载,路由嵌套,router参数传递,导航守卫

    1.什么是路由 路由器提供了两种机制:路由和传送 路由:数据报从来源到目的地的路径 传输:将输入端的数据转移到合适的输出端 路由有一个非常重要的概念教路由表 路由表本质上就是一个映射表,决定了数据包的 ...

  9. VueRouter — vue路由hash模式和history模式

    目录 一.前言 二.hash模式 三.history模式 一.前言 对于hash模式和history模式,最直接的区别就是地址栏带不带"#"号了. vue脚手架搭建的项目的路由默认 ...

最新文章

  1. jittor和pytorch生成网络对比之cyclegan
  2. ipa 打包遇到的坑
  3. fpga电平约束有什么作用_FPGA时序约束有什么样的作用
  4. 多项式的ln、exp、快速幂和开根学习小记
  5. 28 | 读写分离有哪些坑?
  6. 支撑百万级并发,Netty如何实现高性能内存管理
  7. LeetCode:Balanced Binary Tree
  8. HTML-CSS-JS Prettify报错Node.js was not found 解决方法
  9. 数据仓库项目管理面试题整理(十二)
  10. mobile兼容性调整,根据rem,字体大小,视窗宽度
  11. Linux下Tomcat性能优化--文件句柄数增大
  12. 电商十四、pinyougou02.sql的内容⑤
  13. JSONP实现跨域的原理
  14. ipa文件生成扫码安装二维码的方法
  15. C# DLL资源文件打包(图片、JS、CSS)[WebResource]
  16. RFID正为零售防损带来新变化
  17. c语言双精度小数点后取几位_c语言float类型小数点后位数
  18. 一文看懂川土微电子隔离器核心技术
  19. App Inventor 2连接模拟器一直失败的问题解决
  20. 调试Kubernetes集群中的网络停顿问题

热门文章

  1. prefetch 和preload_使用 Preload/Prefetch 优化你的应用
  2. linux smartctl 命令,Linux smartctl命令
  3. 开售16天销量破百万,为何小米8还是那么让人失望?
  4. mysql循环insert_mysql 循环insert
  5. 上市公司股吧帖子爬取
  6. burpsuite的使用--Intruder模块
  7. linux下复制文件
  8. 学习使用IntelliJ IDEA的第二天:进行IDEA的简单配置
  9. SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropri
  10. C++编程 牛吃草问题