返回

修复Vite端口冲突:composer dev意外启动旧项目?

php

修复 composer dev 启动意外项目及 Vite 端口冲突

在使用 Laravel 和 Vite 进行本地开发时,有时会遇到一个挺头疼的问题:运行 composer dev(或者类似启动开发环境的命令),结果发现 Vite 提示端口被占用,更诡异的是,浏览器访问时看到的竟然是另一个旧项目的页面!明明那个旧项目相关的编辑器窗口、终端都关了,甚至用了 Ctrl + C 强制停止,问题依旧。这种情况在 Windows 环境下尤其容易碰到。

咱们来捋一捋这到底是怎么回事,以及怎么一步步解决它。

问题现象

具体表现通常是这样的:

  1. 在一个新的 Laravel 项目目录下,你修改了 composer.json 文件,添加了一个类似下面这样的 dev 脚本,目的是用 concurrently 同时启动 php artisan servephp artisan queue:listennpm run dev(Vite 开发服务器):
    "scripts": {
        "dev": [
            "Composer\\Config::disableProcessTimeout",
            "npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"npm run dev\" --names=server,queue,logs,vite"
        ],
        // ...其他脚本
    }
    
  2. 然后你在终端里信心满满地敲下 composer dev
  3. 很快,concurrently 的输出里,关于 vite 的那部分报了类似下面的错误:
    [vite] Port 5173 is in use, trying another one...
    
    这意味着 Vite 想用的默认端口 5173 已经被占了。
  4. 更让人迷惑的是,当你打开浏览器访问 http://localhost:8000 (或其他 php artisan serve 使用的地址) 并导航到某个页面(比如 /admin 登录页)时,看到的却是你之前开发的另一个旧项目的界面。
  5. 你尝试过关闭旧项目的 VS Code 窗口,确认所有相关终端都已通过 Ctrl + C 停止,清理了 Laravel 缓存 (php artisan cache:clear, config:clear, view:clear, route:clear),甚至重启了 XAMPP 里的 Apache 服务,但问题就是赖着不走。

原因分析

这个问题通常不是缓存或者 Web 服务器(如 Apache、Nginx)配置的问题,根源往往在于 后台残留的进程端口冲突

  1. 残留的开发进程:

    • 当你运行上一个项目的 composer dev 或单独运行 npm run dev / php artisan serve 时,它们会启动独立的后台进程。npm run dev 启动 Node.js 进程来运行 Vite 开发服务器,监听特定端口(默认 5173)。php artisan serve 启动 PHP 内置服务器进程,监听另一个端口(默认 8000)。
    • 在 Windows 上,使用 Ctrl + C 来停止由 concurrently 或其他工具启动的复合进程组时,并不总是能保证所有子进程都被彻底干净地终止 。某些子进程,特别是 Node.js 或 PHP 的服务进程,有可能变成“孤儿进程”继续在后台运行。
    • 所以,即使你关闭了终端和编辑器,旧项目的 Vite 开发服务器进程可能还在运行,并牢牢霸占着端口 5173。同样,旧项目的 php artisan serve 进程也可能还在运行,监听着 8000 端口。
  2. 端口冲突:

    • Vite 端口冲突: 当你为新项目运行 composer dev 时,新启动的 npm run dev (Vite) 尝试监听默认端口 5173,但发现这个端口已经被旧项目残留的 Vite 进程占用了。这就是你看到 Port 5173 is in use 错误的原因。Vite 接着会尝试寻找下一个可用端口(比如 5174),但这并不能解决根本问题。
    • artisan serve 端口冲突(导致看到旧项目): 旧项目的 php artisan serve 进程可能仍然在后台监听 8000 端口。当你用浏览器访问 http://localhost:8000 时,实际上连接到的是那个旧项目的服务进程,自然看到的就是旧项目的页面。你的新项目 php artisan serve 可能因为端口 8000 被占用而启动失败,或者 concurrently 没有正确报告这个失败,让你误以为新服务启动了。

简而言之,旧项目的幽灵进程赖着端口不走,导致了新项目启动失败或行为异常。

解决方案

解决这个问题的核心思路是:找出并终止那些赖着不走的旧进程,或者让新项目使用不同的端口。

方案一:彻底终止旧项目进程(推荐)

