《前端基础实战:从零搭建用户列表,掌握前后端分离核心思想》
从零搭建用户列表:一个完整的前后端分离项目实战
本文将带你从零开始,使用原生HTML/CSS/JS配合json-server,完成一个用户列表展示功能。涵盖语义化HTML、DOM编程、模块化思想、后端Mock数据等核心知识点。
写在前面
最近在整理前端基础知识时,发现很多同学虽然能用框架快速搭建页面,但对底层的HTML语义、DOM操作、模块化设计等概念却掌握不牢。这篇文章从一个简单的用户列表需求出发,完整记录了我的开发思路和技术选型,希望能帮助大家夯实基础。
项目结构
project/
├── fe/ # 前端目录
│ ├── index.html # 页面结构
│ ├── common.js # JS逻辑
│ └── style.css # 样式(可选)
├── backend/ # 后端目录
│ ├── package.json # 项目配置
│ └── db.json # 模拟数据库
└── README.md
📚 知识点:为什么这样组织目录?
-
前后端分离:
fe和backend可以分别部署在不同的服务器 - 职责单一:每个文件只做一件事,符合软件工程原则
- 便于协作:前端和后端可以并行开发,互不干扰
一、HTML 结构设计
语义化标签优先
很多同学写页面喜欢 div 一把梭,但语义化标签对SEO和代码可读性都更有好:
<header>页面头部</header>
<main class="container">
<aside>侧边栏</aside>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<!-- 主要内容 -->
</div>
</div>
<aside>右侧边栏</aside>
</main>
<footer>页面底部</footer>
📚 知识点详解:语义化标签
| 标签 | 语义 | 对SEO的影响 | 替代的div方案 |
|---|---|---|---|
<header> |
页眉/区块头部 | 告诉搜索引擎这是导航区域 | <div class="header"> |
<main> |
页面主体内容 | 标识核心内容,一个页面只有一个 | <div class="main"> |
<aside> |
侧边栏/辅助信息 | 表示与主内容相关的辅助信息 | <div class="sidebar"> |
<footer> |
页脚/版权信息 | 标识页面底部信息区域 | <div class="footer"> |
<nav> |
导航链接 | 标识网站导航区域 | <div class="nav"> |
为什么语义化很重要?
- 搜索引擎优化(SEO):谷歌爬虫给语义化标签更高的权重
- 无障碍访问(a11y):屏幕阅读器可以快速定位页面结构
- 代码可维护性:其他开发者接手时能快速理解页面结构
- 浏览器默认样式:部分标签自带合理的默认样式
Table 的正确写法
表格一定要区分 thead 和 tbody,这不仅让结构清晰,也方便后续JS操作:
<table class="table" id="user-table">
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>昵称</th>
<th>家乡</th>
</tr>
</thead>
<tbody></tbody> <!-- JS动态填充内容 -->
</table>
📚 知识点详解:表格标签
table(表格容器)
├── thead(表头区域,0或1个)
│ └── tr(表格行)
│ └── th(表头单元格,自动加粗居中)
├── tbody(表格主体,0或多个)
│ └── tr
│ └── td(普通单元格)
└── tfoot(表尾区域,0或1个,可选)
└── tr
└── td
th 与 td 的区别:
-
<th>(Table Header):表头单元格,默认字体加粗、居中,表示该列的含义 -
<td>(Table Data):普通数据单元格,默认左对齐、普通字重
为什么JS要操作tbody而不是table?
- 只刷新数据区域,表头保持不变
- 避免意外破坏表格结构
- 性能更好(只重新渲染tbody部分)
盒模型思维
写HTML时先搭盒子框架,再填充内容。container 固定宽度左右留白,row 表示一行,这是经典的PC端布局思路。
📚 知识点详解:CSS盒模型
┌─────────────────────────────────────┐
│ margin(外边距) │
│ ┌───────────────────────────────┐ │
│ │ border(边框) │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ padding(内边距) │ │ │
│ │ │ ┌───────────────────┐ │ │ │
│ │ │ │ content(内容) │ │ │ │
│ │ │ └───────────────────┘ │ │ │
│ │ └─────────────────────────┘ │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
块级元素 vs 行内元素:
| 类型 | 特点 | 常见标签 | 宽度 |
|---|---|---|---|
| 块级元素 | 独占一行,可设宽高 |
<div>、<p>、<header>、<main>
|
默认100% |
| 行内元素 | 与其他元素同行,不可设宽高 |
<span>、<a>、<strong>
|
内容撑开 |
二、CSS 与 Bootstrap
直接引入 Bootstrap 3 快速获得样式支持,省去手写大量CSS的时间:
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet">
Bootstrap的栅格系统非常实用:col-md-6 col-md-offset-3 表示在中等屏幕上占据一半宽度并居中。
📚 知识点详解:Bootstrap栅格系统
Bootstrap把一行(row)分成12等份:
| 类名 | 含义 | 宽度计算 |
|---|---|---|
col-md-4 |
中等屏幕占4格 | 4/12 = 33.33% |
col-md-6 |
中等屏幕占6格 | 6/12 = 50% |
col-md-12 |
中等屏幕占12格 | 100% |
col-md-offset-3 |
向右偏移3格 | 产生左边距 |
响应式断点:
-
col-xs-*:超小屏幕(手机,<768px) -
col-sm-*:小屏幕(平板,≥768px) -
col-md-*:中等屏幕(桌面,≥992px) -
col-lg-*:大屏幕(宽屏,≥1200px)
CDN工作原理:
- 浏览器解析到
<link>标签 - 向CDN服务器发起HTTP请求
- 下载bootstrap.min.css文件
- 解析CSS并应用到页面
为什么用CDN?
- 速度快:CDN就近分配节点
- 缓存共享:访问其他使用同一CDN的网站无需重新下载
- 节省带宽:不占用自己服务器流量
- 并发加载:不同域名可并行下载
三、JavaScript DOM 编程
模块化思维
不要把代码都塞在一个文件里。common.js 专门处理前端逻辑,与HTML结构分离,便于维护和扩展。
📚 知识点详解:模块化设计
| 设计原则 | 说明 | 本例体现 |
|---|---|---|
| 单一职责 | 一个模块只做一件事 | HTML管结构,CSS管样式,JS管逻辑 |
| 高内聚 | 模块内部紧密相关 | common.js只处理用户列表相关逻辑 |
| 低耦合 | 模块之间依赖少 | 通过ID/class连接,不直接依赖 |
动态渲染表格
核心步骤:找到挂载点 → 遍历数据 → 动态生成HTML
// 获取tbody元素作为挂载点
const oBody = document.querySelector('.table tbody');
let i = 1;
for (let user of users) {
oBody.innerHTML += `
<tr>
<td>${i}</td>
<td>${user.name}</td>
<td>${user.nickname}</td>
<td>${user.hometown}</td>
</tr>
`;
}
📚 知识点详解:DOM操作
DOM树结构:
document(根节点)
└─ html(<html>)
└─ body(<body>)
└─ main.container
└─ div.row
└─ div.col-md-6
└─ table
├─ thead
└─ tbody ← querySelector找到这里
querySelector vs 其他选择器:
// 返回第一个匹配的元素
document.querySelector('.table tbody')
// 返回所有匹配的元素(NodeList)
document.querySelectorAll('td')
// 通过ID获取(最快)
document.getElementById('user-table')
// 通过类名获取
document.getElementsByClassName('table')
innerHTML 的工作原理:
- 浏览器解析传入的HTML字符串
- 构建新的DOM节点
- 替换原有的子节点
⚠️ 性能注意事项:
// ❌ 性能差:每次循环都重新解析
for (let user of users) {
oBody.innerHTML += '<tr>...</tr>';
}
// ✅ 性能好:一次构建,一次插入
let html = '';
for (let user of users) {
html += '<tr>...</tr>';
}
oBody.innerHTML = html;
ES6 语法亮点
-
for...of循环:比传统for (let i=0; i<arr.length; i++)更简洁 - 模板字符串:用反引号 +
${变量}替代字符串拼接,可读性暴增
📚 知识点详解:ES6语法
数组遍历方法对比:
// 传统for循环(需要索引时使用)
for (let i = 0; i < users.length; i++) {
console.log(users[i]);
}
// for...of(只需要值,不需要索引)
for (let user of users) {
console.log(user.name);
}
// forEach(函数式编程)
users.forEach(user => {
console.log(user.name);
});
// map(返回新数组)
const names = users.map(user => user.name);
模板字符串特性:
// 传统拼接(难写、难读)
const html = '<tr><td>' + user.id + '</td><td>' + user.name + '</td></tr>';
// 模板字符串(优雅、支持换行)
const html = `
<tr>
<td>${user.id}</td>
<td>${user.name}</td>
</tr>
`;
四、后端模拟:json-server
前端开发经常遇到后端接口还没好的情况,json-server 可以快速Mock一个REST API。
初始化项目
cd backend
npm init -y
npm install json-server
📚 知识点详解:npm包管理
package.json的作用:
{
"name": "backend", // 项目名称
"version": "1.0.0", // 版本号(语义化版本)
"description": "", // 项目描述
"main": "index.js", // 入口文件
"scripts": { // 自定义命令
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [], // 关键词
"author": "", // 作者
"license": "ISC", // 开源协议
"dependencies": { // 生产环境依赖
"json-server": "^0.17.0" // ^表示兼容版本
}
}
npm install 做了什么?
- 读取package.json中的dependencies
- 下载依赖包到node_modules目录
- 生成package-lock.json锁定版本
创建 db.json
{
"users": [
{
"id": 1,
"name": "昌哥",
"hometown": "南昌",
"nickname": "昌哥nb666"
},
{
"id": 2,
"name": "胡航",
"hometown": "南昌",
"nickname": "航哥"
}
]
}
📚 知识点详解:JSON格式
JSON语法规则:
- 键名必须用双引号包裹
- 字符串值必须用双引号
- 数字、布尔值、null不需要引号
- 不能有注释
- 最后一个属性后面不能有逗号
数据类型:
| 类型 | 示例 | 说明 |
|---|---|---|
| 字符串 | "昌哥" |
双引号包裹 |
| 数字 | 25 |
整数或浮点数 |
| 布尔值 | true |
true或false |
| 数组 | [1, 2, 3] |
方括号 |
| 对象 | {"name": "昌哥"} |
花括号 |
| null | null |
空值 |
启动服务
npx json-server db.json --port 3000
访问 http://localhost:3000/users 就能拿到JSON数据。
📚 知识点详解:REST API
| HTTP方法 | 端点 | 功能 | 请求体 |
|---|---|---|---|
| GET | /users |
获取所有用户 | 无 |
| GET | /users/1 |
获取id=1的用户 | 无 |
| POST | /users |
新增用户 | {"name": "新用户"} |
| PUT | /users/1 |
完整更新id=1 | 全量数据 |
| PATCH | /users/1 |
部分更新id=1 | {"nickname": "新昵称"} |
| DELETE | /users/1 |
删除id=1 | 无 |
npx是什么?
- npm 5.2+ 自带的工具
- 直接运行node_modules/.bin下的命令
- 不需要全局安装json-server
五、前后端联调
用 fetch 发起网络请求,拿到数据后渲染到页面:
fetch('http://localhost:3000/users')
.then(response => response.json())
.then(data => {
users = data;
renderTable(); // 重新执行渲染逻辑
});
📚 知识点详解:fetch API和异步编程
fetch执行流程:
fetch() 发起请求
↓(异步,不阻塞后续代码)
浏览器发送HTTP请求
↓
服务器返回响应
↓
第一个.then() 拿到Response对象
↓
调用.json() 解析JSON
↓
第二个.then() 拿到实际数据
↓
执行渲染逻辑
Response对象常用属性和方法:
fetch(url).then(response => {
response.status // HTTP状态码,200表示成功
response.ok // 布尔值,status在200-299之间为true
response.headers // 响应头信息
response.json() // 解析为JSON对象
response.text() // 解析为字符串
response.blob() // 解析为二进制数据
})
Promise的三种状态:
| 状态 | 含义 | 触发时机 |
|---|---|---|
| pending | 进行中 | 初始状态 |
| fulfilled | 成功 | 调用resolve() |
| rejected | 失败 | 调用reject() |
async/await语法糖:
// 等价于上面的fetch写法,但更直观
async function loadUsers() {
const response = await fetch('http://localhost:3000/users');
const data = await response.json();
users = data;
renderTable();
}
六、完整数据流图解
1. 用户打开页面
↓
2. 浏览器加载index.html
↓
3. 解析HTML,构建DOM树
↓
4. 遇到<link>加载Bootstrap CSS
↓
5. 遇到<script>加载并执行common.js
↓
6. common.js执行fetch请求
↓
7. 请求到达json-server
↓
8. json-server读取db.json
↓
9. 返回JSON数据给前端
↓
10. JS遍历数据,生成HTML字符串
↓
11. 设置tbody.innerHTML
↓
12. 浏览器重新渲染表格
↓
13. 用户看到用户列表
知识小结
| 知识点 | 要点 | 代码体现 |
|---|---|---|
| HTML语义化 | 用语义标签替代div |
<header>、<main>、<aside>、<footer>
|
| 表格语义化 | 区分表头和主体 |
<thead> + <tbody>
|
| DOM查询 | 用选择器查找元素 | document.querySelector('.table tbody') |
| 动态渲染 | 用innerHTML插入内容 | oBody.innerHTML += ... |
| 模块化 | 代码按职责拆分 | HTML结构 + JS逻辑 + CSS样式 |
| 后端模拟 | 用json-server | npx json-server db.json --port 3000 |
| 网络请求 | 用fetch获取数据 | fetch(url).then(res => res.json()) |
| 异步处理 | Promise处理异步 |
.then() 链式调用 |
常见问题排查指南
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
| 表格不显示 | 找不到tbody | 检查选择器是否正确 |
| 数据不更新 | fetch是异步的 | 确认渲染在.then回调中 |
| 中文乱码 | 缺少meta标签 | 添加<meta charset="UTF-8">
|
| 跨域报错 | 不同端口访问 | json-server默认支持CORS |
| 页面空白 | JS语法错误 | 打开F12控制台查看 |
| 连接失败 | json-server未启动 | 运行npx json-server db.json --port 3000
|
写在最后
这个项目虽然简单,但涵盖了Web开发的核心流程。打好HTML语义、DOM操作、模块化设计这些基础,后续学习React/Vue等框架会事半功倍。
学习路径建议:
- 手敲一遍代码,不要复制粘贴
- 修改参数观察变化,理解每一行
- 尝试添加新功能(搜索、分页、添加用户)
- 理解原理后再学框架
如果觉得有收获,欢迎点赞收藏!
项目源码已整理,需要的小伙伴可以私信我~