前言

尝试实现前后端通信,前端用 Vite 打包,同时还有热更新功能。后端则是 server.js 脚本。

初始化项目

新建文件夹

1
2
mkdir [yourprojectName]
cd [yourprojectName]

初始化项目结构

项目结构大致如下

  • Project
    • node_modules
    • src
      • dist
      • index.html
      • main.js
      • style.css
    • .gitignore
    • server.js
    • vite.config.js
    • package.json
    • README.md

初始化包管理器

1
npm init -y

安装Vite

1
npm install -D vite

基本配置

进入 package.json 文件,没有则创建一个,写入脚本

1
2
3
4
5
6
7
8
9
10
11
{
...
"main": "server.js",
"scripts": {
"dev:fe": "vite",
"build": "vite build",
"start": "node server.js",
"preview": "vite build && node server.js"
},
...
}

进入 vite.config.js 文件,没有则创建一个,写入配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { defineConfig } from 'vite';

export default defineConfig({
root: 'src' // 指定入口目录为 src
build: {
// 打包输出到 dist 目录,server.js 会将此目录作为静态根目录
outDir: 'dist',
emptyOutDir: true,
// 入口文件
rollupOptions: {
input: 'src/index.html', // 将 index.html 作为入口
}
}
});

还有其他参数可以填写,详情可见vite官网

创建 .gitignore 文件,阻止不必要文件夹上传,写入以下内容

1
2
3
node_modules/
dist/
cache/

如果已经上传可以写入以下命令取消追踪

1
2
3
4
5
# 取消追踪 node_modules 文件夹
git rm -r --cached node_modules

# 取消追踪 dist 文件夹
git rm -r --cached dist

实现简单前后端通信

创建 node.js 服务器

在 server.js 文件中写入以下内容

1
2
3
4
5
6
7
8
9
10
11
const http  = require("http"); //引入http模块

const server = http.createServer((req, res) => {
res.writeHead(200);
res.end('hello world');
});

const port = 3000;
server.listen(port, () => {
console.log("服务器已经在 https://localhost:3000 启动")
});

运行以下代码运行服务器

1
npm run start

在浏览器输入 localhost:3000 可以看到 hello world 输出

实现 GET 请求

GET 请求会向服务端发出申请,然后服务端返回数据

在 index.html 文件中写入如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>

<body>
<div id="result"></div>

<!-- GET 请求按钮 -->
<button id="getBtn">获取数据(GET)</button>

<!-- POST 请求按钮 -->
<button id="postBtn">发送数据(POST)</button>
<script type="module" src="/main.js"></script>
</body>

</html>

前端脚本 main.js 写入以下内容发出 GET 请求

1
2
3
4
5
6
7
8
9
10
11
12
const resultDiv = document.querySelector("#result");
const getBtn = document.querySelector("#getBtn");

getBtn.addEventListener("click", async () => {
try {
const response = await fetch("http://localhost:3000/api/data");
const data = await response.json();
resultDiv.innerHTML = `<pre>${JSON.stringify(data, null, 2)}</pre>`
} catch (error) {
resultDiv.innerHTML = `错误:${error.message}`;
}
});

在后端 server.js 文件中的 createServe 函数中写入以下内容处理 GET 请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
...
res.setHeader('Access-Control-Allow-Origin', '*'); // 指定允许的前端 origin(或用 * 允许所有)
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); // 允许的请求方法
res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); // 允许的请求头

if (req.method === 'OPTIONS') {
res.writeHead(200);
res.end();
return;
}

//处理GET请求
if (req.method === "GET" && req.url === '/api/data') {
res.writeHead(200, {'content-type': 'application/json'});
res.end(JSON.stringify({
message: "来自服务器的问候:hello",
data: [1,2,3]
}));
}
...

终端运行 server.js 和前端

1
2
3
4
5
# 运行前端
npm run dev

# 运行后端
npm run start

进入浏览器地址 localhost:5173 ,点击 “获取数据(GET)” 按钮,可以看到以下输出

1
2
3
4
5
6
7
8
{
"message": "来自服务器的问候:hello",
"data": [
1,
2,
3
]
}

实现 POST 请求与其他请求

POST 请求会向服务端传输数据,然后服务端再返回传输结果

