# 端口与 localhost > 💡 **学习指南**:当你执行 `npm run dev`,终端里出现 `http://localhost:5173` 时,你有没有想过:`localhost` 是什么?`5173` 又代表什么?为什么有时候会报 `EADDRINUSE` 错误?本章就来把这些日常开发中天天见、却很少深究的概念一次讲透。 在开始之前,建议你先补两块"基础砖": - **网络基础**:如果你不太清楚 IP 地址和 HTTP 的概念,可以先看 [计算机基础 - 网络通信](../1-computer-fundamentals/network-fundamentals.md) 部分。 - **终端基础**:如果你还不熟悉终端命令行,可以先看 [命令行与 Shell 脚本](./command-line-shell.md)。 --- ## 0. 引言:那个天天见的 `localhost:5173` 到底是什么? 每个开发者的日常都离不开这一行输出: ``` ➜ Local: http://localhost:5173/ ``` 但你有没有想过,这短短一行字里,藏着好几个关键概念: - **http://** → 通信协议(用什么语言对话) - **localhost** → 目标地址(找谁) - **:5173** → 端口号(找到之后,敲哪扇门) 搞懂这三件事,你就能理解 90% 的开发环境网络问题。接下来我们逐个拆解。 --- ## 1. 什么是端口?(IP 是大楼,端口是房间号) ### 1.1 一个直觉比喻 想象一台服务器是一栋大楼: - **IP 地址**(如 `192.168.1.100`)就是大楼的门牌地址——告诉你"去哪栋楼"。 - **端口号**(如 `:80`)就是楼里的房间号——告诉你"进哪间房"。 一栋楼里可以同时有餐厅(80 号房)、咖啡厅(443 号房)、办公室(22 号房)。同理,一台电脑上可以同时运行 Web 服务器、数据库、SSH 服务,各自占用不同的端口。 👇 **动手点点看**: 点击下面的"房间门牌",模拟向不同端口发起连接。注意观察:当端口"开着"(有程序在监听)和"关着"时,分别会发生什么? ### 1.2 端口号的取值范围 端口号是一个 **0–65535** 之间的整数(共 65536 个)。这么多端口被分为三个区间: | 区间 | 范围 | 用途 | 举例 | | :--- | :--- | :--- | :--- | | **系统端口** | 0 – 1023 | 预留给标准协议,普通用户不能随意占用 | 80 (HTTP)、443 (HTTPS)、22 (SSH) | | **注册端口** | 1024 – 49151 | 给常见应用注册使用 | 3306 (MySQL)、5432 (PostgreSQL)、6379 (Redis) | | **动态端口** | 49152 – 65535 | 操作系统临时分配 | 浏览器发请求时,系统随机分配一个源端口 | > 为什么你的开发服务器喜欢用 3000、5173、8080?因为这些都在"注册端口"范围内,不需要管理员权限就能监听,又不太容易和系统服务冲突。 ### 1.3 开发中常见的端口号速查 👇 **动手点点看**: 输入端口号或服务名搜索,点击任意一行可以展开查看使用示例。 --- ## 2. 什么是 localhost?(自己找自己) ### 2.1 "环回"的核心概念 `localhost` 是一个特殊的域名,它永远指向**你自己这台电脑**。 当你在浏览器输入 `http://localhost:3000` 时,发生了这些事: 1. 浏览器问操作系统:"`localhost` 的 IP 是多少?" 2. 操作系统直接回答:"`127.0.0.1`"(不需要联网查 DNS) 3. 数据包发往 `127.0.0.1`,但**不会真的离开本机** 4. 操作系统通过"环回接口(loopback interface)"把数据包**折返**回来 5. 监听在 3000 端口上的程序收到请求,返回响应 **整个过程不经过网线、不经过路由器、不需要联网。** 👇 **动手点点看**: 点击"发送请求",观察数据包的完整旅程。然后点击下方的"马甲卡片",了解 localhost 的几种写法和区别。 ### 2.2 `localhost` vs `127.0.0.1` vs `0.0.0.0` 这三个概念经常被混淆,但它们的含义完全不同: | 写法 | 含义 | 谁能访问 | | :--- | :--- | :--- | | `localhost` / `127.0.0.1` | 环回地址,仅本机 | 只有你自己的电脑 | | `0.0.0.0` | 监听所有网络接口 | 本机 + 局域网内其他设备 | | `192.168.x.x` | 局域网 IP | 局域网内的设备 | **实际场景**: ```bash # 只有自己能访问(安全,适合开发) npm run dev -- --host localhost # 手机也能访问(适合移动端调试) npm run dev -- --host 0.0.0.0 ``` > 很多框架(如 Vite、Next.js)默认监听 `localhost`,所以你的手机即使连着同一个 WiFi 也访问不了。想用手机调试?加上 `--host` 参数就行。 --- ## 3. 端口冲突:最常见的开发环境问题 ### 3.1 为什么会冲突? **一个端口同一时刻只能被一个程序监听。** 这就像一个房间只能住一户人家。 如果你尝试启动第二个服务在同一个端口上,就会看到这个经典错误: ``` Error: listen EADDRINUSE :::3000 ``` 翻译成人话就是:**"3000 号房已经有人住了,你进不去!"** 常见的冲突场景: - 上次的开发服务器没关干净,还在后台运行 - 两个不同的项目用了相同的默认端口 - 某个系统服务已经占用了你想要的端口 👇 **动手点点看**: 试着在下面的模拟器里多次启动服务。当端口冲突时,对比"直接启动"和"智能启动"的不同处理方式。 ### 3.2 排查与解决 遇到端口冲突时,排查流程非常固定: **macOS / Linux:** ```bash # 第一步:查看谁在占用 3000 端口 lsof -i :3000 # 第二步:拿到 PID 后,强制终止 kill -9 ``` **Windows:** ```bash # 第一步:查看谁在占用 3000 端口 netstat -ano | findstr :3000 # 第二步:终止进程 taskkill /PID /F ``` > 很多现代框架(Vite、Create React App 等)遇到端口冲突时会自动询问"是否换一个端口?"。但了解底层原理,能帮你更快地排查那些框架帮不了你的疑难杂症。 --- ## 4. 开发中的"同源策略"与跨域 ### 4.1 什么是"源"? 浏览器有一个安全机制叫做**同源策略(Same-Origin Policy)**:只有**协议、域名、端口**三者完全一致,才算"同源"。 | 地址 A | 地址 B | 是否同源 | 原因 | | :--- | :--- | :--- | :--- | | `http://localhost:5173` | `http://localhost:5173/about` | ✅ 同源 | 协议、域名、端口都一样 | | `http://localhost:5173` | `http://localhost:3000` | ❌ 不同源 | **端口不同**(5173 vs 3000) | | `http://localhost:5173` | `https://localhost:5173` | ❌ 不同源 | **协议不同**(http vs https) | ### 4.2 为什么前后端分离必然遇到跨域? 当你的项目架构是: ``` 前端 (Vite) → http://localhost:5173 后端 (Express) → http://localhost:3000 ``` 前端页面从 `:5173` 加载,然后用 `fetch('/api/users')` 去请求 `:3000` 的接口——**端口不一样,触发跨域限制!** **两种常见解决方案:** **方案一:后端配置 CORS** ```javascript // Express 后端 app.use(cors({ origin: 'http://localhost:5173' })) ``` **方案二:前端配置代理(推荐)** ```javascript // vite.config.js export default { server: { proxy: { '/api': 'http://localhost:3000' } } } ``` 代理的原理:让 Vite 开发服务器帮你"转发"请求。浏览器以为自己在和 `:5173` 通信(同源),实际上 Vite 在背后偷偷帮你把请求转给了 `:3000`。 --- ## 5. 实战排查:三个最常见的问题 👇 **动手点点看**: 选择一个你遇到过的问题,跟着步骤一起排查。每一步都可以点击"执行"查看输出。 --- ## 6. 名词对照表 | 英文术语 | 中文对照 | 解释 | | :--- | :--- | :--- | | **Port** | 端口 | 一个 0–65535 的数字,用来区分同一台机器上的不同网络服务。每个服务"监听"一个端口,等待客户端连接。 | | **localhost** | 本地主机 | 一个特殊域名,永远指向本机(127.0.0.1)。用于在不联网的情况下访问本机上运行的服务。 | | **Loopback Interface** | 环回接口 | 操作系统的虚拟网络接口。发往 127.0.0.1 的数据包不会离开本机,而是通过该接口"折返"回来。 | | **EADDRINUSE** | 地址已被使用 | Node.js / 操作系统报的错误,表示你要监听的端口已经被另一个程序占用了。 | | **CORS** | 跨域资源共享 | 浏览器安全机制。当前端页面尝试请求不同源(协议/域名/端口不同)的接口时,需要后端明确许可。 | | **Same-Origin Policy** | 同源策略 | 浏览器的安全基石:只允许同协议、同域名、同端口的请求自由通信,阻止跨域的数据读取。 | | **Proxy** | 代理 | 在开发环境中,代理服务器代替浏览器向后端转发请求,绕过浏览器的同源限制。 | | **0.0.0.0** | 所有接口 | 当服务监听 0.0.0.0 时,表示它接受来自任何网络接口(本机、局域网等)的连接。 | | **Well-known Ports** | 知名端口 | 0–1023 端口的统称,预留给 HTTP (80)、HTTPS (443)、SSH (22) 等标准协议。 | | **PID** | 进程 ID | 操作系统为每个运行中的程序分配的唯一编号,用于管理和终止进程。 | | **lsof** | 列出打开的文件 | macOS/Linux 命令,用于查看哪个进程占用了某个端口(`lsof -i :端口号`)。 | | **HMR** | 热模块替换 | 开发服务器的功能:你修改代码后,浏览器自动更新,无需手动刷新页面。底层通过 WebSocket 通知浏览器。 | --- ## 总结 端口和 localhost 是开发环境中最基础、最高频的概念: - **端口** = 一台机器上区分不同服务的"门牌号"(0–65535) - **localhost** = "自己找自己"的特殊地址(127.0.0.1),数据不出本机 - **端口冲突**的本质是"一个门牌只能挂一块牌子" - **跨域**的本质是"端口不同 = 不同源",需要 CORS 或代理来解决 记住这四句话,你在开发环境里遇到的大多数网络问题,都能快速定位原因。