这是最直接也最根本的解决方法。我们需要手动找到并杀掉占用端口的旧进程。

  1. 查找占用端口的进程 ID (PID):
    打开一个新的 Windows 终端(可以是 CMD 或 PowerShell),使用 netstat 命令来查找哪些进程正在使用冲突的端口。

    • 查找占用 Vite 默认端口 5173 的进程:

      netstat -ano | findstr ":5173"
      

      这个命令会列出所有监听或连接到 5173 端口的网络连接。注意最后一列显示的数字,那就是进程的 PID。通常你会看到状态是 LISTENING 的那一行。

    • 查找占用 php artisan serve 默认端口 8000 的进程:

      netstat -ano | findstr ":8000"
      

      同样,记下状态为 LISTENING 的进程 PID。

    • 如何确认进程? 如果不确定找到的 PID 对应的是哪个程序(比如有多个程序用了相近端口),可以用 tasklist 命令结合 PID 查看:

      tasklist /FI "PID eq <PID>"
      

      <PID> 替换为你找到的实际 PID 数字。你应该能看到是 php.exenode.exe 在运行。
      或者,用 PowerShell (推荐) 可以获取更详细的信息,包含命令行参数,更容易判断来源:

      Get-CimInstance Win32_Process -Filter "ProcessId = <PID>" | Select-Object ProcessId, Name, CommandLine
      
  2. 终止进程:
    一旦确认了需要终止的进程 PID,就可以用 taskkill 命令来强制结束它。

    • 假设找到占用 5173 的 Node 进程 PID 是 12345,占用 8000 的 PHP 进程 PID 是 67890

      taskkill /PID 12345 /F
      taskkill /PID 67890 /F
      

      /F 参数表示强制终止。

    • 图形界面方式: 你也可以打开 任务管理器 (Task Manager) (Ctrl+Shift+Esc),切换到 “详细信息” (Details) 标签页。找到 PID 对应的 node.exephp.exe 进程,右键点击,选择 “结束任务” (End task)“结束进程树” (End process tree) (后者更彻底,如果有关联子进程的话)。

  3. 重新运行 composer dev
    在确认相关端口的进程都已结束后,回到你的新项目目录,再次运行 composer dev。这次 Vite 和 php artisan serve 应该都能顺利使用默认端口启动了,访问页面也应该是新项目的内容。

安全建议:

  • 在使用 taskkill 或任务管理器结束进程时,务必确认 PID 或进程名是你想要关闭的开发服务器进程,避免误杀系统或其他重要程序。结合 tasklistGet-CimInstance 检查命令行参数可以帮助确认。

进阶技巧:

  • 如果你经常遇到这个问题,可以考虑写个小脚本(比如 .bat.ps1 文件)来自动化查找并杀死特定端口的进程,一键清理。例如,一个简单的 PowerShell 脚本:
    $portsToClear = 5173, 8000
    foreach ($port in $portsToClear) {
        Write-Host "Checking port $port..."
        $processes = netstat -ano | findstr ":$port.*LISTENING"
        if ($processes) {
            foreach ($line in $processes) {
                $parts = $line.Split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)
                if ($parts.Count -ge 4) {
                    $pid = $parts[-1]
                    try {
                        Write-Host "Attempting to kill process with PID: $pid on port $port"
                        Stop-Process -Id $pid -Force -ErrorAction Stop
                        Write-Host "Process $pid killed."
                    } catch {
                        Write-Warning "Failed to kill process $pid: $_"
                    }
                }
            }
        } else {
            Write-Host "Port $port seems clear."
        }
    }
    Write-Host "Port clearing process finished."
    

方案二:为新项目指定不同端口

