History API 使用指北

2023-12-18 20:18
文章标签 使用 api history 指北

本文主要是介绍History API 使用指北,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

https://yeaseonzhang.github.io/2017/03/03/History-API-%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8C%97/#more

HTML5 History API,基本上是为了SPA(单页应用)而生。

History API能够在不刷新页面的情况下,通过和url匹配历史堆栈中的数据取出来,这样就能大大减少数据请求,提高用户体验。

其实说实话,能不能提高用户体验,我不知道;在历史记录中切换(前进、后退)真的是如丝般顺滑。

History API 简介

在浏览器环境下,我们可以通过window.history访问我们的浏览器访问历史。
console.png
可能说的有点问题,你只能得到三个值length,scrollRestorationstate。其中只有state的值是我们需要的,接下来会提到。

back()

back()方法相当于点击浏览器的后退按钮。

     
1
     
window.history.back();
forward()

forward()方法相当于点击浏览器的前进按钮。

     
1
     
window.history.forward();
go(n)

go(n)方法允许你在历史session前进或者后退n次。

     
1
2
3
4
5
     
// Go back two entries.
window.history.go(-2);
// Go forward 3 entries.
window.history.go(3);
length

就是我们上文在浏览器得到的length属性。

下面就是我们的重头戏,HTML5 History API,上面的属性方法只是简单的热身,你可能不需要热身。

HTML5 History API

HTML5 History API包含两个方法和一个事件。

  • pushState()
  • replaceState()
  • popstate

在详细介绍之前,我们先来看看各个浏览器对于HTML5 History API支持情况。

caniuse.com

caniuse.com

可以说现在主流的浏览器对于HTML5 History API支持都是很好的,不过也要考虑向下兼容。

     
1
2
3
4
5
     
if (!!history.pushState) {
// support
} else {
// dont support
}
pushState()
     
1
     
pushState(state, title, url)
  • state: 传递给history.state
  • title: 似乎浏览器还没有很好支持,传null就好
  • url: 可选,这个参数会改变你的浏览器url

这个方法是在浏览器堆栈历史中push一条新的数据,然后将指针指向这条数据。

replaceState()
     
1
     
replaceState(state, title, url)

replace()方法与pushState()方法类似,主要是能够替换更新pushState()state数据。

参数描述详见pushState()的描述。

popstate事件

当用户点击浏览器的前进/后退按钮,popstate事件会被调用。

     
1
2
3
4
5
6
7
8
     
window.addEventListener('popstate', function (event) {
// update the page content
});
// or
window.onpopstate = function (event) {
// update the page content
}

以上就把History API简略介绍了一下,详细说明请访问MDN。

手把手实战

需求分析

网上也不乏介绍H5 History API应用的文章,大概也都是比较简单的Demo。

http://html5demos.com/history

http://html5demos.com/history

点击链接first、second、third..,url会改变为http://html5demos.com/history#fitst,http://html5demos.com/history#second

获取不同#first,#second的数据,渲染页面中的数据。具体实现源码。

本文,我想介绍的不是这种demo,而是比较实际的项目开发。

实现一个文件系统的浏览页面,通过url哈希值#...记录文件夹的路径。

例如,www.demo.com/filesystem.html#abc/就是根目录下abc文件夹;www.demo.com/filesystem.html#abc/def/代表abc文件夹下的def文件夹。

file system

file system

如上图,该目录下有文件和文件夹,点击文件会直接在浏览器打开该文件或者下载,点击文件夹会进入该文件夹,页面显示该文件夹的内容。

这样就需要我们把urlhash值动态改变,然后根据hash值确定所在文件路径向后台获取资源。

前后端接口API
显示目录

请求:

     
1
     
GET /api/v1/fileproxy/pub/abc/ HTTP/1.1

响应:

     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
     
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"error": 0,
"errormsg": "success",
"data": [
{
"name": "a.txt",
"size": 1234,
"time": 1235153531,
"isdir": false,
},
{
"name": "a.txt",
"size": 1234,
"time": 1235153531,
"isdir": false,
}
]
}

