你是否也曾困惑:前端和后端到底怎么分开又怎么协作?
今天我们就从 0 到 1,手写一个用户信息列表页面。
后端用 json-server 模拟 REST API,前端用三件套(HTML + CSS + JS)实现动态渲染。
整个过程会涉及到:模块化思想、DOM 编程、fetch 异步请求、数组遍历......
全是干货,代码可直接运行。
一、最终效果预览
启动服务后,访问 index.html,页面会从一个"假后端" http://localhost:3000/users 拉取用户数据,并动态展示在表格中:
| ID | 姓名 | 昵称 | 家乡 |
|---|---|---|---|
| 1 | lyg | 东理薛之谦 | 南昌 |
| 2 | hy | 航哥 | 南昌 |
| 3 | lqq | 橙帅 | 信丰 |
(数据来源于 db.json)
二、项目结构与设计思想
我们采用 前后端模块化分离 的目录结构:
bash
project/
├── fe/ # 前端代码(实际可独立部署)
│ ├── index.html
│ └── common.js
└── backend/ # 后端模拟
├── db.json
└── package.json
模块化 的好处:
- 每个文件职责单一,好维护
- 前端只关心视图和交互,后端只关心数据接口
- 方便以后扩展(比如把 json-server 换成真实后端)
三、后端准备:用 json-server 搭建零代码 API
1. 初始化项目
进入 backend 目录,执行:
csharp
npm init -y
得到 package.json(项目描述文件)。
2. 安装 json-server
vbscript
npm install json-server
3. 创建数据库文件 db.json
写入以下内容(对象字面量作为数据源):
json
{
"users": [
{
"id": 1,
"name": "lyg",
"hometown": "南昌",
"nickname": "东理薛之谦"
},
{
"id": 2,
"name": "hy",
"hometown": "南昌",
"nickname": "航哥"
},
{
"id": 3,
"name": "lqq",
"hometown": "信丰",
"nickname": "橙帅"
}
]
}
4. 配置启动脚本
在 package.json 中的 "scripts" 字段添加:
json
"scripts": {
"dev": "json-server --watch db.json"
}
解析 :
--watch表示监听文件变化,自动重启服务。默认会开启http://localhost:3000,并提供/users等 RESTful 接口。
5. 启动后端
arduino
npm run dev
此时访问 http://localhost:3000/users 就能看到 JSON 格式的用户数据,一个完整的后端就"假装"好了。
四、前端页面:HTML + CSS 布局
1. 整体结构(语义化标签)
采用 盒子模型 思维:先划分盒子区域,再往里面填内容。
使用 HTML5 语义标签让结构更清晰:
xml
<header>用户管理系统</header>
<main class="container">
<aside>侧边栏(可留空)</aside>
<div class="row col-md-6 col-md-offset-3">
<table class="table table-striped">
<thead>...</thead>
<tbody><!-- 动态插入数据 --></tbody>
</table>
</div>
<aside>右侧栏(可留空)</aside>
</main>
<footer>© 2025 全栈小练习</footer>
2. 引入 Bootstrap 快速美化
在 <head> 中加入 Bootstrap CDN(Twitter 框架):
ini
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
.container:固定宽度居中(PC 时代经典布局).row/.col-md-*:栅格系统,行列布局.table及.table-striped:表格斑马纹样式
注意:表格必须包含
<tbody>标签,否则 JS 动态插入会找不到挂载点。
完整 index.html 代码如下(见文末汇总)。
五、DOM 编程与动态数据渲染
1. 理解 DOM 树
当浏览器解析 HTML 后,会生成一棵 文档对象模型(DOM)树:
document是整个文档的根对象document.documentElement对应<html>document.body对应<body>- 每个标签都是一个 节点(Node) ,可以查询、修改、添加、删除
我们通过 选择器 找到表格的 tbody 节点,然后动态往里面插入行。
2. 使用 fetch 获取后端数据
在 common.js 中编写:
javascript
fetch('http://localhost:3000/users') // 发起 GET 请求
.then(response => response.json()) // 解析 JSON 流
.then(users => {
// 拿到用户数组,开始渲染
})
fetch是浏览器内置的 API,用于异步 HTTP 请求- 它返回一个 Promise,所以用
.then()处理结果 - 第一个
.then()将原始响应转成 JSON 对象 - 第二个
.then()拿到真正的数据(数组)
3. 动态生成表格行
拿到 users 数组后,遍历每一个用户对象,构造 <tr> 并插入到 <tbody> 中。
less
const tbody = document.querySelector('.table tbody'); // 找到挂载点
let i = 1;
for (let user of users) {
tbody.innerHTML += `
<tr>
<td>${i}</td>
<td>${user.name}</td>
<td>${user.nickname}</td>
<td>${user.hometown}</td>
</tr>
`;
i++;
}
重点解析:
document.querySelector():返回第一个匹配 CSS 选择器的元素.innerHTML:可以获取或设置元素内部的 HTML 结构,我们使用+=追加内容- 模板字符串(反引号
...)内部可以嵌入${变量},非常方便 for...of是 ES6 引入的遍历数组的语法,比传统for (let i=0; i<arr.length; i++)更简洁、可读性更强
为什么不直接用
innerHTML = 完整字符串而要用+=?因为每次追加会保留原有的内容,否则会覆盖掉之前的行(本例中可接受,但更推荐先构建好一个完整字符串再一次性赋值,性能更好)。
4. 完整 common.js
less
fetch('http://localhost:3000/users')
.then(res => res.json())
.then(users => {
const tbody = document.querySelector('.table tbody');
let i = 1;
for (let user of users) {
tbody.innerHTML += `
<tr>
<td>${i}</td>
<td>${user.name}</td>
<td>${user.nickname}</td>
<td>${user.hometown}</td>
</tr>
`;
i++;
}
});
六、完整代码清单
1. backend/db.json
json
{
"users": [
{
"id": 1,
"name": "lyg",
"hometown": "南昌",
"nickname": "东理薛之谦"
},
{
"id": 2,
"name": "hy",
"hometown": "南昌",
"nickname": "航哥"
},
{
"id": 3,
"name": "lqq",
"hometown": "信丰",
"nickname": "橙帅"
}
]
}
2. backend/package.json
json
{
"name": "backend",
"version": "1.0.0",
"description": "模拟后端API",
"main": "index.js",
"scripts": {
"dev": "json-server --watch db.json"
},
"dependencies": {
"json-server": "^1.0.0-beta.15"
}
}
3. fe/index.html
xml
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户列表 - 前后端分离演示</title>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<header class="text-center" style="padding: 20px; background: #f5f5f5;">
<h1>用户信息管理</h1>
</header>
<main class="container" style="margin-top: 30px;">
<aside class="col-md-2"> </aside>
<div class="col-md-8">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>昵称</th>
<th>家乡</th>
</tr>
</thead>
<tbody>
<!-- JS 会动态填充数据 -->
</tbody>
</table>
</div>
<aside class="col-md-2"> </aside>
</main>
<footer class="text-center" style="padding: 15px; margin-top: 50px; background: #eee;">
© 2025 全栈练习 | 数据来自 json-server
</footer>
<script src="./common.js"></script>
</body>
</html>
4. fe/common.js
bash
fetch('http://localhost:3000/users')
.then(response => response.json())
.then(users => {
const tbody = document.querySelector('.table tbody');
let index = 1;
for (let user of users) {
tbody.innerHTML += `
<tr>
<td>${index}</td>
<td>${user.name}</td>
<td>${user.nickname}</td>
<td>${user.hometown}</td>
</tr>
`;
index++;
}
})
.catch(error => console.error('数据加载失败:', error));
七、运行步骤
- 在
backend目录下执行npm run dev,保持终端不关闭(服务运行在3000端口) - 用 Live Server 或直接打开
fe/index.html(注意:直接打开文件会有跨域? json-server 默认支持 CORS,可以直接用file://协议访问,但更推荐用http-server或 Live Server 打开fe目录) - 看到页面上的表格自动填充了数据库里的三条用户信息
如果数据没显示,打开浏览器控制台(F12)查看网络请求是否成功,确认
http://localhost:3000/users能否正常访问。
八、知识点总结
| 技术点 | 说明 |
|---|---|
| 模块化思想 | 前端/后端代码分离,各自独立开发、维护,通过 HTTP 接口通信 |
| json-server | 用 JSON 文件快速搭建 REST API,适合原型开发和学习 |
| 语义化标签 | header, main, footer, aside 等让 HTML 结构更清晰,SEO 友好 |
| DOM 树 | HTML 被解析成节点树,document 对象提供查询和操作节点的方法 |
| fetch API | 现代浏览器内置的异步请求方法,返回 Promise,替代古老的 XMLHttpRequest |
| innerHTML | 动态修改元素内部 HTML 的快捷方式(使用时注意 XSS 风险,本例无用户输入) |
| for...of | 简洁遍历数组的方法,直接获取每个元素的值,无需手动维护索引 |
| 模板字符串 | 反引号 + ${} 语法,轻松拼接字符串和变量 |
九、拓展练习
- 增加用户 :添加一个表单,通过
fetch POST向后端新增用户 - 删除用户 :给每一行加一个删除按钮,调用
DELETE /users/{id} - 改用 async/await :把
.then()改成更现代的async/await写法 - 错误处理 :在
catch中显示友好的错误提示
大厂面试尤其关注 DOM 编程、模块化以及底层 HTTP 交互 ------ 这些基础打好了,无论框架如何变化,都能快速上手。
写在最后
这篇笔记来自我的全栈学习过程,每一行代码都亲手敲过。
前后端分离不是高大上的概念,而是一种可以立刻实践的开发模式。
希望这篇文章能帮你迈出"既懂前端又懂后端"的第一步。
有任何疑问,欢迎在评论区交流 👏
如果你觉得有用,请点赞 ❤️ 或收藏 ⭐,让更多人看到~