接上文,我们主要分享了搭建本地 Mysql 环境,使用数据库工具创建了我们需要的数据库和表结构,字段定义。
那么下一步,我们通过前后端联调功能,实现一个基本的登录注册。
<!-- /public/index.html 文件 -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>用户登录和注册</title>
<link
rel="stylesheet"
type="text/css"
href="./index.css"
/>
</head>
<body>
<!-- 用于防止浏览器自动填充密码 -->
<input
type="password"
clearable
hidden
autocomplete="new-password"
style="display: none"
/>
<div class="form">
<div class="group">
<div class="label">账号</div>
<div class="value">
<input
class="input username"
type="input"
placeholder="请输入用户名"
/>
</div>
</div>
<div class="group">
<div class="label">密码</div>
<div class="value">
<input
class="input password"
type="input"
placeholder="请输入密码"
/>
</div>
</div>
<div class="submit">
<div class="button login">登录</div>
<div class="button register">注册</div>
</div>
</div>
</body>
</html>这是我们的登录注册页面的 HTML部分,值得一提的是,图中注释处,用于防止浏览器自动填充密码,是笔者试过多个方案,才得出的可以有效防止烦人的浏览器密码自动填充写法。
/* /public/index.css 文件 */
* {
padding: 0;
border: 0;
margin: 0;
outline: 0;
box-sizing: border-box;
}
body {
font-size: 14px;
.form {
width: 300px;
padding: 10px;
}
.group {
margin-bottom: 15px;
}
.label {
font-size: 12px;
margin-bottom: 5px;
}
.value {
.input {
height: 30px;
border: 1px solid #ddd;
padding: 0 10px;
width: 100%;
}
}
.submit {
display: flex;
justify-content: space-between;
}
.button {
width: calc((100% - 10px) / 2);
height: 34px;
line-height: 34px;
text-align: center;
border: 1px solid #ddd;
border-radius: 4px;
background-color: #165dff;
color: #fff;
cursor: pointer;
&:hover {
background-color: #4080ff;
}
}
}接下来页面的 CSS部分,新版浏览器已经支持 CSS 原生语法嵌套,所以,笔者也就直接用上了。

在控制台启动服务,访问对应的地址,页面展示效果就是这样的。
// 点击注册按钮
document.querySelector(".register").addEventListener("click", () => {
const username = document.querySelector(".username").value;
const password = document.querySelector(".password").value;
const result = fetch("/api/userRegister", {
method: "POST",
headers: {
"Content-Type": "application/json;charset=utf-8",
},
body: JSON.stringify({
username: username,
password: password,
}),
});
});
// 点击登录按钮
document.querySelector(".login").addEventListener("click", () => {
const username = document.querySelector(".username").value;
const password = document.querySelector(".password").value;
const result = fetch("/api/userLogin", {
method: "POST",
headers: {
"Content-Type": "application/json;charset=utf-8",
},
body: JSON.stringify({
username: username,
password: password,
}),
});
});接着,写 JS 部份代码,功能就是点击注册按钮,向 /api/userRegister 接口发起请求,把我们的用户名和密码提交到接口端。
其中,我们用到了浏览器原生的 fetch 方法,这是传统的 ajax 的升级版,在较新版本的浏览器可以直接使用。
如果对于什么是 ajax?什么是 fetch?不了解的话,请查阅相关资料,或到问答群艾特笔者,本小册不再赘述。
还有个问题,就是代码案例,使用的是原生 JS 语法,关于为什么不用 Vue?为什么不用 React?
这个无所谓,笔者的这个教程,主要给大家讲清楚,说明白,Node.js 全栈开发的基本方法。
你要学习的,是全栈开发的思路,具体用什么技术,什么框架,到时候自己可以举一反三,水到渠成。
为了对 fetch 有一个整体的认识,以下是 fetch 所有属性值一览。
let promise = fetch(url, {
method: "GET", // 请求方法 POST, PUT, DELETE 等等。
headers: {
// 请求头参数
"Content-Type": "text/plain;charset=UTF-8",
},
body: undefined, // string, FormData, Blob, BufferSource, or URLSearchParams
referrer: "about:client", // or "" to send no Referer header,
// or an url from the current origin
referrerPolicy: "strict-origin-when-cross-origin", // no-referrer-when-downgrade, no-referrer, origin, same-origin...
mode: "cors", // same-origin, no-cors
credentials: "same-origin", // omit, include
cache: "default", // no-store, reload, no-cache, force-cache, or only-if-cached
redirect: "follow", // manual, error
integrity: "", // a hash, like "sha256-abcdef1234567890"
keepalive: false, // true
signal: undefined, // AbortController to abort request
window: window, // null
});到了这一步,我们登录注册功能的前端部分,就完成十之八九了。
下一步,就是实现登录注册功能的接口部分。
// /apis/userRegister.js 文件
export default () => {
return {
code: 0,
msg: "注册成功",
data: {
id: 1,
name: "chensuiyi",
score: 100,
},
};
};先不管三七二十一,把之前的接口 getUserScore.js,改成 userRegister.js。

