Express.js/Node.js/EJS同名功能脚本的两种实现差异解析
关于Express+EJS实现姓名字母统计的两种方案对比疑问
任务背景
我需要完成以下课程练习任务:
- 创建包含h1标题和姓名输入表单的index.ejs文件
- 在index.js中接收表单POST请求的姓名参数
- 统计全名字母的总数
- 根据统计结果修改h1的显示内容
我用Express.js、Node.js和EJS完成了功能,代码可正常运行,还额外添加了请求日志中间件,但我的实现和老师的版本存在差异,想搞清楚老师处理app.get()和app.post()请求响应的原因,同时确认我的实现是否更便捷直观。
我的实现代码
index.ejs
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Name Letters</title> </head> <body> <% if (!countLetters) { %> <h1>Enter your name here!</h1> <% } else { %> <h1>There are <%= countLetters %> letters in your name.</h1> <% } %> <form action="/submit" method="POST" autocomplete="off"> <input type="text" name="fName" placeholder="First name"> <input type="text" name="lName" placeholder="Last name"> <input type="submit" value="OK"> </form> </body> </html>
index.js
import express from "express"; const app = express(); const port = 3000; app.use(express.urlencoded({ extended: true })); app.use(logger); let countLetters = 0; app.get("/", (req, res) => { res.render("index.ejs", {countLetters: countLetters}); }); app.post("/submit", (req, res) => { const firstName = req.body.fName; const lastName = req.body.lName; countLetters = firstName.length + lastName.length; res.redirect("/"); }); app.listen(port, () => { console.log(`Server running on port ${port}`); }); function logger(req, res, next) { console.log("Request: " + req.method + " " + req.url); next(); }
老师的实现代码
index.ejs
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Name Letters</title> </head> <body> <% if (locals.letterNumber) { %> <h1>There are <%= letterNumber %> letters in your name.</h1> <% } else { %> <h1>Enter your name below 👇</h1> <% } %> <form action="/submit" method="POST"> <input type="text" name="fName" placeholder="First name"> <input type="text" name="lName" placeholder="Last name"> <input type="submit" value="OK"> </form> </body> </html>
index.js
import express from "express"; import bodyParser from "body-parser"; const app = express(); const port = 3000; app.use(bodyParser.urlencoded({ extended: true })); app.get("/", (req, res) => { res.render("index.ejs"); }); app.post("/submit", (req, res) => { const numLetters = req.body["fName"].length + req.body["lName"].length; res.render("index.ejs", { numberOfLetters: numLetters }); }); app.listen(port, () => { console.log(`Server running on port ${port}`); });
两种实现的思路解析
老师的实现逻辑
app.get('/')处理:直接渲染index.ejs,不传递任何数据。EJS模板通过locals.letterNumber判断该变量是否存在,以此切换h1的显示内容。这种方式初始状态下无需额外数据传递,代码逻辑更简洁。app.post('/submit')处理:计算完字母总数后,直接渲染index.ejs并传递统计结果。属于直接渲染响应,少了一次重定向的步骤,流程更短。
我的实现逻辑
- 用全局变量
countLetters存储统计结果,每次app.get('/')都将该变量传递给EJS,通过变量值是否为0来切换h1内容。 - POST请求计算完成后,通过
res.redirect('/')重定向到GET请求,这是**PRG模式(Post/Redirect/Get)**的典型应用,核心目的是避免用户刷新页面时重复提交POST请求。
优劣对比与建议
- 老师的实现:代码更简洁,没有全局变量的风险,但存在一个隐患:用户刷新结果页面时,浏览器会重新发送POST请求,在涉及数据库写入等场景下,会导致重复操作(这个案例中影响不大,但生产环境需注意)。
- 我的实现:采用PRG模式避免了表单重复提交问题,还添加了日志中间件方便调试,但全局变量
countLetters是严重的问题——Express是多用户并发的服务,全局变量会被所有请求共享,A用户的统计结果会被B用户看到,完全不适合多用户场景。
如果想保留PRG模式的优势,同时解决全局变量的问题,可以改用URL参数传递结果:
// 修改POST请求的处理逻辑 app.post("/submit", (req, res) => { const firstName = req.body.fName; const lastName = req.body.lName; const countLetters = firstName.length + lastName.length; res.redirect(`/?count=${countLetters}`); }); // 修改GET请求的处理逻辑 app.get("/", (req, res) => { const countLetters = req.query.count || 0; res.render("index.ejs", {countLetters: countLetters}); });
内容的提问来源于stack exchange,提问作者fmerighi
相关产品推荐
相关产品推荐

