Node.js 后端服务核心解析:从模块化到端口映射

0 阅读9分钟

一、核心能力解析

  1. 双模块化方案

    • CommonJS (require):Node 早期标准,同步加载(示例中http/fs模块引入)
    • ES6 Modules (import):现代方案,异步加载(注释中强调其先进性)
    • 进化意义requireimport 是 Node 向浏览器规范靠拢的标志性升级
  2. 服务定位三要素

    graph LR
    A[域名 localhost] --> B[IP 127.0.0.1]
    B --> C[端口 8080]
    C --> D[服务进程]
    
    • 端口:服务入口(如 MySQL:3306,HTTP:80/443)
    • 进程:资源分配单元(服务容器)
    • 线程:执行单元(处理并发请求)


二、关键设计思想

  1. 轻量化定位

    • 聚焦中小项目场景(对比 Java/PHP 重型框架)
    • 单线程事件循环 → 高并发 I/O 处理能力
  2. 端口管理原则

    端口类型示例使用建议
    知名端口80/443避免占用
    注册端口3306服务默认端口
    动态端口8080开发首选
    避坑指南1234/8080等常用端口需检测占用


三、代码映射网络架构

// 分层架构实现
const http = require("http");    // 网络层:TCP/IP协议封装
const fs = require("fs");        // 系统层:文件读写
const path = require("path");    // 工具层:路径处理

// 核心服务逻辑:将物理路径映射为网络资源
fs.readFile(path.join(__dirname, "public", "index.html"), (err, content) => {
  res.end(content); // 文件内容 → HTTP响应体
});

关键洞察

  • localhost:8080/style.css → 磁盘文件路径映射
  • 内容协商:Content-Type头决定资源解析方式(HTML/CSS/JS)

工程启示:Node.js 用 200 行代码揭示了 Web 服务的本质——通过端口映射,将文件系统转化为网络资源。理解端口-进程-线程的关系,是掌握后端开发的基石。



四、两种http模块化对比

这段代码是基于Node.js实现的后端代码。Node.js作为 JavaScript的运行环境,让我们能够在命令行中运行 JavaScript代码。在模块化方面,Node.js早期采用的是CommonJS规范,使用 require 来实现模块导入;而 ES6 则带来了更为先进的 import 模块化方案。

const http = require('http');
const fs = require('fs'); // file system
const path = require('path'); // 路径模块 提供一些路径相关的方法

const server = http.createServer((req,res)=>{
  // res.end('hello http server')
  // http 基于请求响应的协议
  if(req.method == 'GET' && req.url == '/'){
    fs.readFile(path.join(__dirname,'public','index.html'),
    (err,content)=>{
      if(err){
        res.writeHead(500); // 5XX 服务器错误
        res.end('Server error');
        return;
      }
      res.end('hello http server');
    })
  }
});

server.listen(8080);

可以看到页面实现的效果

image.png

现在让我们来看看es6 module 更先进的 mjs

import http from 'http';

const server = http.createServer((req,res)=>{
  res.end('hello World')
})

server.listen(1314);

让我们看下这段代码实现的效果,发现效果跟使用require关键字的作用相同,但是代码少了一大段❗

image.png


五、 创建静态的服务端口

// 引入 Node.js 的 http 模块,用于创建 HTTP 服务器
const http = require("http");
// 引入 Node.js 的 fs 模块,用于文件系统操作
const fs = require("fs"); // file system
// 引入 Node.js 的 path 模块,用于处理和转换文件路径
const path = require("path"); // 路径模块 提供一些路径相关的方法