浏览器端点击注册按钮,发起请求,成功拿到了接口返回的数据。
说明了什么?前后的联调成功了。
但是,返回的数据是写死的,肯定不行。
所以,下一步,我们要改造接口代码。
思考一下,前端和接口联调,它们之间是如何 联 起来的?是请求的 数据,也就是 用户名 和 密码 这 2 个数据。
浏览器给了数据,后端就要拿到数据,进行业务逻辑的处理。那么后端如何拿到我们的请求数据呢?
由于请求的数据是流式的,怎么说呢?很简单,浏览器通过 POST 方式发给接口端的参数,不是一次性给的,而是像自来水一样,是流动的。
想象一下,我们要把水桶装满水 (拿到请求的数据),那么我们需要等水桶装满后 (数据全部拿到后) 再进一步操作。
// /server.js 文件
import { createServer } from "node:http";
import { readFileSync, existsSync } from "node:fs";
import { lookup } from "mime-types";
import { apiMaps } from "./routes.js";
const server = createServer((req, res) => {
if (req.url.startsWith("/api/") === true) {
const apiName = req.url.replace("/api/", "");
res.setHeader("Content-Type", "application/json");
if (apiMaps[apiName]) {
// 请求数据接收开始
let body = "";
req.on("data", (chunk) => {
body += chunk.toString();
});
req.on("end", async () => {
try {
req.body = JSON.parse(body);
cosole.log(req); // 打印请求参数
const result = await apiMaps[apiName].default(req); // 把请求直接给接口
res.end(JSON.stringify(result));
} catch (err) {
console.log("🚀 ~ req.on ~ err:", err);
res.end(
JSON.stringify({
code: 1,
msg: "请求参数结构有误",
})
);
}
});
// 请求数据接收结束
} else {
res.end(
JSON.stringify({
code: 1,
msg: "接口不存在",
})
);
}
} else {
if (req.url === "/") {
req.url = "/index.html";
}
if (existsSync(`./public/${req.url}`) === true) {
res.setHeader("Content-Type", lookup(req.url));
res.end(readFileSync(`./public/${req.url}`));
} else {
res.writeHead(404, { "Content-Type": "text/plain" });
res.end("404 Not Found");
}
}
});
server.listen(3000, "127.0.0.1", () => {
console.log("服务已启动,监听端口为:3000");
});请注意代码中注释 请求数据接收开始 和 请求数据接收结束 之间,我们定义一个 body 变量,用来接收所有的请求数据。
然后监听数据的 data 事件,把所有一段一段的数据,用 body 来拼接到一起。
最后,监听数据的 end 事件,表示水桶满了 (参数数据全部拿到了),我们就把参数解析成 JSON 格式,然后放到请求 req 的 body 属性,以备使用。

接着发起请求,在我们的接口运行控制台,可以看到几百上千个各种属性,万军从中,我们的 body 属性赫然在列,而其中正是我们需要的参数!
记住,一定要眼见为实地看到我们的数据,而不是盲目猜测,这其实就是笔者学习 Node.js,学习各种编程技术的核心方法。
通过做实验的方式,去验证,去用眼睛看到一切地发生,我们就能更加深刻地理解一个技术,一个知识。
确认请求数据拿到后,直接传递给注册接口。
// /apis/userRegister.js 文件
export default (req) => {
// 接收 req 参数
return {
code: 0,
msg: "注册成功",
data: {
id: 1,
name: "chensuiyi",
score: 100,
},
};
};万事具备,只欠东风,本文也不短了。
那么下一篇文章,我们就进行最后的步骤,操作数据库,进行登录和注册的功能实现。