打开/下载文件

请求:

     
1
     
GET /api/v1/fileproxy/pub/abc/a.txt HTTP/1.1

从后端获取到的文件信息包括name文件(夹)名,size大小,time修改时间,isdir是否是文件夹。

流程
  • initGetfile()
  • openDir(str)
  • window.onpopstate

initGetfile()函数,首次进入或者刷新页面自动执行该函数,渲染文件列表。

openDir(str)函数,当点击文件夹时调用该函数,获取新的文件列表。

window.onpopstate也就是popstate事件。

在没有使用History API时,是通过hashchange事件来触发文件列表的更新,这就导致每一次前进后退都会发起一次ajax请求,没有把之前请求过的数据进行缓存,影响用户体验。

对了在此要声明一下,psuhState()replaceState()不会触发hashchange事件

怎么理解呢,就是当你通过psuhState()replaceState()的第三个参数url对于hash值有更改的时候,也不会触发hashchange事件。

Chrome 和 Safari浏览器在重载页面的时候会触发popstate事件,Firefox浏览器不会。

重头戏

下面将会贴上我的代码,使用Vue2 和 axios实现。

HTML
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
     
<div class="file-list">
<div class="weui-cells">
<a v-if="fileList.length" v-for="item in fileList" href="javascript:;" class="weui-cell weui-cell_access">
<!-- Dir -->
<div v-if="item.isdir">
<div class="weui-cell__hd">
<span class="icon-size an-folder"></span>
</div>
<div class="weui-cell__bd" @click="openDir(item)">
<p>{{ item.name }}</p>
</div>
</div>
<!-- File -->
<div v-else>
<div v-else class="weui-cell__hd">
<span class="icon-size an-file"></span>
</div>
<div class="weui-cell__bd" @click="openFile(item)">
<p>{{ item.name }}</p>
</div>
</div>
</a>
<a v-if="!fileList.length" class="weui-cell weui-cell_access" href="javascript:;">
<div class="weui-cell__bd">
<p>No such file or directory</p>
</div>
</a>
</div>
</div>

将文件夹和文件分成两类进行渲染,绑定不同的点击函数,并传递参数为文件(夹)名,前面的前后端API也提示我们是通过name进行请求。

JS
     
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
     
(function (exports) {
exports.app = new Vue({
el: '.file-list',
data: {
haveFile: true,
fileList: [],
},
mounted: fucntion () { // 挂载之后,自动执行
this.$nextTick(function () {
this.initGetfile();
})
},
methods: {
initGetfile: function () {
var _this = this;
var hash = _this.getHash();
var url = '/api/v1/fileproxy/' + hash;
axios.get(url)
.then(function (res){
_this.fileList = res.data.data;
// replaceState()
history.replaceState(_this.fileList, null);
if (!res.data.data.length) {
_this.haveFile = false;
} else {
_this.haveFile = true;
}
})
},
openDir: function (item) {
var _this = this;
_this.loadFile = true;
var hash = _this.getHash();
var url = '/api/v1/fileproxy/-/' + hash + item.name +'/';
axios.get(url)
.then(function (res) {
_this.fileList = res.data.data;
_this.loadFile = false;
var newUrl = window.location.href + item.name + '/';
// pushState()
history.pushState(_this.fileList,null, newUrl);
if (!res.data.data.length) {
_this.haveFile = false;
} else {
_this.haveFile = true;
}
});
},
openFile: function (item) {
var _this = this;
var hash = _this.getHash();
var url = '/api/v1/fileproxy/-/' + hash + item.name;
window.open(url);
},
getHash: function () {
var hash = window.location.hash.substr(1);
if (hash.substr(-1) != '/') {
hash += '/';
}
return hash;
},
}
});
window.onpopstate = function (e) {
console.log('pop state');
console.log('state', JSON.stringify(e.state));
if (e.state != null) {
app.fileList = e.state;
} else {
window.location.reload();
}
}
})(window);

