HTML5 history API,创造更好的浏览体验

2024-08-30 09:08

本文主要是介绍HTML5 history API,创造更好的浏览体验,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

来源URL:http://segmentfault.com/a/1190000002447556


HTML5 history API有什么用呢?

从Ajax翻页的问题说起

请想象你正在看一个视频下面的评论,在翻到十几页的时候,你发现一个写得稍长,但非常有趣的评论。正当你想要停下滚轮细看的时候,手残按到了F5。然后,页面刷新了,评论又回到了第一页,所以你又要重新翻一次。

再或者,你想把这个评论发给别人分享,一面给了别人页面地址(为什么不直接复制呢?因为要连带视频等场景啊),一面又要加一句嘱咐:请翻到下面评论的第XX页的XX楼。

这就是问题。试想一下,如果浏览器能记住你当前的状态(比如看到了第十几页),而不是一刷新就还原,是不是就显得智能多了?

为什么用Ajax?

用Ajax实现翻页等内容切换是有原因的。在传统的无Ajax的站点里,页面A和页面B可能只有10%的地方是不同的,其他90%的内容(尤其是导航、页脚等公用元素)都是一样的,但却仍然需要浏览器下载并显示新的一整个页面。而如果使用Ajax,不仅节省了浏览器需要下载的资源,而且无刷新切换明显比页面跳转更平滑、流畅。

就视频下面的评论来说,Ajax可以说是必须的。视频这样的重量级元素,动不动给你重新加载一次,不能忍。

传统的跳转翻页的可取之处

传统的不使用Ajax的站点,每一个翻页是一个跳转,然后你可以在浏览器地址栏里看到诸如?page=2这样的参数。每一页就这样通过地址栏的URL做了标记,每一次请求,浏览器都会根据参数返回正确的页码。

所以,传统的跳转翻页,刷新也不会丢失状态。

结合两者

现在我们就可以想到,如果在Ajax更新页面局部内容的同时,也在地址栏的URL里更新状态参数,就可以做出更完美的Ajax翻页了。

不过,JavaScript修改location的除hash外的任意属性,页面都会以新URL重新加载。而唯一不引发刷新的hash参数并不会发送到服务器,因此服务器无法获得状态。

然后,HTML5 history API将解决这个问题。

介绍HTML5 history API

HTML5 history API只包括2个方法:history.pushState()history.replaceState(),以及1个事件:window.onpopstate

history.pushState()

它的完全体是 history.pushState(stateObject, title, url),包括三个参数。

第1个参数是状态对象,它可以理解为一个拿来存储自定义数据的元素。它和同时作为参数的url会关联在一起。

第2个参数是标题,是一个字符串,目前各类浏览器都会忽略它(以后才有可能启用,用作页面标题),所以设置成什么都没关系。目前建议设置为空字符串。

第3个参数是URL地址,一般会是简单的?page=2这样的参数风格的相对路径,它会自动以当前URL为基准。需要注意的是,本参数URL需要和当前页面URL同源,否则会抛出错误。

调用pushState()方法将新生成一条历史记录,方便用浏览器的“后退”和“前进”来导航(“后退”可是相当常用的按钮)。另外,从URL的同源策略可以看出,HTML5 history API的出发点是很明确的,就是让无跳转的单站点也可以将它的各个状态保存为浏览器的多条历史记录。当通过历史记录重新加载站点时,站点可以直接加载到对应的状态。

history.replaceState()

它和history.pushState()方法基本相同,区别只有一点,history.replaceState()不会新生成历史记录,而是将当前历史记录替换掉

window.onpopstate

push的对立就是pop,可以猜到这个事件是在浏览器取出历史记录并加载时触发的。但实际上,它的条件是比较苛刻的,几乎只有点击浏览器的“前进”、“后退”这些导航按钮,或者是由JavaScript调用的history.back()等导航方法,且切换前后的两条历史记录都属于同一个网页文档,才会触发本事件。

上面的“同一个网页文档”请理解为JavaScript环境的document是同一个,而不是指基础URL(去掉各类参数的)相同。也就是说,只要有重新加载发生(无论是跳转到一个新站点还是继续在本站点),JavaScript全局环境发生了变化,popstate事件都不会触发。

popstate事件是设计出来和前面的2个方法搭配使用的。一般只有在通过前面2个方法设置了同一站点的多条历史记录,并在其之间导航(前进或后退)时,才会触发这个事件。同时,前面2个方法所设置的状态对象(第1个参数),也会在这个时候通过事件的event.state返还回来。

此外请注意,history.pushState()history.replaceState()本身调用时是不触发popstate事件的。pop和push毕竟不一样!

如何应用

HTML5 history API的内容不多,具体如何应用它来改进Ajax翻页呢?

首先,在服务器端添加对URL状态参数的支持,例如?page=3将会输出对应页码的内容(后端模板)。也可以是服务器端把对应页码的数据给JavaScript,由JavaScript向页面写入内容(前端模板)。

接下来,使用history.pushState(),在任一次翻页的同时,也设置正确的带参数的URL。代码可能是这样:

newURL = "?page=" + pageNow;
history.pushState(null, "", newURL);

到此,就解决了F5刷新状态还原的事了。

不过,还没有结束,在浏览器中点击后退,例如从?page=3退到?page=2,会发现没有变化。按道理说,这时候也应该对应变化。这就要用到popstate事件了。

