前后端模块化分离实战:从零搭建用户列表展示(HTML+CSS+JS + json-server)

你是否也曾困惑:前端和后端到底怎么分开又怎么协作?

今天我们就从 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));

七、运行步骤

  1. backend 目录下执行 npm run dev,保持终端不关闭(服务运行在 3000 端口)
  2. 用 Live Server 或直接打开 fe/index.html(注意:直接打开文件会有跨域? json-server 默认支持 CORS,可以直接用 file:// 协议访问,但更推荐用 http-server 或 Live Server 打开 fe 目录)
  3. 看到页面上的表格自动填充了数据库里的三条用户信息

如果数据没显示,打开浏览器控制台(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 简洁遍历数组的方法,直接获取每个元素的值,无需手动维护索引
模板字符串 反引号 + ${} 语法,轻松拼接字符串和变量

九、拓展练习

  1. 增加用户 :添加一个表单,通过 fetch POST 向后端新增用户
  2. 删除用户 :给每一行加一个删除按钮,调用 DELETE /users/{id}
  3. 改用 async/await :把 .then() 改成更现代的 async/await 写法
  4. 错误处理 :在 catch 中显示友好的错误提示

大厂面试尤其关注 DOM 编程、模块化以及底层 HTTP 交互 ------ 这些基础打好了,无论框架如何变化,都能快速上手。


写在最后

这篇笔记来自我的全栈学习过程,每一行代码都亲手敲过。

前后端分离不是高大上的概念,而是一种可以立刻实践的开发模式。

希望这篇文章能帮你迈出"既懂前端又懂后端"的第一步。

有任何疑问,欢迎在评论区交流 👏

如果你觉得有用,请点赞 ❤️ 或收藏 ⭐,让更多人看到~

相关推荐
天才熊猫君1 小时前
我把一个 bug 发给 ai,ai 直接通过 playwright mcp 直接排查出 bug。。
前端·javascript
meilindehuzi_a1 小时前
打破0基础:通过 5 个核心案例深度拆解 JavaScript 正则表达式与运行时类型系统
开发语言·javascript·正则表达式
时寒的笔记1 小时前
LF11期 day21-day22:逆向瑞数加密 欧冶案例分析(一)
javascript
lbb 小魔仙1 小时前
稳定比技巧更重要:海外多地区数据采集的经验教训
开发语言·javascript·ecmascript
布兰妮甜1 小时前
Vue 视图不更新?常见赋值踩坑点汇总
前端·javascript·vue.js·vue踩坑·vue视图不更新
我有满天星辰2 小时前
【Dart 语言学习教程 】第三章:函数式编程与高阶特性
开发语言·javascript·ecmascript
前端 贾公子2 小时前
uni-app工程化实战:基于vue-i18n和i18n-ally的国际化方案 (下)
前端
@zulnger2 小时前
selenium 操作浏览器
前端·javascript·selenium
爱编程的小金2 小时前
告别手写分页逻辑:usePagination 从 50 行到 3 行
javascript·vue·前端分页·alova·usepagination