页面重新加载是一个普遍现象。 有时,当我们认为页面没有响应或认为有新内容可用时,我们会刷新页面。 有时我们只是对该站点感到愤怒,并愤怒地刷新以让它知道我们不满意。
了解用户何时刷新页面不是很好吗? 不仅如此,还有多少次? 这些数据可以帮助我们在特定次数的重新加载后触发某种行为。
体育网站就是一个很好的例子。 如果我想查看正在进行的比赛的比分,但比分没有实时更新,那么我可能会发现自己刷新了很多次。
我们的目标是让用户改掉这种习惯。 我们将利用我们的页面刷新计数功能,让用户知道由于实时比分更新,刷新是不必要的。 而且,如果他们刷新超过三次? 我们将把他们踢出他们的会话。 这会告诉他们。
这是一个简单演示该概念的演示。
让我们一起重新创建它。 但是,在我们开始之前,我们需要在开始编码之前回答几个问题
- 我们如何持久保存用户重新加载站点的次数? 我们需要一个地方来保存用户重新加载站点的次数(
reloadCount
),这个地方需要在重新加载之间持久保存该值——localStorage
听起来像是一个不错的解决方案。 - 我们如何检测用户是重新加载了站点还是在几个小时后才回来? 如果我们将
reloadCount
存储在localStorage
中,它将在重新加载之间持久保存该值,但它将保留该值,直到我们以编程方式删除或清除浏览器存储。 这意味着,如果我们几个小时后回来,该站点仍将记住上一次的reloadCount
,并且可能会在没有警告的情况下在第一次刷新后执行注销。 我们想避免这种情况,并允许用户在每次用户在一段时间后回来时重新加载站点两次。 最后一句话包含了这个问题的答案。 我们需要存储用户离开站点的時間,然后在站点再次加载时检查发生的时间。 如果这段时间不够长,我们就激活重新加载计数逻辑。 - 我们如何知道用户何时离开站点? 为了存储那个时间,我们使用
beforeunload
窗口事件并将该值存储在localStorage
中。
好的,现在我们有了答案,让我们深入代码。
步骤 1:我们必须存储上次重新加载的时间
我们将使用beforeunload
窗口事件存储上次重新加载的时间。 我们需要两件事:(1)一个事件侦听器,它将侦听事件并触发相应的 method,以及(2)我们的beforeUnloadHandler
method。
首先,让我们创建一个名为initializeReloadCount
的函数,该函数将使用窗口对象上的addEventListener
method 设置我们的事件侦听器。
function initializeReloadCount() {
window.addEventListener("beforeunload", beforeUnloadHandler)
}
然后,我们创建一个将在我们离开站点之前触发的第二个 method。 该 method 将保存刷新发生的时间在localStorage
中。
function beforeUnloadHandler() {
localStorage.setItem("lastUnloadAt", Math.floor(Date.now() / 1000))
window.removeEventListener("beforeunload", beforeUnloadHandler);
}
步骤 2:我们需要一种方法来处理和存储重新加载计数
现在我们有了上次站点关闭的时间,我们可以继续并实现负责检测和计数站点重新加载次数的逻辑。 我们需要一个变量来保存我们的reloadCount
,并告诉我们用户重新加载站点的次数。
let reloadCount = null
然后,在我们的initializeReloadCount
函数中,我们需要做两件事
- 检查我们是否已经在我们的
localStorage
中存储了reloadCount
值,如果有,获取该值并将其保存在我们的reloadCount
中。 如果该值不存在,则意味着用户是第一次加载站点(或至少没有重新加载它)。 在这种情况下,我们将reloadCount
设置为零,并将该值保存到localStorage
。 - 检测站点是否被重新加载或用户在较长时间后返回站点。 这是我们需要使用我们的
lastUnloadAt
值的地方。 为了检测站点是否确实被重新加载,我们需要将站点加载的时间(当前时间)与lastUnloadAt
值进行比较。 如果这两个事件在例如五秒钟内发生(这完全是任意的),则表示用户重新加载了站点,我们应该运行重新加载计数逻辑。 如果这两个事件之间的时间间隔更长,我们重置reloadCount
值。
有了这些,让我们创建一个名为checkReload
的新函数,并将该逻辑保留在那里。
function checkReload() {
if (localStorage.getItem("reloadCount")) {
reloadCount = parseInt(localStorage.getItem("reloadCount"))
} else {
reloadCount = 0
localStorage.setItem("reloadCount", reloadCount)
}
if (
Math.floor(Date.now() / 1000) - localStorage.getItem("lastUnloadAt") <
5
) {
onReloadDetected()
} else {
reloadCount = 0;
localStorage.setItem("reloadCount", reloadCount)
}
}
在这一步中,我们需要创建的最后一个函数是一个负责当我们确认用户重新加载了站点时发生的事情的 method。 我们将该函数称为onReloadDetected
,在其中,我们递增reloadCount
的值。 如果用户第三次刷新站点,我们将按下炸弹并调用我们的logout
逻辑。
function onReloadDetected() {
reloadCount = reloadCount + 1
localStorage.setItem("reloadCount", reloadCount)
if (reloadCount === 3) {
logout()
}
}
步骤 3:“亲爱的用户,你为什么不听?!”
在这一步中,我们实现了负责用户重新加载站点的逻辑,尽管我们明确警告他们停止这样做,但他们还是重新加载了站点,直到超过了我们的三次限制阈值。
当这种情况发生时,我们调用我们的 API 注销用户,然后清理与重新加载计数逻辑相关的所有属性。 这将允许用户回来并获得重新加载的干净记录。 我们还可以将用户重定向到有用的地方,例如登录屏幕。(但将他们发送到这里不是很有趣吗?)
function logout(params) {
// logout API call
resetReloadCount()
}
function resetReloadCount() {
window.removeEventListener("beforeunload", beforeUnloadHandler)
localStorage.removeItem("lastUnloadAt")
localStorage.removeItem("reloadCount");
}
奖励:让我们重新 Vue 它!
现在我们已经实现了逻辑,让我们看看如何将该逻辑移动到基于此示例的 Vue 站点
首先,我们需要将我们所有的变量移动到我们组件的data
中,所有反应式道具都位于此。
export default {
data() {
return {
reloadCount: 0,
warningMessages: [...]
}
},
然后,我们将所有函数移动到methods
。
// ...
methods: {
beforeUnloadHandler() {...},
checkReload() {...},
logout() {...},
onReloadDetected() {...},
resetReloadCount() {...},
initializeReloadCount() {...}
}
// ...
由于我们使用的是 Vue 及其反应式系统,我们可以删除所有直接的 DOM 操作(例如document.getElementById("app").innerHTML
),并依赖于我们的warningMessages
数据属性。 为了显示正确的警告消息,我们需要添加一个计算属性,该属性将在每次我们的reloadCount
发生变化时重新计算,以便我们可以从我们的warningMessages
返回一个字符串。
computed: {
warningMessage() {
return this.warningMessages[this.reloadCount];
}
},
然后,我们可以直接在组件的模板中访问我们的计算属性。
<template>
<div id="app">
<p>{{ warningMessage }}</p>
</div>
</template>
我们需要做的最后一件事是找到一个适当的位置来激活重新加载阻止逻辑。 Vue 提供了组件生命周期钩子,这些钩子正是我们需要的,特别是created
钩子。 让我们把它放进去。
// ...
created() {
this.initializeReloadCount();
},
// ...
很好。
总结
这就是它,检查并计算页面刷新次数的逻辑。 希望您喜欢这段旅程,并且您发现此解决方案有用或至少鼓舞人心,可以做得更好。🙂
不错的指南。
似乎您可以通过使用 sessionStorage 而不是使用 localStorage 来模拟它,从而省去很多麻烦。
我认为 sessionStorage 不会起作用,因为每次用户重新加载都会生成一个新的会话。 如果我错了请纠正我。
sessionStorage 在页面重新加载后仍然存在。 来自 MDN 文档
那重新加载的问题是什么?难道你的额外代码,服务于每个人,抵消了大部分甚至全部的节省吗?
同意。我不确定为什么我会关心用户刷新两次或 200 次。
强制重新加载的问题是,它们可能会一次又一次地在后端触发一些“执行”。
因此,建议在重新加载的情况下,考虑对用户和后端做出一些有意义的响应。
当然,这一切都取决于应用程序和架构。
我认为这不是打破习惯的方式。用户重新加载是有原因的。如果他开始频繁重新加载,那就意味着他觉得 UI 没有更新,或者没有响应他的操作。重新加载是对大多数与 js 相关的 bug 的简单修复,这些 bug 会导致页面处于损坏状态。恕我直言,我们应该表明页面是有响应的(不,不是在浏览器屏幕尺寸的背景下,而是其他的响应)。向他展示,你正在检查东西,即使这本身并没有改变应用程序状态。
当然,如果重新加载对你来说是一个问题,你应该已经在寻找可以减少重新加载时间和服务器端成本的助手——查询性能、快速的缓存层,或者甚至横向扩展,如果你的服务器已经接近超载,即使是连续几次重新加载也会影响它。
最后,告诉用户“你不允许重新加载页面”并不会改变他的习惯。你甚至可能会惹恼他。正如我所说,他这样做是有原因的,在大多数情况下,他会觉得这样做是合理的。如果我看到这样的警告,特别是在我经历了应用程序崩溃、连接问题或 UI 没有更新之后,我宁愿愤怒地退出并找到一个正常工作的程序。
我认为,唯一能证明这种机制合理的是 DOS 和 DDOS 防护,但这些防护已经内置在 Cloudflare 等软件中。
这可能只是为了演示目的,但因为用户没有“表现得体”而将他们踢出去,这可能永远不是正确的解决方案。更不用说,这可能是有歧视性的,因为技术水平较低的人更容易受到影响。
这里的理想解决方案似乎是一个用户界面和体验,鼓励用户不要隐式地重新加载。这可能意味着有一个“上次更新时间为下午 1:22”的刷新指示器。
我完全同意。这对用户来说极其不友好,虽然它作为一个不错的技术示例,但用其他东西替换会更好;也许是关于 WebSockets 的教程来更新页面并保持其活动状态?捕获 Ctrl+R 来重新加载内容而无需刷新整个页面?展示如何迎接回头客,并向他们展示自上次会话以来的新内容?
对我来说,使用 Vue.js 来做这件事似乎有点过分。
这是一个代码笔,其中包含上述代码,它包含在一个简单的 HTML 中,并进行了一些细微的调整。
此致
“如果他们重新加载超过三次?我们会把他们踢出他们的会话。这样可以教训他们。”
我希望这真的是个玩笑?拜托。