如果你不想每次都去杀进程,或者你确实需要同时运行多个项目,可以为新项目指定不同的端口。

  1. 修改 php artisan serve 端口:
    composer.jsondev 脚本里,给 php artisan serve 命令加上 --port 选项。选择一个不太可能冲突的端口,比如 8001

    "scripts": {
        "dev": [
            "Composer\\Config::disableProcessTimeout",
            "npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve --port=8001\" \"php artisan queue:listen --tries=1\" \"npm run dev\" --names=server,queue,logs,vite"
        ],
        // ...
    }
    

    这样,新项目的 php artisan serve 会在 http://localhost:8001 上运行。

  2. 修改 Vite 开发服务器端口:
    编辑项目根目录下的 vite.config.js 文件。在 defineConfig 中添加或修改 server 配置块。

    import { defineConfig } from 'vite';
    import laravel from 'laravel-vite-plugin';
    
    export default defineConfig({
        // 添加或修改这个 server 部分
        server: {
            port: 5174, // 指定一个新的端口,比如 5174
            strictPort: true, // (可选) 如果端口被占用,则直接退出,而不是尝试下一个端口
            hmr: { // (可能需要,根据你的具体环境) 明确 HMR 主机
                 host: 'localhost',
            }
        },
        plugins: [
            laravel({
                input: ['resources/css/app.css', 'resources/js/app.js'],
                refresh: true,
            }),
        ],
    });
    

    这样,新项目的 Vite 会尝试监听 5174 端口。

  3. 重新运行 composer dev
    修改配置后,运行 composer dev。现在它应该使用你指定的新端口 80015174 了。访问新项目时,记得使用新的地址 http://localhost:8001

进阶技巧:

  • 使用 .env 文件管理端口: 为了方便管理和避免硬编码,可以将端口号定义在 .env 文件中,然后在配置里读取。
    • .env 文件:
      DEV_SERVER_PORT=8001
      VITE_PORT=5174
      
    • composer.json (可能需要稍微调整脚本逻辑或使用额外包来读取 .env,或者直接在命令中使用环境变量语法,取决于你的 shell):
      // 示例,不一定所有环境直接支持 ${VAR},可能需要 dotenv-cli 等工具
      "php artisan serve --port=${DEV_SERVER_PORT:-8001}"
      
    • vite.config.js:
      import { defineConfig, loadEnv } from 'vite';
      // ...
      export default defineConfig(({ mode }) => {
          // 加载 .env 文件环境变量
          const env = loadEnv(mode, process.cwd(), '');
          return {
              server: {
                  port: parseInt(env.VITE_PORT || '5174'), // 从环境变量读取,提供默认值
                  strictPort: true,
                  hmr: { host: 'localhost' }
              },
              // ... plugins ...
          }
      });
      

方案三:检查 hosts 文件和 Web 服务器配置(可能性较低)

虽然你提到了 composer dev 使用的是 php artisan serve 和 Vite 内置服务器,它们通常不依赖 XAMPP 的 Apache。但以防万一,尤其如果你是通过自定义域名(如 myproject.test)而非 localhost:port 访问的,可以检查一下:

  1. 检查 hosts 文件:

    • 用管理员权限打开 C:\Windows\System32\drivers\etc\hosts 文件。
    • 看看是否有将你访问的域名(比如 myproject.test)指向 127.0.0.1 的条目。确认没有错误的或重复的条目指向旧项目的配置。但对于直接用 localhost 的情况,这个文件一般没影响。
  2. 检查 XAMPP/Apache 配置:

    • 如果你确实配置了 Apache 的虚拟主机 (VirtualHost) 来代理 php artisan serve 或处理特定域名,检查 Apache 的配置文件(如 httpd-vhosts.conf)。确保没有冲突的配置指向了旧项目路径。
    • 不过,根据你的,这步大概率不是问题所在,因为 php artisan serve 是独立的服务器。临时停用 Apache 服务 (net stop Apache2.4 或通过 XAMPP 控制面板) 再运行 composer dev,看问题是否消失,可以帮你排除 Apache 的干扰。

方案四:清理 composernpm 缓存(辅助手段)

这通常不是直接原因,但在某些极端情况下,缓存问题也可能导致意想不到的行为。作为一种补充手段,可以尝试清理:

  1. 清理 Composer 缓存:

    composer clear-cache
    
  2. 清理 npm 缓存:

    npm cache clean --force
    
  3. 删除依赖目录并重装:

    • 删除项目下的 vendor 目录。
    • 删除项目下的 node_modules 目录。
    • 删除 composer.lockpackage-lock.json (可选,如果怀疑锁文件有问题)。
    • 运行 composer install
    • 运行 npm install
    • 然后再次尝试 composer dev

总结来说,遇到 composer dev 启动了错误项目并且 Vite 端口冲突的问题,十有八九是旧项目的开发进程没有完全退出。首选方案是找到并终止这些残留进程。如果需要同时开发或想避免手动杀进程,为新项目配置不同的端口是可靠的备选方案。