前端脚本 main.js 中写入以下内容来使用 POST 请求传输数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const postBtn = document.querySelector("#postBtn");

postBtn.addEventListener("click", async () => {
const postDate = {name: "Charlie", age: 28};
try {
const response = await fetch("http://localhost:3000/api/data", {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(postDate)
});
const data = await response.json();
resultDiv.innerHTML = `<pre>${JSON.stringify(data, null, 2)}</pre>`
} catch (error) {
resultDiv.innerHTML = `错误:${error.message}`;
}
});

后端服务器 server.js 中写入以下内容来处理 POST 请求与其他请求 (其他请求返回代码 404)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
//处理POST请求
else if (req.method === "POST" && req.url === '/api/data') {
let body = "";
req.on('data', chaunk => {
body += chaunk.toString();
});
//数据流结束,解析并响应
req.on('end', () => {
try{
const parsedBody = JSON.parse(body);
res.writeHead(200, {'content-type': 'application/json'});
res.end(JSON.stringify({
recived: parsedBody,
Response: "数据传输成功"
}));
} catch (error) {
res.writeHead(400, {'content-type': 'application/json'});
res.end(JSON.stringify({ error: 'Invalid JSON'}));
}
});
}
//其他请求返回404
else {
res.writeHead(404, {'content-type': 'application/json'});
res.end(JSON.stringify({error:'Not found'}));
}
...

重启 server.js 服务器 (Vite是热更新,可以不需要重启,按下 Ctrl + C 可以终止任务)

1
npm run start

进入浏览器地址 localhost:5173 ,点击 “传输数据(POST)” 按钮,可以看到以下输出

1
2
3
4
5
6
7
{
"recived": {
"name": "Charlie",
"age": 28
},
"Response": "Data received successfully"
}

添加一个简单的功能

给这个简陋的系统添加一个计算平方数的功能

在 index.html 页面中加入以下内容来实现输入数据

1
2
3
4
<div>
<label for="countNum">计算平方<input type="number" id="Num"></label>
<button id="count">计算</button>
</div>

前端脚本 main.js 中写入以下内容来将输入的数据传入后端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const countBtn = document.querySelector("#count");

countBtn.addEventListener("click", async () => {
const inputmodule = document.querySelector("#Num");
const input = Number(inputmodule.value);
console.log(input)
try {
const response = await fetch("http://localhost:3000/api/count", {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({ "num": input })
});
const data = await response.json();
resultDiv.innerHTML = `<pre>计算结果为${data.result}</pre>`
} catch (error) {
resultDiv.innerHTML = `错误:${error.message}`;
}
});

后端服务器 server.js 中写入以下内容来处理前端传入的数据与实现核心功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    ...
else if (req.method === "POST" && req.url === '/api/count') {
//改用Buffer拼接更加保险 (虽然不知道Buffer是什么,但好像这样写更好)
let buffer = Buffer.alloc(0);
req.on('data', chunk => {
buffer = Buffer.concat([buffer, chunk]);
});
req.on('end', () => {
try {
const body = buffer.toString();
const parseData = JSON.parse(body);

const receivedNum = parseData.num;

//简单的判断
if (typeof receivedNum !== 'number' || isNaN(receivedNum)) {
res.writeHead(400, {'content-type': "application/json"});
res.end(JSON.stringify({ error: '传入的必须是数字'}));
return;
}

//实现核心功能
const result = countNum(receivedNum);

res.writeHead(200,{'content-type': 'applicaton/json'});
res.end(JSON.stringify({result: result}));
} catch (error) {
console.log('JSON解析失败',error.message);
res.writeHead(400, {'content-type': 'application/json'});
res.end(JSON.stringify({ error: 'Invalid JSON'}));
}
});
}
...

function countNum (input) {
return input*input;
}

这样一个简单的平方运算功能就实现了。

结语

折腾半天终于也是成功实现了简单的前后通信。为了整这个中途还去了解了一些 http 相关的东西。

哦对了,写完后还自动引入了其他模块,所有引入的模块如下:

1
2
3
4
const { error } = require("console");
const { write } = require("fs");
const http = require("http");
const { writeHeapSnapshot } = require("v8");