H5 页面跳转与刷新

H5 业务里跳转、刷新、回退看起来都是改地址,真正要分清的是:会不会进历史栈、会不会走 bfcache、页面脚本会不会重新执行。

Location 对象是浏览器用来操作 URL 的接口,window.locationdocument.location 均指向该对象;History 对象负责管理浏览器会话历史记录。

页面跳转

a 标签跳转

最基础的 HTML 跳转方式。

<!-- 当前页面跳转 -->
<a href="https://example.com/path/index.html">页面跳转</a>

<!-- 新标签页打开 -->
<a href="https://example.com/path/index.html" target="_blank">新窗口跳转</a>

阻止跳转的几种写法对比:

写法 说明 副作用
href="#" 空片段,多数浏览器会滚到页面顶部 改变 hash,可能触发滚动
href="###" 指向不存在的锚点,一般不会滚到顶部 会修改地址栏 hash;搭配 target="_blank" 会打开空白新页面
href="javascript:;" 执行空 JS 语句,不做跳转 不修改 URL,业务里较常见
href="javascript:void(0);" void 计算表达式,返回 undefined,阻止跳转 不修改 URL,兼容性好
移除 href 属性 a 标签变为普通文本标签 失去 a 标签原生样式,需要手动 CSS 还原

最佳实践:阻止跳转优先使用 href="javascript:;",或者直接移除 href、改用 click 事件。javascript: 伪协议对无障碍和内容安全策略不友好,能改成 <button> 时更合适。

location 系列跳转

location.href 可读可写,赋值新 URL 即可完成页面跳转,会向 history 新增一条历史记录。

// 当前窗口跳转
location.href = "https://example.com";
window.location.href = "https://example.com";
self.location.href = "https://example.com";

// iframe嵌套场景:父窗口页面跳转
parent.location.href = "https://example.com";

// iframe嵌套场景:顶层最外层页面跳转(跳出iframe)
top.location.href = "https://example.com";

备注self 代表当前窗口;parent 直接父级窗口;top 最顶层窗口;多层 iframe 嵌套时三者行为差异明显。如需要跳出 iframe,务必使用 top.location.href

meta 标签自动跳转

HTML 元标签,实现页面加载后延迟自动跳转,不需要 JS。

<!-- 5秒后自动跳转到目标地址 -->
<meta http-equiv="refresh" content="5; url=https://www.example.com">

location.assign(url)

加载指定 URL 资源,会新增历史记录,用户可以回退回到上一页。行为等价于直接赋值 location.href

location.assign("https://example.com");

location.replace(url)

使用新 URL 替换当前完整页面 + 当前 history 历史记录,页面会整体重载,不会新增 history 记录。跳转后浏览器回退按钮无法回到上一页。

location.replace("https://example.com");

window.open()

打开新窗口、新标签页。

和 location 系列区别:location 操作当前窗口window.open 会开启新浏览上下文。

// 打开新标签页
window.open("https://example.com");

// 在指定name的iframe窗口内加载页面
window.open("/demo.html","iframeName");

注意:浏览器弹窗拦截策略,如果不是用户手动点击触发,window.open() 会被浏览器拦截。

页面刷新

meta 标签定时自动刷新

<!-- 每5秒自动刷新页面 -->
<meta http-equiv="refresh" content="5">

history.go(0)

读取会话历史,参数为 0 代表刷新当前页。

备注:是否向服务器重新请求,由该页的 HTTP 缓存策略决定,并不保证跳过缓存。

history.go(0);

location.reload()

模拟浏览器刷新按钮,重载当前页面。

location.reload();

注意:该方法在跨域调用(执行该方法的脚本文件的域和 Location 对象所在页面的域不同)时,将会抛出 SECURITY_ERROR DOMException 异常。

注意:标准用法不传参数。Firefox 曾支持传入 true 强制走服务器请求,这不属于现行标准,Chrome、Safari 会忽略该参数,业务不要传参。

页面回退

history.back()、history.go(-1)

回到浏览器上一条历史记录,读取页面 bfcache 缓存,页面不会重新请求资源,和浏览器左上角返回按钮行为完全一致。

history.back();
history.go(-1);

注意:Safari 部分场景下单纯调用 history.go(-1) 会失效,可以配合业务逻辑做兼容,优先使用 history.back()

document.referrer

返回来源页面完整 URI,即用户是从哪个页面跳转过来。

// 直接跳转到来源页,会新增历史记录
location.href = document.referrer;

注意:如果是直接打开地址、本地文件、跨域特殊策略,document.referrer 可能为空字符串,使用前建议做空值判断。