// 创建一个 HTTP 服务器实例,传入请求处理函数
const server = http.createServer((req, res) => {
  // 注释掉的代码,原本用于直接返回响应内容
  // res.end('hello http server')
  // 说明 HTTP 是基于请求 - 响应的协议
  // http 基于请求响应的协议
  // 解释路由的概念,Method 和 url 组合可以定位服务器端的资源
  // 路由 Method + url 定位了服务器端的资源
  // 强调路由的作用是为了获取资源
  // 为了资源
  // 判断请求方法是否为 GET,并且请求路径是根路径或 /index.html
  if (req.method == "GET" && (req.url == "/" || req.url == "/index.html")) {
    // 打印当前文件所在目录和要读取的 index.html 文件的完整路径
    console.log(__dirname, path.join(__dirname, "public", "index.html"));

    // 异步读取 public 目录下的 index.html 文件
    fs.readFile(
      path.join(__dirname, "public", "index.html"),
      // 异步操作的回调函数,处理读取结果
      // 异步 callback
      (err, content) => {
        // 前端开发通常更注重用户体验,后端开发更注重稳定性
        // 前端体验为主
        // 后端稳定为主
        // 如果读取文件过程中出现错误
        if (err) {
          // 设置响应状态码为 500,表示服务器内部错误
          res.writeHead(500); // 5XX 服务器错误
          // 返回错误信息给客户端
          res.end("Server error");
          // 结束当前函数执行,避免后续代码继续执行
          return;
        }
        // 说明响应内容不仅可以是 HTML,还可以是 CSS、JS、JPG 等格式
        // 不只是html, css, js, jpg
        // 设置响应状态码为 200,表示请求成功,并指定响应内容类型为 HTML
        res.writeHead(200, {
          "Content-Type": "text/html",
        });
        // 将读取到的文件内容作为响应返回给客户端
        res.end(content);
      }
    );
  }
  // 说明这部分代码是后端路由,用于暴露服务器资源
  // 后端路由, 暴露资源
  // 举例说明 URL 的组成部分,包括协议、域名、端口、路径和查询字符串
  //localhost:8080/style.css?a=1&b=2
  // 协议 http:// localhost 域名 端口 /style.css path queryString
  // 判断请求方法是否为 GET,并且请求路径是 /style.css
  if (req.method == "GET" && req.url == "/style.css") {
    // 异步读取 public 目录下的 style.css 文件
    fs.readFile(path.join(__dirname, "public", "style.css"), (err, content) => {
      // 如果读取文件过程中出现错误
      if (err) {
        // 设置响应状态码为 500,表示服务器内部错误
        res.writeHead(500);
        // 返回错误信息给客户端
        res.end("Server error");
        // 结束当前函数执行,避免后续代码继续执行
        return;
      }
      // 设置响应状态码为 200,表示请求成功,并指定响应内容类型为 CSS
      res.writeHead(200, { "Content-Type": "text/css" });
      // 将读取到的文件内容作为响应返回给客户端
      res.end(content);
    });
    // 结束当前请求处理,避免后续代码继续执行
    return;
  }

  // 判断请求方法是否为 GET,并且请求路径是 /script.js
  if (req.method == "GET" && req.url == "/script.js") {
    // 异步读取 public 目录下的 script.js 文件
    fs.readFile(path.join(__dirname, "public", "script.js"), (err, content) => {
      // 如果读取文件过程中出现错误
      if (err) {
        // 设置响应状态码为 500,表示服务器内部错误
        res.writeHead(500);
        // 返回错误信息给客户端
        res.end("Server error");
        // 结束当前函数执行,避免后续代码继续执行
        return;
      }
      // 设置响应状态码为 200,表示请求成功,并指定响应内容类型为 JavaScript
      res.writeHead(200, { "Content-Type": "text/javascript" });
      // 将读取到的文件内容作为响应返回给客户端
      res.end(content);
    });
    // 结束当前请求处理,避免后续代码继续执行
    return;
  }
});

// 启动服务器,监听 8080 端口
server.listen(8080);

当我们在JS端使代码跑起来之后可以看到效果如下:

image.png

在一个静态网站项目中,服务器需要将 public 目录下的 index.html 作为首页返回给用户。这段代码就可以部署在服务器上,当用户访问网站时,服务器就能正确返回首页内容。若 index.html 文件缺失,服务器也会返回相应的错误信息,方便开发者排查问题。



