现象
由于 IOS 系统的页面缓存机制,经常会遇到在移动端返回到上一个页面不刷新的情况。
比如今天在开发微信 H5 页面的时候,在IOS微信内置浏览器中返回上一页时,上一个页面不会被刷新。 而通常在浏览器缓存机制中,在返回上一页的操作中, html/css/js/接口 等动静态资源不会重新请求,但是js会重新加载。但在IOS微信页面中js也会保存上一页面最后执行的状态,不会重新执行js。 使用这种模式的缓存机制可以加快渲染速度,但是部分数据需要经常展示和编辑的情况下会导致不同步。比如‘详情页’跳转到‘编辑页’,编辑完后再返回到‘详情页’,如果‘详情页’数据展示未进行同步修改那肯定是不能接受的。 在webview和5+的混合app模式中,也会遇到这种返回上一个页面不刷新的问题
产生原因
浏览器前进/后退缓存
这里提到一个概念,浏览器前进/后退缓存(Backward/Forward Cache, BF Cache),当然也有人叫 disk Cache。 BF Cache 是一种浏览器优化, HTML 标准并未指定其如何进行缓存,因此缓存行为是各浏览器各自实现,所以不尽相同。 由于不是 HTTP 缓存,所以通过头文件缓存设置 no-cache 是无效的。当然也不能以 HTTP 缓存机制来理解 BF Cache。
解决思路
设置浏览历史当前记录
//监听后退返回事件 --解决微信返回不刷新问题
pushHistory: function(){
window.addEventListener("popstate", function(e) {
self.location.reload();
}, false);
var state = {
title : "",
url : "#"
};
window.history.replaceState(state, "", "#");
},
/** * 页面初始化调用pushHistory,监听popstate事件和执行replaceState() * 当执行replaceState()时,不会触发popstate事件,所以不会重复刷新 * 当在ios微信内置浏览器中执行浏览器前进后退操作时,触发popstate事件,执行location.reload() * 但是在谷歌浏览器中执行浏览器前进后退操作时,不会触发popstate事件!因为不在一个document中了 * 但是如果手动改变URL的哈希值,比如www.baidu.com# 改成 www.baidu.com#1 会触发popstate事件,执行location.reload() * 以上如果有一项不理解或不清晰,请往下看原理深究,你会找到答案 **/
原理深究
前端路由实现(history)原理
以前浏览器操作浏览器历史记录主要依据history对象。在它的 proto 继承有 back、forward、go 等函数。 而 HTML5 后新增 popState 来控制浏览历史记录的 api。有可以存储当前历史记录点的 pushState、替换当前历史记录点的 replaceState、和监听历史记录点的 onPopState。
window.history.pushState(state, title, url)
- state (状态对象): 一个js对象,可以用来存储一些简单的数据。值得一提的是如果被激活的历史记录条目是通过 pushState 或 replaceState 调用而生成的,popState事件的state属性包含历史条目的状态对象的副本。可以从 history.state中读取存储的 state。
- title (标题): 目前被浏览器忽略了,所以通常传入一个空字符串
- URL (地址): 新的历史记录条目的地址。值得一提的是调用 pushState() 后浏览器并不会立即加载这个 URL,但可能会在用户重新打开浏览器时等某些情况加载这个 URL。并且新 URL 必须与当前 URL 同源,否则会抛出一个异常。 新 URL 可为绝对路径或者是相对路径。
window.history.replaceState(state, title, url)
- 相同之处是两个API都会操作浏览器的历史记录,而不会引起页面的刷新,也不会去验证这个新条目对应的网页是否存在。
- 不同之处在于 pushState 会增加一条新的历史记录,而 replaceState 则会替换当前的历史记录。
- 两个 API 都绝对不会触发 hashchange 事件,即使新的 URL 与旧的 URL 仅哈希不同也是如此
- 如果传递了 stateObj,就会更新当前条目关联的状态对象;如果传递了 url,就会替换当前条目的页面地址和更改浏览器地址栏的地址。
window.onpopstate事件
- 仅在浏览器前进后退操作、history.go/back/forward 调用、hashChange 的时候触发
- history.pushState 和 history.replaceState 都不会触发这个事件
- firfox,chrome 在页面首次打开时都不会触发 popstate 事件,但是 safari 会
- popstate 事件作用范围仅在于一个 document 里面,由于 pushState 和 hashchange 都不会改变网页的内容也就是 document,所以这样的网页里面才能有效使用 popstate。假如我们输入一个网页,并且在它里面添加了 popstate 回调;然后通过链接跳转的方式转到另外一个网页;再点击后退按钮回到第一个网页。这样的情况,第一个网页里面的 popstate 回调,除了有可能因为页面初始化被触发外,浏览器的后退前进是不会触发它的,因为这种方式改变了窗口的 document。但是!!!在 iOS 微信内置浏览器上,重复上述的操作,会触发 onpopstate 事件!,所以我们才能解决在微信浏览器上页面返回不刷新的问题
pushState 和replaceState 的第一个参数 stateObj,会与第三个参数对应的历史条目绑定在一块,当 popstate 事件触发的时候,意味着有新的历史记录条目被激活,在 popstate 的事件对象里面,有一个 state 属性,会返回这个激活条目关联的 stateObj 对象的拷贝。一个历史记录条目只有当它是被 pushState 创建的,或者用 replaceState 改过的,才可能有关联的 stateObj 对象,所以当某些非这2种条件的历史记录条目被激活的时候,可能拿到的 stateObj 就是 null。
禁止返回上一页的一种方案
/** * 向历史记录中手动添加一条记录 * 用户选择返回的时候,每次都会消耗一个 history 实体,此时触发 popstate 监听事件,再手动添加一条history实体记录 * 所以用户无论点击多少次都会永远留在这个页面了,当然页面也不会刷新 **/
function pushHistory() {
window.history.pushState(null, null, "#");
window.addEventListener("popstate", function (e) {
console.log(e);
window.history.pushState(null, null, "#");
}, false);
}
URL中的#
URL 中的 # 就表示的是 URL 的哈希值
- #代表网页中的一个位置,其右边的字符,就是该位置的标识符。
设置方法:
step1:设置一个锚点<a href="#print">定位到print位置</a>
step2:在页面需要定位的内容加上id="print"。例如:<div id="print"></div> 或者 <a name="print"></a>
测试:step1设置的锚点,step2中id为print的内容会滚动到页面顶端(可观察滚动条的距离)。同时,页面的url末端中会出现 # print的哈希值。
-
HTTP请求不包含#
#号是用来指导浏览器动作的,对服务器端完全无用。
在第一个#后面出现的任何字符,都会被浏览器解读为位置标识符。这意味着,这些字符都不会被发送到服务器端。
访问下面的网址: www.w3cschool.cn/#hello 浏览器实际发出的请求时这样的: -
改变#不触发网页重载
单单改变#后的内容,浏览器只会滚动到相应位置,不会重新加载网页。
浏览器也不会重新向服务器请求页面 -
改变#会改变浏览器的访问历史
每一次改变#后的部分,都会在浏览器的访问历史中增加一个记录,使用”后退”按钮,就可以回到上一个位置。 -
window.location.hash读取#值
window.location.hash这个属性可读可写。读取时,可以用来判断网页状态是否改变;写入时,则会在不重载网页的前提下,创造一条访问历史记录。 -
onhashchange事件
这是一个HTML 5新增的事件,当#值发生变化时,就会触发这个事件。IE8+、Firefox 3.6+、Chrome 5+、Safari 4.0+支持该事件。// 它的使用方法有三种: window.onhashchange = func; <body onhashchange="func();"> window.addEventListener("hashchange", func, false);
-
Google抓取#的机制
默认情况下,Google的网络蜘蛛忽视URL的#部分。
但是,Google还规定,如果你希望Ajax生成的内容被浏览引擎读取,那么URL中可以使用”#!”,Google会自动将其后面的内容转成查询字符串_escaped_fragment_的值。
比如,Google发现新版twitter的URL:twitter.com/#!/username
就会自动抓取另一个URL:twitter.com/?escaped_fragment=/username
通过这种机制,Google就可以索引动态的Ajax内容。
另一种解决办法:pageShow事件
window.addEventListener('pageshow', () => {
if (e.persisted || (window.performance &&
window.performance.navigation.type == 2)) {
location.reload()
}
}, false)
- 为了查看页面是直接从服务器上载入还是从缓存中读取,可以使用 PageTransitionEvent 对象的 persisted 属性来判断。如果页面从浏览器的缓存中读取该属性返回 ture,否则返回 false。
- window.performance对象,performance.navigation.type是一个无符号短整型
TYPE_NAVIGATE (0):
当前页面是通过点击链接,书签和表单提交,或者脚本操作,或者在url中直接输入地址,type值为0
TYPE_RELOAD (1)
点击刷新页面按钮或者通过Location.reload()方法显示的页面,type值为1
TYPE_BACK_FORWARD (2)
页面通过历史记录和前进后退访问时。type值为2
TYPE_RESERVED (255)
任何其他方式,type值为255 这真是我们需要的部分
今天的文章解决浏览器返回页面不刷新的问题分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/13703.html