一个端口两个服务:从 IPv6 双栈到 AI 编辑器的隐形代理
⚠️ 警告:你可能正在踩这个坑
代码已保存、服务已重启、页面无报错——但就是永远加载不到最新代码。
这不是缓存、不是 HMR 失效、不是配置错误,而是一个藏在网络底层 + AI 编辑器沙箱里的双重陷阱。
一小时的崩溃排查
晚上 10 点,本来只是给组件加个拖拽功能。
代码写完,保存,刷新页面——没反应。
"可能是 HMR 没触发?" 手动刷新。还是旧的。
"缓存问题?" 清空浏览器缓存。还是旧的。
"Vite 挂了?" 重启服务。还是旧的。
"是不是我代码写错了?" 逐行检查,加日志,改页面标题——连 App.vue 根组件的内容都改了,刷新后依然是旧页面。
"难道是 Vite 出 Bug 了?还是我本地环境坏了?"
无痕模式、换浏览器、删 node_modules 重装——一轮操作下来,半小时过去了,页面纹丝不动。
我开始怀疑人生:代码明明改了,它凭什么不生效?
诡异的现象
近期在开发一个树形关系图 Vue3 组件,需要为画布新增拖拽移动、滚轮缩放、鼠标样式适配等交互功能。
满心欢喜写完逻辑:新增缩放控制代码、绑定鼠标按下事件、添加拖拽光标样式,甚至修改了根组件 App.vue 的页面标题,用来快速校验代码是否正常更新。
诡异的现象就此发生:
- 本地服务正常运行,页面刷新毫无报错
- 但所有代码变更完全无效
- 组件内新增的缩放逻辑不触发
- 鼠标事件日志控制台空白
grabbing拖拽指针样式毫无反应
为了排查问题,不断缩小范围:从业务子组件 TreeGraphView.vue 加日志,逐级往上排查页面调用处,一路追到顶层 App.vue,直接修改页面标题、改动根模板内容。
常规操作全部试了一遍:
- 手动刷新页面
- 强制清空缓存
- 重启 Vite 开发服务
- 关闭浏览器重开
- 切换无痕隐私模式
所有手段全部失效,页面依旧顽固展示旧版本代码,新增功能彻底消失。
明明代码已经保存、服务正常启动、终端无任何报错,就是永远加载不到最新代码。
第一层真相:IPv4/IPv6 双栈同端口共存
排除了语法错误、缓存拦截、HMR 故障、项目配置问题,最终聚焦到本地端口上。
通过端口监听命令排查本机 5173 端口:
$ lsof -nP -iTCP:5173 | grep LISTEN
node 12345 user 23u IPv4 0x... TCP 127.0.0.1:5173 (LISTEN)
node 67890 user 24u IPv6 0x... TCP [::1]:5173 (LISTEN)
颠覆认知的细节出现了:同一个 5173 端口,竟然同时存在两个独立的 Vite 服务监听:
| 地址 | 协议 | 服务 |
|---|---|---|
| 127.0.0.1:5173 | IPv4 | 残留的旧版后台服务 |
| [::1]:5173 | IPv6 | 当前启动的新版开发服务 |
这就是问题的核心:
日常开发中默认等效的 localhost 和 127.0.0.1,本质并不是同一个地址。
- macOS、Win11 等现代系统中,
localhost会优先解析为 IPv6 回环地址::1 127.0.0.1是固定 IPv4 回环地址
早期启动项目时,曾手动指定 --host 127.0.0.1 启动服务,强制绑定 IPv4 地址;后续直接执行默认 npm run dev 启动项目,Vite 会跟随系统规则,默认监听 IPv6 地址。
关键问题来了:Vite、Vue CLI 等构建工具的端口检测机制,存在天然缺陷。
工具进行端口占用校验时,只会检测「当前即将绑定的单一协议地址」:
- 绑定 IPv6 就只检测
::1:端口,完全不会扫描 IPv4 的127.0.0.1:端口 - IPv4 与 IPv6 属于两套完全独立的寻址空间,同端口下互不冲突、互不感知
这就导致:旧 IPv4 服务占用 5173 端口常驻后台,新 IPv6 服务无视占用,直接同端口启动,端口冲突检测彻底失效。
两个服务安静共存于同一个端口,却互不干扰:
| 访问地址 | 命中服务 | 代码状态 |
|---|---|---|
127.0.0.1:5173 | 残留旧版服务 | 所有修改无效 |
localhost:5173 | 全新新版服务 | 代码正常更新 |
而我开发时习惯性输入 127.0.0.1 访问页面,全程都在访问早已被遗忘的幽灵旧服务,无论怎么改代码、重启服务,自然永远不会生效。
第二层真相:Cursor 沙箱,看不见的幽灵终端
到这里问题已经够隐蔽了,但更坑的是:那个残留的旧服务,我根本看不到它在哪运行。
回查记录发现,旧服务对应的启动命令是:
cd "tree-graph-vue" && npm run dev -- --host 127.0.0.1
这种带 command、started_at、running_for_ms 的记录,很像 AI 编辑器(如 Cursor)为了预览或验证启动的后台进程。
关键问题:
- Cursor 的沙箱终端是独立环境,启动的服务进程在后台运行
- 你在 IDE 里看不到这个终端窗口
- 关掉 Cursor 窗口,进程可能还在跑
- 重启电脑之前,它一直占用着端口
这就是为什么排查过程极度痛苦:
- 我看不到终端,以为服务已经关了
- 但 IPv4 端口还被占着
- 新服务绑定 IPv6 启动成功,不报错
- 我访问
127.0.0.1,命中旧服务,代码永远是旧的
双重陷阱叠加:
- 第一层:IPv4/IPv6 双栈同端口共存,端口检测失效
- 第二层:AI 编辑器沙箱后台进程,终端不可见,排查更难
三个致命的心理预设
复盘整个排查过程,我发现真正增加难度的,不是技术本身,而是三个"我以为我知道"的认知偏差:
预设一:Vite 会兜底,端口冲突自动换端口
我以为是: Vite 检测到端口占用,会自动换一个新端口启动,终端会提示。
实际情况: Vite 的端口检测只检查当前协议的占用。IPv4 服务占用了 5173,IPv6 服务来绑定 5173 时,Vite 认为"没占用",直接启动成功,不会换端口,也不会报错。
认知偏差: 我以为"端口冲突"是跨协议的,但实际上 IPv4 和 IPv6 是两套独立的寻址空间。
预设二:启动服务的终端窗口能找到,可以手动关闭
我以为是: 既然有服务在跑,那肯定有终端窗口,我关掉就行。
实际情况: Cursor 的沙箱终端是独立环境,启动的进程在后台运行,IDE 里看不到这个窗口。关掉 Cursor 窗口,进程可能还在跑。我根本找不到终端在哪。
认知偏差: 我以为"服务进程 = 可见的终端窗口",但 AI 编辑器打破了这个等式。
预设三:127.x.x.x 和 localhost 是等效的
我以为是: 127.0.0.1、127.0.0.2、localhost 都是本地回环地址,随便用哪个都一样。
实际情况: 在双栈环境下,localhost 优先解析为 IPv6 地址 ::1,而 127.0.0.1 是固定 IPv4 地址。它们指向的是两个完全不同的网络空间,可以各自绑定独立的服务。
认知偏差: 我以为"127 家族是同一个世界",但它们其实是两扇通往不同房间的门。
这三个预设叠加,构成了一个完美的认知盲区:
- Vite 不报错 → 我以为一切正常
- 终端看不见 → 我以为没有旧服务在跑
- 127 和 localhost 等效 → 我以为访问哪个都一样
结果就是:我一直在访问一个看不见的幽灵服务,还以为是自己代码写错了。
更深层的问题:AI 编辑器的"隐形代理"
跳出这个具体问题,我发现一个更值得警惕的趋势:
在前 AI 开发时代,开发服务都是手工启动的。
你手动敲 npm run dev,终端窗口就在眼前,服务跑在哪、端口是多少、什么时候关掉,一切都在掌控之中。出问题时,扫一眼终端就知道发生了什么。
AI 编辑器改变了这个范式。
AI 可以在沙箱里后台启动服务、执行命令、跑脚本——而且不会告诉你。它的本意是帮你省事,但副作用是:它可能干了很多你没看到的事。
- 它帮你启动了 dev server,但窗口你看不见
- 它绑定了某个端口,但你不知道是哪个
- 它跑完任务走了,但进程还在后台活着
这次踩坑的根源,就是 AI 之前在沙箱里启动了一个 127.0.0.1:5173 的服务,我完全不知情。后来我手动启动新服务,绑定 IPv6,两个服务同端口共存,我访问错了地址,排查了一小时。
这不是 AI 的 Bug,而是 AI 辅助开发的新范式带来的"不可见性"问题。
我们习惯了"我做过的操作我知道",但 AI 打破了这个假设。未来这类"幽灵问题"可能会越来越多:
- AI 改了配置文件,你没注意到
- AI 装了依赖,和你的版本冲突
- AI 启动了进程,占用了端口
新的开发范式,需要新的排查意识:当你用 AI 编辑器时,"我看到的"不等于"实际发生的"。
这个坑的本质:前端开发的网络认知盲区
深挖下来,这不是 Vite 的 Bug,而是前端开发的通用隐藏陷阱:
localhost ≠ 127.0.0.1:双栈环境下解析地址完全不同- 端口号仅在单协议内唯一:IPv4 和 IPv6 可共用同一个端口
- 脚手架端口检测只校验单地址、单协议:跨协议占用完全检测不到
- AI 编辑器沙箱进程不透明:后台残留极难发现
还会衍生出连锁问题:
- CORS 跨域失败(不同协议视为不同源)
- Cookie 隔离(域名不同)
- OAuth 回调失败(redirect_uri 不匹配)
日常开发里,很多莫名其妙的问题都源于此:热更新突然失效、本地环境时好时坏、同事能跑自己报错、接口回调偶现失败,大概率都是 IPv4/IPv6 混用、地址绑定不一致导致。
快速排查 & 解决方案
第一步:检查端口占用(多协议)
# macOS/Linux
lsof -nP -iTCP:5173 | grep LISTEN
# Windows
netstat -ano | findstr :5173
如果看到同一端口有 IPv4 和 IPv6 两个监听,说明踩坑了。
第二步:清理残留进程
# 找到占用端口的进程 PID
lsof -nP -iTCP:5173 | grep LISTEN
# 杀掉进程
kill -9 <PID>
# Windows
taskkill /PID <PID> /F
第三步:固化 Vite 配置(推荐)
// vite.config.js
export default defineConfig({
server: {
host: '127.0.0.1', // 强制绑定 IPv4,避免双栈混用
strictPort: true // 端口被占用时报错,而非自动换端口
}
})
第四步:统一访问地址
- 项目配置绑定
127.0.0.1→ 访问时用127.0.0.1 - 项目配置绑定
localhost→ 访问时用localhost - 切忌混用
第五步:AI 编辑器使用习惯
- 让 AI 启动 dev server 时,明确要求它告知端口
- 排查问题时,先检查是否有沙箱后台进程
- 不确定时,直接
lsof扫一遍端口
写在最后
这次踩坑经历让我意识到:前端开发的很多"玄学问题",本质是对底层网络协议的认知盲区。
我们习惯了框架、构建工具、HMR 帮我们封装一切,却忽略了:
localhost和127.0.0.1不是同一回事- IPv4 和 IPv6 可以同端口共存
- AI 编辑器的沙箱进程可能在你不知情的情况下后台运行
下次遇到"代码改了但不生效"的诡异问题,别死磕业务代码,先看看端口和网络底层。
你在开发中遇到过类似的"玄学问题"吗?是缓存、端口、还是 AI 编辑器沙箱?
欢迎评论区分享你的踩坑经历 👇