以上就是我完成基本功能的源码,添加History API到项目中并没有做太多改动:

  1. 增加pushState()repalceState()函数

  2. 替换原有的hashchange事件为popstate事件。

结语

本没有对源码做过多的分析,因为可能需求不一样采取的处理方法也不一样,我只是提供一个#abc/def...这种hash值比较复杂需求的一种实现。

希望能够对你有所启发,也是现学现卖,如有不妥之处,望指正。

这篇关于History API 使用指北的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

使用Python删除Excel中的行列和单元格示例详解

《使用Python删除Excel中的行列和单元格示例详解》在处理Excel数据时,删除不需要的行、列或单元格是一项常见且必要的操作,本文将使用Python脚本实现对Excel表格的高效自动化处理,感兴... 目录开发环境准备使用 python 删除 Excphpel 表格中的行删除特定行删除空白行删除含指定

深入理解Go语言中二维切片的使用

《深入理解Go语言中二维切片的使用》本文深入讲解了Go语言中二维切片的概念与应用,用于表示矩阵、表格等二维数据结构,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录引言二维切片的基本概念定义创建二维切片二维切片的操作访问元素修改元素遍历二维切片二维切片的动态调整追加行动态

prometheus如何使用pushgateway监控网路丢包

《prometheus如何使用pushgateway监控网路丢包》:本文主要介绍prometheus如何使用pushgateway监控网路丢包问题,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录监控网路丢包脚本数据图表总结监控网路丢包脚本[root@gtcq-gt-monitor-prome

Python通用唯一标识符模块uuid使用案例详解

《Python通用唯一标识符模块uuid使用案例详解》Pythonuuid模块用于生成128位全局唯一标识符,支持UUID1-5版本,适用于分布式系统、数据库主键等场景,需注意隐私、碰撞概率及存储优... 目录简介核心功能1. UUID版本2. UUID属性3. 命名空间使用场景1. 生成唯一标识符2. 数

SpringBoot中如何使用Assert进行断言校验

《SpringBoot中如何使用Assert进行断言校验》Java提供了内置的assert机制,而Spring框架也提供了更强大的Assert工具类来帮助开发者进行参数校验和状态检查,下... 目录前言一、Java 原生assert简介1.1 使用方式1.2 示例代码1.3 优缺点分析二、Spring Fr

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

java使用protobuf-maven-plugin的插件编译proto文件详解

《java使用protobuf-maven-plugin的插件编译proto文件详解》:本文主要介绍java使用protobuf-maven-plugin的插件编译proto文件,具有很好的参考价... 目录protobuf文件作为数据传输和存储的协议主要介绍在Java使用maven编译proto文件的插件

SpringBoot线程池配置使用示例详解

《SpringBoot线程池配置使用示例详解》SpringBoot集成@Async注解,支持线程池参数配置(核心数、队列容量、拒绝策略等)及生命周期管理,结合监控与任务装饰器,提升异步处理效率与系统... 目录一、核心特性二、添加依赖三、参数详解四、配置线程池五、应用实践代码说明拒绝策略(Rejected

C++ Log4cpp跨平台日志库的使用小结

《C++Log4cpp跨平台日志库的使用小结》Log4cpp是c++类库,本文详细介绍了C++日志库log4cpp的使用方法,及设置日志输出格式和优先级,具有一定的参考价值,感兴趣的可以了解一下... 目录一、介绍1. log4cpp的日志方式2.设置日志输出的格式3. 设置日志的输出优先级二、Window

Ubuntu如何分配​​未使用的空间

《Ubuntu如何分配​​未使用的空间》Ubuntu磁盘空间不足,实际未分配空间8.2G因LVM卷组名称格式差异(双破折号误写)导致无法扩展,确认正确卷组名后,使用lvextend和resize2fs... 目录1:原因2:操作3:报错5:解决问题:确认卷组名称​6:再次操作7:验证扩展是否成功8:问题已解