在现代 Web 开发中,存储 无处不在:
前端需要在浏览器里存储数据,后端需要把数据长期保存,客户端和服务端之间还要通过网络把数据安全、高效地传输。
这篇文章就结合一个最简单的 Node.js 原生 HTTP 服务器示例,带你串一遍前后端常用的存储方案、模块化演进、端口与路由等核心知识点,顺便看看底层是怎么跑起来的。
📦 1. 前端存储:从 Cookie 到 IndexDB
1.1 Cookie
Cookie 是最老牌的浏览器存储方案,浏览器会在每次请求时自动带上它。常见用途:
- 登录状态:比如存 session ID 或 token,让后端识别用户身份。
- 购物车信息:如果网站没有登录功能,也可以先把购物车信息放在 Cookie 中。
特点:
- 每次请求都会带上,体积要尽量小(限制 4KB)。
- 有过期时间和作用域。
1.2 localStorage 与 sessionStorage
这俩是更现代的浏览器存储方案,都不会随请求自动发送,纯本地保存。
- localStorage:持久化存储,除非用户主动清理,否则一直存在。
- sessionStorage:生命周期只在当前标签页或窗口内,有效期更短。
简单用法:
localStorage.setItem('key', 'value');
const val = localStorage.getItem('key');
1.3 IndexDB
当需要在浏览器里存储更大、结构化的数据时,IndexDB 更合适。
它支持事务、索引查询和异步操作,常用于离线应用、缓存大文件或前端搜索等场景。
🗄️ 2. 后端存储:从关系型到非关系型
后端要处理持久化、事务、并发和可扩展性,常见选型包括:
- MySQL:典型的关系型数据库,表结构清晰,事务支持好,适合对一致性要求高的场景。
- NoSQL:如 MongoDB(文档型)、Redis(键值对),读写性能高,适合存非结构化或高并发的热点数据。
例如:用户表、订单表通常放 MySQL;缓存、Session 可以放 Redis。
⚡ 3. 缓存
缓存无处不在:
- 前端:浏览器自带的强缓存、协商缓存,或者 Service Worker 做离线缓存。
- 后端:Redis、Memcached 等把热点数据放内存,减少数据库压力。
合理使用缓存能极大提高网站的访问速度和并发能力。
🧩 4. Node.js 的模块化与原生 HTTP 服务实践
4.1 模块化
JavaScript 的模块化方案主要有两种:
-
CommonJS(require) :Node.js 早期标准,语法简单,使用广泛。
const http = require('http');
-
ES6 Module(import) :ECMAScript 标准,自带模块化能力,支持更好的静态分析和 Tree Shaking。
import http from 'http';
Node.js 从 12 版本后就逐步支持了 ES6 模块化(通过
.mjs
后缀或在package.json
中声明"type": "module"
)。
社区也在逐步从require
迁移到import
,require
未来可能会淡出。
4.2 原生 HTTP 服务示例 + 详细解析
下面是一份最小可用的 Node.js 原生 HTTP 静态资源服务器示例,包含路由、读文件、正确设置响应头等关键点。
📄 完整示例
const http = require('http');
const fs = require('fs'); // 文件操作模块
const path = require('path'); // 路径操作模块
const server = http.createServer((req, res) => {
// 根据请求 Method + URL 路由
if (req.method === 'GET' && (req.url === '/' || req.url === '/index.html')) {
fs.readFile(path.join(__dirname, 'public', 'index.html'), (err, content) => {
if (err) {
res.writeHead(500);
res.end('Server error');
return;
}
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(content);
});
} else if (req.method === 'GET' && req.url === '/style.css') {
fs.readFile(path.join(__dirname, 'public', 'style.css'), (err, content) => {
if (err) {
res.writeHead(500);
res.end('Server error');
return;
}
res.writeHead(200, { 'Content-Type': 'text/css' });
res.end(content);
});
} else if (req.method === 'GET' && req.url === '/script.js') {
fs.readFile(path.join(__dirname, 'public', 'script.js'), (err, content) => {
if (err) {
res.writeHead(500);
res.end('Server error');
return;
}
res.writeHead(200, { 'Content-Type': 'text/javascript' });
res.end(content);
});
} else {
res.writeHead(404);
res.end('Not Found');
}
});
server.listen(8080, () => {
console.log('Server is running on http://localhost:8080');
});
✅ 逐步解析
1) 核心模块
http
:创建服务器fs
:读取静态文件path
:拼接路径,保证跨平台兼容
2) 创建服务
const server = http.createServer((req, res) => {
// 每次请求都会进入这个回调
});
req
:请求信息(方法、路径、头信息)res
:响应对象,用来返回数据
3) 路由匹配
GET /
或/index.html
:读取并返回首页 HTMLGET /style.css
:读取并返回 CSSGET /script.js
:读取并返回 JS- 其他:404
每个路由都做了:
- 用
fs.readFile
异步读取文件内容 - 设置合适的
Content-Type
- 错误处理:如果文件读不到返回 500,找不到路由返回 404
4) 响应头的作用
Content-Type
决定浏览器如何解析返回的文件:
- HTML:
text/html
- CSS:
text/css
- JS:
text/javascript
如果少了或写错了,浏览器可能当成纯文本处理,页面就会显示异常。
5) 启动服务器
server.listen(8080, () => {
console.log('Server is running on http://localhost:8080');
});
8080
是常见的开发环境端口,避免跟常用服务(如 80/3306/6379)冲突。localhost
会解析成127.0.0.1
,只在本机可访问。
🌐 4.3 前端示例文件
在 public
目录下,放上简单的前端文件:
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Storage</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<h1>Cookie</h1>
<script src="/script.js"></script>
</body>
</html>
/* public/style.css */
* {
margin: 0;
padding: 0;
}
body {
background-color: green;
height: 100vh;
}
// public/script.js
console.log('智能前端,智能后端,笑傲秋招');
🌍 5. 域名、IP 和端口
顺带复习下基础概念:
- 域名(如 localhost) :解析成 IP(如 127.0.0.1)
- 端口(如 8080) :同一台机器上的不同服务/进程通过不同端口区分
- 访问 URL 完整结构:
http://域名:端口/路径?查询参数
这些都是前端与后端打交道时必备的底层知识,理解了这些,写更复杂的全栈应用、面试原理题都会轻松很多。