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

相关文章

Spring Boot整合Redis注解实现增删改查功能(Redis注解使用)

《SpringBoot整合Redis注解实现增删改查功能(Redis注解使用)》文章介绍了如何使用SpringBoot整合Redis注解实现增删改查功能,包括配置、实体类、Repository、Se... 目录配置Redis连接定义实体类创建Repository接口增删改查操作示例插入数据查询数据删除数据更

使用python生成固定格式序号的方法详解

《使用python生成固定格式序号的方法详解》这篇文章主要为大家详细介绍了如何使用python生成固定格式序号,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考一下... 目录生成结果验证完整生成代码扩展说明1. 保存到文本文件2. 转换为jsON格式3. 处理特殊序号格式(如带圈数字)4

Java使用Swing生成一个最大公约数计算器

《Java使用Swing生成一个最大公约数计算器》这篇文章主要为大家详细介绍了Java使用Swing生成一个最大公约数计算器的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下... 目录第一步:利用欧几里得算法计算最大公约数欧几里得算法的证明情形 1:b=0情形 2:b>0完成相关代码第二步:加

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Linux jq命令的使用解读

《Linuxjq命令的使用解读》jq是一个强大的命令行工具,用于处理JSON数据,它可以用来查看、过滤、修改、格式化JSON数据,通过使用各种选项和过滤器,可以实现复杂的JSON处理任务... 目录一. 简介二. 选项2.1.2.2-c2.3-r2.4-R三. 字段提取3.1 普通字段3.2 数组字段四.

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

k8s按需创建PV和使用PVC详解

《k8s按需创建PV和使用PVC详解》Kubernetes中,PV和PVC用于管理持久存储,StorageClass实现动态PV分配,PVC声明存储需求并绑定PV,通过kubectl验证状态,注意回收... 目录1.按需创建 PV(使用 StorageClass)创建 StorageClass2.创建 PV