页面替换(不新增历史记录)

业务场景:跳转之后,不把当前页面留在 history 栈,用户回退不会再次回到本页。

location.replace(url) 直接替换页面,整个页面重载:

location.replace(url)

history.replaceState() 仅修改浏览器地址栏 URL,不会刷新页面,不会发起网络请求,属于 SPA 路由核心 API:

history.replaceState(stateObj, title, url);

参数说明:

  • stateObj:状态对象,popstate 事件可以拿到该对象;
  • title:绝大多数浏览器忽略该参数;
  • url:必须和当前页面同源,否则抛出安全异常。

回退并刷新页面

业务场景举例:A 页面进入 B 页面,B 页面做数据修改;用户回退回到 A,A 必须刷新拿到最新数据。

问题根源:移动端浏览器 bfcache 往返缓存,回退页面直接读取内存缓存,JS 不重新执行,接口不会重新请求。

pageshow 事件(推荐,H5 通用)

pageshow 在页面显示时触发;persisted 属性为 true,代表页面来自 bfcache 缓存。

window.addEventListener('pageshow', function (e) {
  // 从bfcache缓存恢复,执行刷新
  if (e.persisted) {
    location.reload();
  }
})

另一种标记变量写法,pagehide 页面隐藏时打标记:

let pageHiddenFlag = false;
window.addEventListener('pagehide', () => {
  pageHiddenFlag = true;
})
window.addEventListener('pageshow', () => {
  if(pageHiddenFlag) {
    location.reload();
  }
})

visibilitychange 事件

监听页面可见性变化。当标签页从后台切回前台、从隐藏变为可见时触发。

注意:该事件不区分是「回退回来」还是「切 Tab 回来」;适合做兜底,不能单独作为回退刷新唯一方案

document.addEventListener('visibilitychange', function () {
  // 页面变为可见状态
  if (!document.hidden) {
    location.reload();
  }
})

注意:即使页面 DOM 刷新成功,接口还可能读取 HTTP 缓存;请求接口可以追加时间戳避免缓存:/api/list?_t=${Date.now()}

location.replace(document.referrer)

直接替换为来源页面,会重载页面,但是如果 referrer 为空会出错,需要判空。

if(document.referrer){
  location.replace(document.referrer);
}

popstate 监听 + replaceState

适合 SPA 单页应用场景,监听浏览器回退 popstate 事件,回退触发时手动刷新页面。

history.replaceState({flag:1}, "", location.href);
window.addEventListener('popstate',function(){
  location.reload();
})

常见疑难问题

页面回退不刷新

现象:A→B,回退A,A页面数据还是旧的。

原因:

  • 浏览器 bfcache 往返缓存,页面从内存直接恢复,脚本不重新执行;
  • Ajax 接口读取 HTTP 缓存;
  • App 内嵌 WebView 新开窗口,history 栈互相独立。

解决:优先使用 pageshow 事件做刷新,接口增加时间戳破除 HTTP 缓存。

history.back() 和 history.go(-1) 有什么区别?

相同点:效果都是回退到上一条历史记录,大多数浏览器表现一致。

差异:

  • 语义定义history.back() 是无参数方法,语义明确,代表后退一步history.go(delta) 是通用跳转方法,支持正负数字,可以前进、后退多步,go(-1)只是传入参数实现后退一步。
  • Safari、WebView 兼容性差异:部分旧版 iOS Safari、部分 APP WebView 中,history.go(-1) 偶现不生效;而 history.back() 兼容性更稳定。

最佳实践:只需要回退上一页优先使用 history.back();需要跨多步跳转才使用 history.go(n)

window.location 与 document.location

  • 无 iframe 嵌套时:window.location === document.location,完全等价。
  • iframe 嵌套时:document.location 代表当前 iframe 文档的 URL;top.location 代表最外层窗口。

禁止浏览器页面回退

拦截鼠标回退按钮、手势、键盘回退,持续把历史记录锁在当前页面。

// 压入一条当前页面历史
history.pushState(null, null, document.URL);
window.addEventListener('popstate', function () {
  // 用户回退,再压入记录,停留在本页
  history.pushState(null, null, document.URL);
})

注意:只能阻止 history 栈内回退,无法阻止用户关闭标签页。

跳转改的是地址,刷新改的是文档生命周期。选 API 之前先确认要不要留下历史记录、回退时要不要重新拉数据。

参考资料

MDN Location

MDN History

MDN pageshow_事件

MDN visibilitychange_事件

© lizhao all right reserved,powered by Gitbook文件修订时间: 2026-08-31 20:20:41

results matching ""

    No results matching ""