Web开发基础进阶:从静态页面到动态交互
- Web 开发
- 17天前
- 50热度
- 0评论
1. 前言:你已经会写静态页面了,然后呢?
很多初学者掌握了HTML、CSS、JavaScript后,发现自己的网站依然是"死"的——刷新就重置、数据无法保存、不能和其他用户互动。这就是静态页面与动态网站的区别。
本教程能带给你什么?
✅ 理解前后端如何通信(AJAX/Fetch)
✅ 学会搭建简单的Node.js后端
✅ 掌握RESTful API设计与调用
✅ 连接MySQL数据库实现数据持久化
✅ 实战:从零搭建一个用户留言系统
本教程假设你已经了解HTML/CSS/JS基础,目标是帮你跨越"静态"到"动态"这道坎。
2. 核心概念:前后端如何通信?
传统方式:表单提交 → 刷新页面 → 服务器返回新页面
现代方式(AJAX):JavaScript发起请求 → 服务器返回数据(JSON) → JS更新局部页面(无刷新)
API(应用程序接口):前后端约定的通信规则,前端发请求到指定URL,后端返回数据。
RESTful API规范:
GET /api/users → 获取用户列表
POST /api/users → 创建新用户
PUT /api/users/1 → 更新用户1
DELETE /api/users/1 → 删除用户1
3. 环境准备:Node.js + Express
我们使用Node.js和Express框架搭建后端,轻量且易于理解。
3.1 安装Node.js
访问 nodejs.org 下载LTS版本安装,验证:node -v && npm -v
3.2 初始化项目
mkdir web-advanced && cd web-advanced
npm init -y
3.3 安装依赖
npm install express cors nodemon
- express:Web框架
- cors:解决跨域问题
- nodemon:代码改动自动重启服务器
在 package.json 中添加启动脚本:
"scripts": {
"start": "nodemon server.js"
}
4. 第一个API接口
创建 server.js:
const express = require('express');
const cors = require('cors');
const app = express();
const PORT = 3000;
app.use(cors());
app.use(express.json());
app.get('/', (req, res) => {
res.json({ message: 'API服务已启动', status: 'success' });
});
app.get('/api/hello', (req, res) => {
res.json({ data: 'Hello from backend!' });
});
app.post('/api/echo', (req, res) => {
const { text } = req.body;
res.json({ received: text, reply: `你发送了:${text}` });
});
app.listen(PORT, () => {
console.log(`服务器运行在 http://localhost:${PORT}`);
});
运行:npm start,访问 http://localhost:3000/api/hello 看到返回的JSON数据。
5. 前端调用API(Fetch)
创建一个前端页面来调用我们刚写的API。创建 index.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>前后端交互示例</title>
<style>
body{font-family:system-ui;max-width:800px;margin:50px auto;padding:20px;background:#f5f5f5;}
.card{background:#fff;border-radius:12px;padding:20px;margin-bottom:20px;box-shadow:0 2px 8px rgba(0,0,0,0.1);}
button{background:#4CAF50;color:#fff;border:none;padding:10px 20px;border-radius:6px;cursor:pointer;}
input,textarea{width:100%;padding:10px;margin:10px 0;border:1px solid #ddd;border-radius:6px;}
pre{background:#f0f0f0;padding:10px;border-radius:6px;overflow-x:auto;}
.result{background:#e8f4fd;padding:15px;border-radius:8px;margin-top:15px;}
</style>
</head>
<body>
<h1>🌐 前后端交互演示</h1>
<div class="card">
<h3>1. GET请求 - 获取后端消息</h3>
<button id="getBtn">发送GET请求</button>
<div id="getResult" class="result" style="display:none;"></div>
</div>
<div class="card">
<h3>2. POST请求 - 发送数据给后端</h3>
<input type="text" id="postText" placeholder="输入一些文字...">
<button id="postBtn">发送POST请求</button>
<div id="postResult" class="result" style="display:none;"></div>
</div>
<script>
const API_BASE = 'http://localhost:3000';
// GET请求示例
document.getElementById('getBtn').onclick = async () => {
try {
const response = await fetch(`${API_BASE}/api/hello`);
const data = await response.json();
document.getElementById('getResult').style.display = 'block';
document.getElementById('getResult').innerHTML = `
<strong>响应数据:</strong><br>
<pre>${JSON.stringify(data, null, 2)}</pre>
`;
} catch(err) {
console.error('请求失败:', err);
alert('请求失败,请确保后端服务已启动');
}
};
// POST请求示例
document.getElementById('postBtn').onclick = async () => {
const text = document.getElementById('postText').value;
if(!text) {
alert('请输入内容');
return;
}
try {
const response = await fetch(`${API_BASE}/api/echo`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: text })
});
const data = await response.json();
document.getElementById('postResult').style.display = 'block';
document.getElementById('postResult').innerHTML = `
<strong>响应数据:</strong><br>
<pre>${JSON.stringify(data, null, 2)}</pre>
`;
} catch(err) {
alert('请求失败');
}
};
</script>
</body>
</html>
打开 index.html(直接用浏览器打开,或使用Live Server),确保后端已启动,点击按钮查看前后端交互效果。
6. 数据持久化:连接MySQL数据库
目前数据存在内存中,重启即丢失。我们来连接MySQL实现数据持久化。
6.1 安装MySQL和数据库驱动
安装MySQL(使用XAMPP/MAMP等),然后安装驱动:npm install mysql2
6.2 创建数据库和数据表
CREATE DATABASE web_advanced;
USE web_advanced;
CREATE TABLE messages (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
6.3 带数据库的完整API
更新 server.js:
const express = require('express');
const cors = require('cors');
const mysql = require('mysql2/promise');
const app = express();
const PORT = 3000;
app.use(cors());
app.use(express.json());
const dbConfig = {
host: 'localhost',
user: 'root',
password: '',
database: 'web_advanced'
};
// GET /api/messages - 获取所有留言
app.get('/api/messages', async (req, res) => {
try {
const connection = await mysql.createConnection(dbConfig);
const [rows] = await connection.execute(
'SELECT * FROM messages ORDER BY created_at DESC'
);
await connection.end();
res.json({ success: true, data: rows });
} catch(err) {
res.status(500).json({ success: false, error: err.message });
}
});
// POST /api/messages - 新增留言
app.post('/api/messages', async (req, res) => {
const { username, content } = req.body;
if(!username || !content) {
return res.status(400).json({ success: false, error: '用户名和内容不能为空' });
}
try {
const connection = await mysql.createConnection(dbConfig);
const [result] = await connection.execute(
'INSERT INTO messages (username, content) VALUES (?, ?)',
[username, content]
);
await connection.end();
res.json({ success: true, id: result.insertId });
} catch(err) {
res.status(500).json({ success: false, error: err.message });
}
});
// DELETE /api/messages/:id - 删除留言
app.delete('/api/messages/:id', async (req, res) => {
const id = req.params.id;
try {
const connection = await mysql.createConnection(dbConfig);
await connection.execute('DELETE FROM messages WHERE id = ?', [id]);
await connection.end();
res.json({ success: true });
} catch(err) {
res.status(500).json({ success: false, error: err.message });
}
});
app.listen(PORT, () => {
console.log(`服务器运行在 http://localhost:${PORT}`);
});
7. 实战:完整的前后端留言板
创建一个完整的前端页面,调用上面的API实现增删查功能。
创建 message-board.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>留言板系统</title>
<style>
*{margin:0;padding:0;box-sizing:border-box;}
body{background:linear-gradient(135deg,#667eea,#764ba2);min-height:100vh;padding:40px;font-family:system-ui;}
.container{max-width:800px;margin:0 auto;background:#fff;border-radius:20px;padding:30px;box-shadow:0 20px 40px rgba(0,0,0,0.2);}
h1{text-align:center;color:#333;margin-bottom:30px;}
.form-group{margin-bottom:20px;}
label{display:block;margin-bottom:8px;font-weight:bold;color:#555;}
input,textarea{width:100%;padding:12px;border:1px solid #ddd;border-radius:10px;font-size:16px;}
textarea{resize:vertical;min-height:100px;}
button{background:#667eea;color:#fff;border:none;padding:12px 30px;border-radius:10px;cursor:pointer;font-size:16px;}
button:hover{background:#5a67d8;}
.message-list{margin-top:30px;}
.message-item{background:#f8f9fa;border-radius:12px;padding:15px;margin-bottom:15px;}
.message-header{display:flex;justify-content:space-between;margin-bottom:10px;color:#667eea;font-weight:bold;}
.message-date{color:#999;font-size:12px;}
.message-content{color:#333;line-height:1.6;margin-top:10px;}
.delete-btn{color:#e74c3c;text-decoration:none;cursor:pointer;font-size:12px;}
.empty{text-align:center;color:#999;padding:40px;}
.loading{text-align:center;color:#999;padding:20px;}
</style>
</head>
<body>
<div class="container">
<h1>📝 动态留言板</h1>
<div class="form-group">
<label>昵称:</label>
<input type="text" id="username" placeholder="请输入昵称">
</div>
<div class="form-group">
<label>留言内容:</label>
<textarea id="content" placeholder="请输入留言..."></textarea>
</div>
<button id="submitBtn">发表留言</button>
<div class="message-list">
<h3>最新留言</h3>
<div id="messageList"><div class="loading">加载中...</div></div>
</div>
</div>
<script>
const API_URL = 'http://localhost:3000/api/messages';
async function loadMessages() {
try {
const response = await fetch(API_URL);
const result = await response.json();
if(result.success) {
renderMessages(result.data);
}
} catch(err) {
document.getElementById('messageList').innerHTML = '<div class="empty">加载失败,请确保后端服务已启动</div>';
}
}
function renderMessages(messages) {
const container = document.getElementById('messageList');
if(messages.length === 0) {
container.innerHTML = '<div class="empty">暂无留言,抢个沙发吧!</div>';
return;
}
container.innerHTML = messages.map(msg => `
<div class="message-item">
<div class="message-header">
<span>👤 ${escapeHtml(msg.username)}</span>
<div>
<span class="message-date">${msg.created_at}</span>
<a class="delete-btn" onclick="deleteMessage(${msg.id})">删除</a>
</div>
</div>
<div class="message-content">${escapeHtml(msg.content).replace(/\n/g, '<br>')}</div>
</div>
`).join('');
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
async function addMessage() {
const username = document.getElementById('username').value.trim();
const content = document.getElementById('content').value.trim();
if(!username || !content) {
alert('请填写昵称和留言内容');
return;
}
try {
const response = await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, content })
});
const result = await response.json();
if(result.success) {
document.getElementById('username').value = '';
document.getElementById('content').value = '';
loadMessages();
} else {
alert('发表失败:' + result.error);
}
} catch(err) {
alert('请求失败');
}
}
async function deleteMessage(id) {
if(!confirm('确定删除这条留言吗?')) return;
try {
const response = await fetch(`${API_URL}/${id}`, { method: 'DELETE' });
const result = await response.json();
if(result.success) {
loadMessages();
}
} catch(err) {
alert('删除失败');
}
}
document.getElementById('submitBtn').onclick = addMessage;
loadMessages();
</script>
</body>
</html>
8. 进阶概念与最佳实践
8.1 环境变量管理
敏感信息(数据库密码、API密钥)不应写死在代码中。安装dotenv:npm install dotenv,创建 .env 文件:
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=
DB_NAME=web_advanced
8.2 中间件概念
Express中间件可以处理请求日志、身份验证、错误处理等。
// 日志中间件
app.use((req, res, next) => {
console.log(`${req.method} ${req.path} - ${new Date().toISOString()}`);
next();
});
// 错误处理中间件(放在所有路由之后)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: '服务器内部错误' });
});
8.3 跨域问题详解
前后端端口不同时会触发跨域。我们使用了cors包解决。生产环境可以配置具体的允许域名:
app.use(cors({
origin: 'http://your-frontend-domain.com',
credentials: true
}));
8.4 常用的HTTP状态码
- 200 OK:成功
- 201 Created:创建成功
- 400 Bad Request:请求参数错误
- 401 Unauthorized:未认证
- 403 Forbidden:无权限
- 404 Not Found:资源不存在
- 500 Internal Server Error:服务器错误
9. 总结与学习资源
恭喜你完成了Web开发进阶教程!你已经掌握了:
✅ 前后端分离架构和API设计
✅ Node.js + Express搭建后端服务
✅ Fetch API调用后端接口
✅ MySQL数据库连接与操作
✅ 完整的留言板系统开发
下一步学习方向:
🔹 前端框架:React/Vue/Angular
🔹 数据库进阶:联表查询、索引优化、事务处理
🔹 身份认证:JWT、Session、OAuth2.0
🔹 项目部署:Nginx反向代理、PM2进程管理、Docker容器化
🔹 全栈框架:Next.js、Nuxt.js
推荐资源:
- Express官方文档:expressjs.com
- MDN Fetch API文档
- Node.js最佳实践:github.com/goldbergyoni/nodebestpractices
- B站搜索:"Node.js零基础教程"、"前后端分离项目实战"
版权声明:本文为原创教程,欢迎分享转发。