我正在尝试使用 Node.js 和 Express 在 SPA 上实现访客计数器。访客计数存储在文本文件 (count.txt) 中,我有一个 API 端点 (/api/visitor-count),每次访问时都会检索并递增计数。但是,我注意到一个问题,即访问者计数在单个页面加载时会多次增加,即使我只从客户端触发单个提取请求。由于某种原因,当检查面板打开或页面处于移动视图时,计数器会完美计数,但在全屏或大屏幕上,会出现多次重新加载。然后突然间,它完全停止工作,直到我重新启动我的电脑,然后它再次工作,但我遇到了同样的问题。
fetch('http://localhost:3000/api/visitor-count')
.then(response => response.json())
.then(data => {
console.log('Received response:', data);
document.getElementById('count').textContent = data.count;
})
.catch(error => {
console.error('Error fetching visitor count:', error);
});
const express = require('express');
const fs = require('fs');
const path = require('path');
const cors = require('cors'); // I Imported cors here
const app = express();
const PORT = 3000;
// Enable CORS for all routes
app.use(cors());
// Directory to where the count.txt is stored
const countFilePath = path.join(__dirname, 'count.txt');
// Helper functions
function readCount() {
try {
const count = fs.readFileSync(countFilePath, 'utf8');
return parseInt(count, 10) || 0;
} catch (error) {
return 0;
}
}
function writeCount(count) {
fs.writeFileSync(countFilePath, count.toString(), 'utf8');
}
// API endpoint
app.get('/api/visitor-count', (req, res) => {
let count = readCount();
count +=1;
writeCount(count);
res.json({ count });
});
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
<div id="visitor-count">Visitors: <span id="count">Loading...</span></div>
我通过在 fetch 调用中添加 console.log() 语句来跟踪其执行情况,确认 fetch 请求仅被调用一次。 我尝试过在开发人员工具打开和关闭的情况下进行测试,并且行为有所不同(开发人员工具关闭时会发生多次增量)。 我使用了浏览器开发工具中的“网络”选项卡来确保页面加载时仅发送一个请求。 我需要帮助理解为什么尽管只发出一个提取请求,但页面重新加载时访问者计数会多次增加。
编辑。 我刚刚注意到,由于某种原因,该页面在宽屏幕上重新加载多次
肯定缺少信息,你使用框架吗?某些框架在开发模式下双重运行页面作为边缘情况测试。
但是一个好的解决方案仍然是处理此服务器端,因为用户可以简单地刷新多次。
const express = require('express');
const fs = require('fs');
const path = require('path');
const cors = require('cors'); // I Imported cors here
const app = express();
const PORT = 3000;
// Enable CORS for all routes
app.use(cors());
// Directory to where the count.txt is stored
const countFilePath = path.join(__dirname, 'count.txt');
// Helper functions
async function readCount() {
try {
const count = await new Promise(resolve => fs.readFile(countFilePath, 'utf8', (err, data) => {
if (err) return resolve(0);
resolve(data);
});
return parseInt(count, 10) || 0;
} catch (error) {
console.log(error);
return 0;
}
}
async function addCount() {
const count = await readCount() + 1;
await new Promise(resolve => fs.writeFile(countFilePath, count.toString(), 'utf8', resolve);
return count + 1;
}
const visitMap = {};
const timeWindow = 5*60*1000; // Five minutes
async function accountVisitor(ip) {
if (visitMap[ip] && Date.now() - visitMap[ip] > timeWindow) {
delete visitMap[ip];
return addCount();
}
if (!visitMap[ip]) {
visitMap[ip] = Date.now();
return addCount();
}
return await readCount()
}
function cleanMap() {
for (ip in visitMap) {
if (Date.now() - visitMap[ip] > timeWindow) {
delete visitMap[ip];
}
}
}
// API endpoint
app.get('/api/visitor-count', (req, res) => {
const count = await accountVisitor();
res.json({ count });
});
// clean visit map every 5 minutes.
setInterval(cleanMap(), 5*60*1000);
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
});
这样,无论是前端导致问题,还是某些用户垃圾刷新,问题都有望得到解决。
还有;在服务器上,请不要执行同步功能。