前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >hash和history路由模式

hash和history路由模式

作者头像
y191024
发布2024-01-29 17:06:15
1000
发布2024-01-29 17:06:15
举报
什么是前端路由?

前端路由是指在浏览器端控制页面内容切换显示的机制。在没有服务器端参与的情况下,前端路由可以根据URL的变化,对应展现不同的内容,实现页面的“伪”跳转。

在学习路由之前首先要了解一下SPA单页面应用

SPA( single-page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机制实现 HTML 内容的变换,UI 与用户的交互,避免页面的重新加载。

其实就是说,我们点击页面上的一些东西,并没有真正的发送请求进行页面跳转,而是在组件之间切换而已,仅仅刷新局部资源。

我们熟知的JS框架如react,vue,angular,ember都属于SPA

与之对应的是多页面应用,他们的区别如下

优点:

  • 用户体验好、快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复渲染
  • 基于上面一点,SPA 相对对服务器压力小
  • 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理

缺点

  • 初次加载耗时多:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 JavaScript、CSS 统一加载,部分页面按需加载
  • SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上其有着天然的弱势。

为了实现前端路由,SPA需要监听URL的变化,并据此渲染对应的组件或页面不同部分,无需重新加载整个页面。下面让我们分别深入了解两种路由模式的原理。

hash和history

hash模式原理:

  • 浏览器原生支持通过window.location.hash读写URL中的hash值,并且当hash值变化时,页面不会触发重新加载。
  • SPA可以监听hashchange事件,在URL的hash部分变化时根据定义好的路由映射关系来动态渲染内容。
  • 早期的前端路由的实现就是基于location.hash来实现的,location.hash的值就是URL中#后面的内容 其实现原理就是监听#后面的内容来发起Ajax请求来进行局部更新,而不需要刷新整个页面。
  • 使用hashchange事件来监听 URL 的变化,以下这几种情况改变 URL 都会触发 hashchange 事件:浏览器前进后退改变 URL、<a>标签改变 URL、window.location改变URL。
代码语言:javascript
复制
// Hash模式的简易实现
window.addEventListener('hashchange', routeChange);
function routeChange() {
  const hash = window.location.hash.slice(1); // Remove the '#' symbol
  // 基于hash值显示不同内容
  routerView.innerHTML = routes[hash] ? routes[hash] : routes['404'];
}

我使用了vue中的router.push,发现没有触发hashchange事件, 这是因为hashchange是浏览器的事件,push是vue内部的机制在处理路由变化。

History模式原理

  • History API 允许SPA在浏览历史记录中添加、修改记录而不会触发页面加载。
  • 通过history.pushState和history.replaceState可以改变URL且不重新加载页面。
  • SPA可以监听popstate事件来响应浏览器前进、后退操作。
  • history 提供了 pushState 和 replaceState 两个方法来记录路由状态,这两个方法改变 URL 不会引起页面刷新
  • history 提供类似 hashchange 事件的 popstate 事件,但 popstate 事件有些不同:通过浏览器前进后退改变 URL 时会触发 popstate 事件,通过pushState/replaceState或<a>标签改变 URL 不会触发 popstate 事件。好在我们可以拦截 pushState/replaceState的调用和<a>标签的点击事件来检测 URL 变化,所以监听 URL 变化可以实现,只是没有 hashchange 那么方便。
  • pushState(state, title, url) 和 replaceState(state, title, url)都可以接受三个相同的参数:
  • state:需要保存的数据,这个数据在触发popstate事件时,可以在event.state里获取
  • title:标题,基本没用,一般传 null
  • url:设定新的历史记录的 url,新的 url 与当前 url 的 origin 必须是一样的,否则会抛错,url可以是绝对路径,也可以是相对路径。
代码语言:javascript
复制
// History模式的简易实现
window.addEventListener('popstate', routeChange);
function navigate(path) {
  history.pushState({}, "", path);
  routeChange();
}
function routeChange() {
  const path = window.location.pathname;
  // 根据pathname来渲染不同的页面组件
  routerView.innerHTML = routes[path] ? routes[path] : routes['404'];
}

// navigate('/user'); // 导航至用户页面

关于刷新404的问题

为什么history模式下会出现?

根据nginx的配置,当我们在地址栏输入 http://www.xxx.com 时,这时会打开我们 dist 目录下的 index.html 文件,然后我们再跳转路由进入到 http://www.xxx.com/login

关键在这里,当我们在 http://‍website.com/login 页执行刷新操作,会向真正的服务器发送请求资源,nginx location 是没有相关配置的,所以就会出现 404 的情况

为什么hash模式下不会出现?

router hash 模式我们都知道是用符号#表示的,如 http://website.com/#/login, hash 的值为 #/login

它的特点在于:hash 虽然出现在 URL 中,但不会被包括在内 HTTP 请求中,对服务端完全没有影响,因此改变 hash 不会重新加载页面

hash 模式下,仅 hash 符号之前的内容会被包含在请求中,如 http://website.com/#/login ...只有 http://website.com 会被包含在请求中 ,因此对于服务端来说,即使没有配置location,也不会返回404错误

简单来说: 前端打包后的 dist 包中,只有 index.html。所以,history 模式下发送的请求地址,服务端是找不到的。

  • hash 模式:只将 hash 前面的部分当作地址
  • history 模式:会将地址栏中的地址全部看作请求地址

hash模式的优缺

  • 兼容低版本浏览器,Angular1.x和Vue默认使用的就是hash路由
  • 只有#符号之前的内容才会包含在请求中被发送到后端,也就是说就算后端没有对路由全覆盖,但是不会返回404错误
  • hash值的改变,都会在浏览器的访问历史中增加一个记录,所以可以通过浏览器的回退、前进按钮控制hash的切换
  • 会覆盖锚点定位元素的功能
  • 不太美观,#后面传输的数据复杂的话会出现问题

本文由“壹伴编辑器”提供技术支持

大致到这里就差不多了,又看见一篇写的比较好的文章,可以看一下。

单页应用

当我们在浏览器地址栏输入一个地址时,浏览器就会去服务端去请求内容。但每次点击一个链接,就去服务端请求,这样会有页面加载的等待。

后来慢慢就出现了单页应用,在第一次访问时,就把 html 文件,以及其他静态资源都请求到了客户端。之后的操作,只是利用 js 实现组件的展示和隐藏。除非需要刷新数据,才会利用 ajax 去请求。

但是纯粹的单页应用不方便管理,尤其是开发复杂应用的时候,需要有“多页面”的概念,并且很多用户习惯浏览器的前进后退的导航功能。

能不能有一种方法,可以在不向服务器发送请求的条件下,改变浏览器的 URL,以此来实现“多页面”概念?

答案是有,Vue Router 就是官方开发的一个插件,专门来做这件事。

URL 相关 API

最早改变 URL,但不向服务器发送请求的方式就是 hash。比如这种:

代码语言:javascript
复制
https://music.163.com/#/discover/toplist

同时浏览器也提供了一个事件来监听 hash 的改变,当 URL 的片段标识符更改时,将触发 hashchange 事件 (跟在#符号后面的URL部分,包括#符号)。

代码语言:javascript
复制
window.addEventListener('hashchange', function() {
   
  console.log('The hash has changed!')
}, false);

后来 HTML5 发布,history 对象又增加了两个方法,用来改变浏览器的 URL。只是改变浏览器的访问记录栈,但是不会向服务器发起请求。

代码语言:javascript
复制
history.pushState(state, title[, url])
// 该方法会向浏览器会话的历史堆栈中添加一个状态。
history.replaceState(stateObj, title[, url]) 
//该方法与上一个方法类似,但区别是它会在历史堆栈中替换掉当前的记录。
代码语言:javascript
复制
const state = {
    'page_id': 1, 'user_id': 5 }
const title = ''
const url = 'hello-world.html'

history.pushState(state, title, url)

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2024-01-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 睡不着所以学编程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档