gmfe / Think

观麦前端团队的官方博客
68 stars 3 forks source link

浏览器历史堆栈管理 #56

Open Gesangs opened 5 years ago

Gesangs commented 5 years ago

前言

先说背景,在商品列表中,每一个商品都有详情入口,跳转到新的页面后,列表页会被销毁。

再说这样一个场景,这个列表很长,浏览到列表中部时,进入某个商品详情页,再返回时,列表页就刷新了,列表回到了顶部。这样体验很不好...... 然后需求就很明确了,如何在从详情页回到列表页之后,回到原始浏览位置,甚至列表页保持原样。

这里参考对话框浮层的做法,用一个覆盖全屏的浮层,利用History的历史堆栈,使其拥有页面的基本功能(前进、后退)。

History API

借用栈和指针的概念介绍一下History的接口,文档中并未说明Histroy是个堆栈,这里只是借用一下

pushState

history.pushState(stateObject, title, url) 包括三个参数。

  1. stateObject用于存储该url对应的状态对象,该对象可在onpopstate事件中获取,也可通过history.state获取。

  2. title是标题,目前浏览器并未实现。

  3. url则是设定的url。一般设置为相对路径,如果设置为绝对路径时需要保证同源。若不传则默认为当前url。

history.pushState()向栈中压入一个指定url的记录,然后将指针指向这个记录,并截去这个记录之后的所有记录,使之成为栈顶(如下图);无副作用的将当前url更改为传入的url(修改hash值也不会触发hashchange事件) image

replaceState

与pushState参数相同,含义也相同。唯一的区别在于replaceState是仅仅是将当前指针指向的记录更改为传入url。如上图

back、forward和go

这三个方法作用仅在于,移动指针并触发popstate事件,并不会改变histroy的长度(histroy.length)。

onpopstate

在不改变document的前提下,一旦当前指针改变(包括浏览器的前进后退)就会触发onpopstate事件。并在指针修改之后才触发。监听这个事件会传入一个对象,包含指针指向的记录信息。

Talk is cheap, Show me the code

请看这里👉code

详情页不仅要从某个页面的商品点击进入,更要通过url作为独立的页面展示,所以详情页有两种存在形式:

  1. 依附于某个页面,作为浮层的形式存在
  2. 作为一个独立的页面存在(当然这里页面还是SPA中的页面的意思,由router控制)

首先看这段代码 image 做了3件事:

  1. 向历史记录堆栈中压入一条记录
  2. 监听popstate事件
  3. 唤起详情页(浮层形式)

这里存在一个问题,当唤起页面之后,刷新或复制链接发送给人,该如何唤起浮层呢。前文提到pushState可无副作用的修改url,我们只需将url修改成和独立页面的详情页的url一样即可。这样刷新还是复制链接都将跳转到路由层的详情页,从而免去唤起浮层。

再看这段: image 重点在于这个判断条件,上文提到popstate事件在指针移动之后触发,并传入一个对象,包含了指针指向的记录的信息。若在浮层页之后有其他不是我们预期的行为移动了指针,触发popstate事件,就会把浮层页关闭了。所以一定这里要确保指针移动后不是指向浮层页的记录,再关闭浮层。

以上。

参考: History API与浏览器历史堆栈管理