Node.js点击登录按钮提交后无法跳转加载新HTML页面问题
问题原因
你现在用fetch发异步POST请求,哪怕服务端把cardcast.html的内容返回了,这些内容只会存在fetch的响应对象里,浏览器不会自动替换当前页面,自然看不到跳转效果。另外代码里还有几个隐性问题:
- 响应没设置正确的
Content-Type头,浏览器无法正确识别返回内容类型 new_player没有变量声明,会变成污染全局的隐式变量- 用相对路径读前端文件,很容易因为服务端启动目录不对找不到文件
JSON.parse是同步方法,不需要加await
修复方案
1. 调整服务端代码
先引入path模块处理文件路径,补全路由逻辑、响应头,修正语法问题,改完的完整代码如下:
const http = require('http'); const fs = require('fs'); const path = require('path'); console.log('running'); let player_count = 0; let players = []; class player { constructor(id) { this.id = id; this.name = ""; this.card_czar = false; this.cards = new Set(); this.score = 0; this.selection = ""; } setName(nick_name) { this.name = nick_name; } setCardCzar(czar_boolean) { this.card_czar = czar_boolean; } delCard(card_val) { this.cards.delete(card_val); } addCard(card_val) { this.cards.add(card_val); } addScore() { this.score++; } } // 固定前端文件夹的绝对路径,避免相对路径出错 const FRONTEND_PATH = path.resolve(__dirname, '../frontend'); let server = http.createServer(async (req, res) => { console.log(req.url); if (req.url === '/') { res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); res.write(fs.readFileSync(path.join(FRONTEND_PATH, 'test.html'))); res.end(); } else if (req.url === '/login' && req.method === 'POST') { const buffers = []; for await (const chunk of req) { buffers.push(chunk); } const data = JSON.parse(Buffer.concat(buffers).toString()); let new_player = new player(player_count); new_player.setName(data.nickname); players.push(new_player); player_count++; console.log(players); // 登录成功返回200状态即可 res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' }); res.end(JSON.stringify({ code: 0, msg: '登录成功' })); } // 新增游戏页面路由 else if (req.url === '/game') { res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); res.write(fs.readFileSync(path.join(FRONTEND_PATH, 'cardcast.html'))); res.end(); } else { res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' }); res.end('页面不存在'); } }); server.listen(3000);
2. 调整前端登录逻辑
修改登录页的login函数,在fetch请求提交昵称成功后,手动跳转到游戏页路由即可,同时修正之前拼接请求URL的问题(直接写绝对路径/login比拼接window.location.href稳妥,避免带参数时路径错误):
<script type="text/javascript"> async function login() { let nickname = document.getElementById('username-field').value.trim(); if (!nickname) { alert('请输入昵称'); return; } console.log(nickname); // 提交昵称到登录接口 await fetch('/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({nickname}) }); // 提交成功后跳转到游戏页 window.location.href = '/game'; } </script>
如果你不想单独加/game路由,也可以在fetch拿到/login返回的HTML内容后直接替换当前页面,不过这种方式后续加载游戏页的CSS、JS等静态资源容易出路径问题,不推荐使用,写法参考:
let res = await fetch('/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({nickname}) }); let html = await res.text(); document.open(); document.write(html); document.close();
额外提醒
- 目前用
fs.readFileSync读文件是同步阻塞操作,小范围测试没问题,并发高了会阻塞服务进程,后续可以换成fs/promises模块的异步读文件方法 - 后续
cardcast.html如果引用了独立的CSS、JS、图片等资源,记得在服务端加对应的静态资源处理逻辑,不然这些资源会加载失败 - 现在玩家数据全存在服务端内存里,服务端重启后所有数据都会清空,需要长期保存的话可以后续接入文件存储或者数据库
内容的提问来源于stack exchange,提问作者godpleasehelp
相关产品推荐
相关产品推荐