window添加popstate事件,加入这种导航变化时的处理。代码可能是这样(jQuery):

$(window).on("popstate", function(event) {// 取得之前通过pushState保存的state object,尽管本示例并不打算使用它。// jQuery对event做了一层包装,需要通过originalEvent取得原生event。var state = event.originalEvent.state,// 本示例直接取URL参数来处理reg = /page=(\d+)/,regMatch = reg.exec(location.search),// 第1页的时候既可以是 ?page=1,也可以根本没有page参数pageNow = regMatch === null ? 1 : +regMatch[1];updateByPage(pageNow);
});

这样,就完成了。这样看起来是否会觉得还挺容易的呢?在支持HTML5 history API的浏览器中,以上部分就已经做到了带页码记录的Ajax翻页。

有待斟酌的兼容性问题

根据caniuse上的数据,IE10+及其他主流浏览器都支持HTML5 history API。为保证不支持的浏览器不报错,可以加入是否支持HTML5 history API的判断:

// 参考自 http://modernizr.com/download/#-history 源码
var isHistoryApi = !!(window.history && history.pushState);// ...if(isHistoryApi){// ...
}

这样,一个Ajax翻页,在支持HTML5 history API的浏览器上,将会智能地保存当前页码信息,而不支持的浏览器仍然可以正常使用,只是不保存页码信息(就像改进前那样)。我认为,按照“渐进增强”的思路,这样就是最好的了,也就是:只使用较少的代码优化高级浏览器的使用体验。

如果真的想要在各类浏览器里都表现一致,拥有这样的记录功能呢?

这时候推荐使用Benjamin Lupton的History.js,它提供和HTML5 history API近似的api,会在不支持的浏览器里回退到hash形式去处理历史记录。尽管为了兼容这种hash的回退形式你可能要额外做点事(hash不会发送到服务器端),但它确实可以让你做到更广范围的兼容。

HTML5 history API并不完美

即使只考虑支持HTML5 history API的浏览器,它们对HTML5 history API的一些细节处理也会有差异和问题。History.js提供的只针对HTML5浏览器的版本,仍然包含了不少处理兼容问题的代码。

但是,不完美也没有关系。以我的测试结果,本文所介绍的简单的写法,就可以在绝大部分支持HTML5 history API的浏览器上正常运行。如果你担心有哪些浏览器会有潜在问题,去测试那个浏览器就可以了。你最后用于兼容处理的自写代码很可能远比一个JavaScript库少得多,毕竟,你也不一定会喜欢额外引入一个JavaScript库来完成一个功能吧。

一些相关内容

地址栏里的hash曾是过去被广泛用来记录页面状态的标记,你可以阅读W3C Blog的这篇文章了解它的经历。

现在可以在不刷新的状况下操作浏览器地址栏和历史记录了,那同一站点的普通链接跳转是否都可以转变为Ajax来提升使用体验?是的,而且已经有了pjax、turbolinks这些专门完成这个功能的作品。

不只是翻页,HTML5 history API将尤其适合用在大量使用Ajax、包含多个视图的单页应用。

为一个页面的每一个状态都生成一条历史记录不一定合适(会让用户的历史记录变多变乱),酌情使用replaceState()而不是pushState()来控制历史记录的数量。

结语

HTML5 history API简单易学,不多的几行代码就可以做到“状态记录”这个小小的改进,如果可以由你选择“渐进增强”,它还真的可以上线!

(重新编辑自我的博客,原文地址:http://acgtofe.com/posts/2014/12/play-with-browser-history/)


这篇关于HTML5 history API,创造更好的浏览体验的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1120387

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

Vue3项目开发——新闻发布管理系统(六)

文章目录 八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染 4、退出功能实现①注册点击事件②添加退出功能③数据清理 5、代码下载 八、首页设计开发 登录成功后,系统就进入了首页。接下来,也就进行首页的开发了。 1、页面设计 系统页面主要分为三部分,左侧为系统的菜单栏,右侧

【VUE】跨域问题的概念,以及解决方法。

目录 1.跨域概念 2.解决方法 2.1 配置网络请求代理 2.2 使用@CrossOrigin 注解 2.3 通过配置文件实现跨域 2.4 添加 CorsWebFilter 来解决跨域问题 1.跨域概念 跨域问题是由于浏览器实施了同源策略,该策略要求请求的域名、协议和端口必须与提供资源的服务相同。如果不相同,则需要服务器显式地允许这种跨域请求。一般在springbo

HTML提交表单给python

python 代码 from flask import Flask, request, render_template, redirect, url_forapp = Flask(__name__)@app.route('/')def form():# 渲染表单页面return render_template('./index.html')@app.route('/submit_form',

Java 后端接口入参 - 联合前端VUE 使用AES完成入参出参加密解密

加密效果: 解密后的数据就是正常数据: 后端:使用的是spring-cloud框架,在gateway模块进行操作 <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30.0-jre</version></dependency> 编写一个AES加密

vue2 组件通信

props + emits props:用于接收父组件传递给子组件的数据。可以定义期望从父组件接收的数据结构和类型。‘子组件不可更改该数据’emits:用于定义组件可以向父组件发出的事件。这允许父组件监听子组件的事件并作出响应。(比如数据更新) props检查属性 属性名类型描述默认值typeFunction指定 prop 应该是什么类型,如 String, Number, Boolean,