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

相关文章

Rust中的注释使用解读

《Rust中的注释使用解读》本文介绍了Rust中的行注释、块注释和文档注释的使用方法,通过示例展示了如何在实际代码中应用这些注释,以提高代码的可读性和可维护性... 目录Rust 中的注释使用指南1. 行注释示例:行注释2. 块注释示例:块注释3. 文档注释示例:文档注释4. 综合示例总结Rust 中的注释

Linux使用cut进行文本提取的操作方法

《Linux使用cut进行文本提取的操作方法》Linux中的cut命令是一个命令行实用程序,用于从文件或标准输入中提取文本行的部分,本文给大家介绍了Linux使用cut进行文本提取的操作方法,文中有详... 目录简介基础语法常用选项范围选择示例用法-f:字段选择-d:分隔符-c:字符选择-b:字节选择--c

使用Go语言开发一个命令行文件管理工具

《使用Go语言开发一个命令行文件管理工具》这篇文章主要为大家详细介绍了如何使用Go语言开发一款命令行文件管理工具,支持批量重命名,删除,创建,移动文件,需要的小伙伴可以了解下... 目录一、工具功能一览二、核心代码解析1. 主程序结构2. 批量重命名3. 批量删除4. 创建文件/目录5. 批量移动三、如何安

springboot的调度服务与异步服务使用详解

《springboot的调度服务与异步服务使用详解》本文主要介绍了Java的ScheduledExecutorService接口和SpringBoot中如何使用调度线程池,包括核心参数、创建方式、自定... 目录1.调度服务1.1.JDK之ScheduledExecutorService1.2.spring

Java使用Tesseract-OCR实战教程

《Java使用Tesseract-OCR实战教程》本文介绍了如何在Java中使用Tesseract-OCR进行文本提取,包括Tesseract-OCR的安装、中文训练库的配置、依赖库的引入以及具体的代... 目录Java使用Tesseract-OCRTesseract-OCR安装配置中文训练库引入依赖代码实

Python使用Pandas对比两列数据取最大值的五种方法

《Python使用Pandas对比两列数据取最大值的五种方法》本文主要介绍使用Pandas对比两列数据取最大值的五种方法,包括使用max方法、apply方法结合lambda函数、函数、clip方法、w... 目录引言一、使用max方法二、使用apply方法结合lambda函数三、使用np.maximum函数

Qt 中集成mqtt协议的使用方法

《Qt中集成mqtt协议的使用方法》文章介绍了如何在工程中引入qmqtt库,并通过声明一个单例类来暴露订阅到的主题数据,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录一,引入qmqtt 库二,使用一,引入qmqtt 库我是将整个头文件/源文件都添加到了工程中进行编译,这样 跨平台

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Python使用国内镜像加速pip安装的方法讲解

《Python使用国内镜像加速pip安装的方法讲解》在Python开发中,pip是一个非常重要的工具,用于安装和管理Python的第三方库,然而,在国内使用pip安装依赖时,往往会因为网络问题而导致速... 目录一、pip 工具简介1. 什么是 pip?2. 什么是 -i 参数?二、国内镜像源的选择三、如何