六、 痛点切入:

面试中常问的 Cookie 和 LocalStorage 区别主要体现在以下几个方面:

  1. 存储容量:Cookie 每个大小通常受限制,一般为几 KB,浏览器对每个域名下的 Cookie 总大小也有限制;而 LocalStorage 容量通常较大,一般为 5MB 或更多 1 3 4 5。
  2. 生命周期:Cookie 可以设置过期时间,有会话 Cookie(浏览器关闭后自动删除)和持久性 Cookie(在指定过期时间前一直有效);LocalStorage 数据永久存储,除非用户手动清除或网站代码清除 1 4 5。
  3. 数据共享:Cookie 通过设置 domainpath 可以实现不同页面之间的共享,但受同源策略限制;LocalStorage 存储在同源(相同协议、域名和端口)的所有窗口和标签页之间共享 1。
  4. 安全性:Cookie 在 HTTP 请求中自动发送到服务器,存在被窃取的风险,可以设置 HttpOnlySecure 属性增加安全性;LocalStorage 存储在客户端,相对较安全,不会在 HTTP 请求中自动发送到服务器,但仍然可能受到 XSS 攻击 1 4。
  5. 与服务器通信:Cookie 会在浏览器和服务器间来回传递,而 LocalStorage 仅在本地保存,不会自动把数据发给服务器 3 5。
  6. API 操作:LocalStorage 使用 Web Storage API,提供了 setItemgetItemremoveItem 等方法来操作数据;Cookie 通过 document.cookie 属性进行操作,也可以使用辅助函数设置,操作相对复杂 1 4。
  7. 应用场景:Cookie 主要用于在客户端和服务器之间传递数据,或者存储少量的用户会话信息,如用户身份验证、登录状态等;LocalStorage 适用于大容量数据的存储和持久化需求,比如保存用户偏好设置、离线缓存数据等 1 2。

总体而言,Cookie 更适合处理需要与服务器交互的少量数据,而 LocalStorage 更适合本地大容量数据的持久化存储。



七、核心内容架构

Part 1:前端存储体系剖析

# 🌐 前端四大存储方案
1. **Cookie**  
   - 核心特性:4KB限制、自动携带在HTTP头  
   - 应用场景:登录态Token、购物车ID存储  
   - 安全风险:XSS攻击与`HttpOnly`解决方案  

2. **Web Storage**  
   - `localStorage`:持久化存储(如用户主题配置)  
   - `sessionStorage`:会话级存储(表单草稿临时保存)  
   - 对比:5MB容量 vs Cookie的4KB  

3. **IndexedDB**  
   - 前端NoSQL数据库:支持事务、索引查询  
   - 适用场景:离线应用(如PWA的本地数据缓存)  

Part 2:Node.js服务端存储实战

### ⚙️ 手写静态资源服务器
```javascript
// 关键代码:路由映射+文件读取
if (req.method === "GET" && req.url === "/style.css") {
  fs.readFile("public/style.css", (err, content) => {
    res.writeHead(200, { "Content-Type": "text/css" });
    res.end(content); // 返回CSS文件内容
  });
}
  • 三大核心模块
    http创建服务 + fs读取文件 + path解析路径
  • 性能优化:
    createReadStream()流式传输大文件(对比readFile内存占用)

**Part 3:前后端存储协作模式**  
```markdown
### 🔗 数据流动实战案例
| 技术          | 前端角色          | 后端角色          |
|---------------|------------------|------------------|
| **Cookie**    | 读取`document.cookie` | Set-Cookie响应头  |
| **LocalStorage| 长期存储用户配置  | 无需感知         |
| **MySQL**     | 发起API请求       | 持久化核心数据   |



八、总结升华

  • 核心观点
    “存储是数据生命周期的起点,前端需根据时效性/安全性选择方案,后端需保证资源稳定交付。”
    “从Cookie到IndexedDB,是前端从简单到复杂的存储能力进化;
    readFile到流处理,是后端对性能边界的不断突破。”