一个端口两个服务:从 IPv6 双栈到 AI 编辑器的隐形代理

0 阅读10分钟

一个端口两个服务:从 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:5173IPv4残留的旧版后台服务
[::1]:5173IPv6当前启动的新版开发服务

这就是问题的核心:

日常开发中默认等效的 localhost127.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 沙箱,看不见的幽灵终端

到这里问题已经够隐蔽了,但更坑的是:那个残留的旧服务,我根本看不到它在哪运行。

本地开发服务端口复盘:localhost 与 127.0.0.1 不等效问题分析与解决方案.png

回查记录发现,旧服务对应的启动命令是:

cd "tree-graph-vue" && npm run dev -- --host 127.0.0.1

这种带 commandstarted_atrunning_for_ms 的记录,很像 AI 编辑器(如 Cursor)为了预览或验证启动的后台进程

关键问题:

  • Cursor 的沙箱终端是独立环境,启动的服务进程在后台运行
  • 你在 IDE 里看不到这个终端窗口
  • 关掉 Cursor 窗口,进程可能还在跑
  • 重启电脑之前,它一直占用着端口

这就是为什么排查过程极度痛苦:

  1. 我看不到终端,以为服务已经关了
  2. 但 IPv4 端口还被占着
  3. 新服务绑定 IPv6 启动成功,不报错
  4. 我访问 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.1127.0.0.2localhost 都是本地回环地址,随便用哪个都一样。

实际情况: 在双栈环境下,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,而是前端开发的通用隐藏陷阱:

  1. localhost ≠ 127.0.0.1:双栈环境下解析地址完全不同
  2. 端口号仅在单协议内唯一:IPv4 和 IPv6 可共用同一个端口
  3. 脚手架端口检测只校验单地址、单协议:跨协议占用完全检测不到
  4. 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 帮我们封装一切,却忽略了:

  • localhost127.0.0.1 不是同一回事
  • IPv4 和 IPv6 可以同端口共存
  • AI 编辑器的沙箱进程可能在你不知情的情况下后台运行

下次遇到"代码改了但不生效"的诡异问题,别死磕业务代码,先看看端口和网络底层。


你在开发中遇到过类似的"玄学问题"吗?是缓存、端口、还是 AI 编辑器沙箱?

欢迎评论区分享你的踩坑经历 👇