1. 问题来了:360浏览器那个烦人的“确认重新提交表单”警告
不知道你有没有遇到过这种情况:辛辛苦苦在登录页面填好了用户名和密码,点击登录按钮,页面跳转了,一切看起来都很顺利。但就在这个时候,你习惯性地按了一下浏览器的刷新键,或者不小心点了后退又前进,一个刺眼的弹窗突然跳出来——“确认重新提交表单 您所查找的网页要使用已输入的信息。返回此页可能需要重复已进行的所有操作。是否要继续操作?”
这个弹窗,在360浏览器里尤其常见。我第一次在项目里遇到时,测试同事跑过来问我:“用户登录后刷新页面,怎么还要他确认是否重新提交?这体验太差了,用户会以为登录失败了。” 我一开始还以为是后端重定向逻辑有问题,排查了半天才发现,问题出在前端,更具体地说,是出在浏览器对表单提交后页面刷新的特殊处理机制上。
简单来说,当你通过一个表单(比如POST方式的登录表单)提交数据并跳转到新页面后,这个新页面的“历史记录”状态是和表单提交行为绑定在一起的。如果你在这个新页面刷新,浏览器会认为你想“重复上一次的提交动作”。对于Chrome、Firefox等现代浏览器,它们可能只是静默地重新发起请求(有时会导致数据重复提交),但360浏览器选择弹出一个明确的警告框来询问用户。这个设计的初衷可能是为了防止误操作导致数据重复提交,比如重复下单、重复评论。但在登录这个场景下,用户只是想刷新一下页面看看自己的登录状态,这个警告就显得多此一举,甚至会引起困惑。
所以,我们的目标很明确:在用户成功登录后,我们要“抹掉”历史记录里那个“表单提交”的印记,让浏览器认为当前页面是一个普通的、可以通过GET请求安全刷新的页面。这样,即使用户刷新,也不会再看到那个烦人的警告。而实现这个目标的关键,就是history.pushState这个方法。它允许我们在不真正跳转页面的前提下,动态地修改浏览器地址栏的URL,从而“替换”掉历史堆栈里的当前记录。
2. 核心武器:history.pushState 是什么?为什么是它?
要理解怎么解决问题,我们得先搞清楚手里的工具。history.pushState 是 HTML5 引入的 History API 的一部分。我更喜欢把它想象成一本可以随意涂改的“浏览器历史记录本”。
通常,当我们点击一个链接(<a>标签)或者用 window.location.href 进行跳转时,浏览器会做两件事:第一,向新地址发起请求,加载新页面;第二,在历史记录本里新写一页,记录下这个新地址。这个过程是“有刷新”的。
而 history.pushState 的强大之处在于,它只做第二件事,不做第一件。它允许我们直接在历史记录本里,不动声色地替换掉当前这一页的地址标签,而浏览器不会因此去加载一个新页面。这就像是你把历史书里当前页的页码和标题悄悄改掉了,但书页里的内容纹丝不动。
它的基本语法是这样的:
history.pushState(stateObject, title, url);
- stateObject: 一个JavaScript对象,可以关联一些状态信息到这条新的历史记录上。我们可以通过
history.state来获取它。在解决我们这个警告的场景里,这个参数通常用不上,传null就行。 - title: 理论上,这是给新历史记录设置的标题。但请注意,目前几乎所有浏览器都忽略了这个参数,为了安全起见,我们传空字符串
""。 - url: 这是关键!你想要设置的新地址栏URL。它必须是同源的(即协议、域名、端口一致)。你可以设置一个完整的路径,也可以只是一个哈希值(比如
#home),或者像我们例子中一样,简单地改成另一个路径名,比如"index"。
那么,为什么 window.location.href 或 window.location.replace 不行呢?这正是我踩过的坑。原始文章里的注释也提到了://bug,会二次刷新。当你使用 location.href = "index" 时,浏览器会立即向 index 这个地址发起一次新的导航

3849

被折叠的 条评论
为什么被折叠?



