解决端口被占用问题的 Webpack 启动脚本

一、使用背景

在前端开发过程中,经常会遇到如下错误:

perl 复制代码
Error: listen EADDRINUSE: address already in use :::3000

这意味着端口 3000 已被其他进程占用,Webpack 开发服务器无法启动。

为了解决这一问题,编写了一个启动脚本 start-dev.js,可在启动时自动检测端口状态,并根据策略自动选择下一个可用端口或强制释放占用端口,从而确保开发服务器顺利启动。

二、使用方法

1️⃣ 在 package.json 中添加启动脚本

json 复制代码
{
  "scripts": {
    "start:dev": "node scripts/start-dev.js"
  }
}

2️⃣ 默认启动方式

arduino 复制代码
npm run start:dev

三、命令行参数说明

参数名 格式 默认值 说明
--port --port=<number> 3000 指定开发服务器启动端口
--mode --mode=<increment|kill> increment 端口冲突处理模式

模式一:increment(默认模式)

检测到端口被占用时,自动递增选择下一个空闲端口。

css 复制代码
node scripts/start-dev.js --port=3000 --mode=increment

输出:

yaml 复制代码
⚠️ 端口 3000 已被占用,自动切换到 3001
🚀 启动 webpack 开发服务器 (端口 3001)...

模式二:kill

检测到端口被占用时,自动终止占用该端口的进程后重新启动。

bash 复制代码
node scripts/start-dev.js --port=3000 --mode=kill

输出:

yaml 复制代码
✅ 已终止占用进程 PID: 45822
🚀 启动 webpack 开发服务器 (端口 3000)...

⚠️ 注意: kill 模式仅建议在本地开发环境使用,避免误关闭其他关键程序。

🧾 四、代码

javascript 复制代码
#!/usr/bin/env node
// scripts/start-dev.js
/**
 * 自动启动 webpack 开发服务器脚本(支持端口冲突处理模式)
 * 功能:
 * 1. 支持命令行指定端口 (--port=3000)
 * 2. 支持端口冲突处理模式:(--mode=increment)
 *    - increment(默认):端口被占用时自动选择下一个空闲端口
 *    - kill:端口被占用时尝试 kill 占用进程再使用该端口
 * 3. 输出局域网 IP + 本地访问 URL
 */

const { execSync } = require("child_process");
const portfinder = require("portfinder");
const os = require("os");

// ===== 1️⃣ 解析命令行参数 =====
let TARGET_PORT = 3000;
let MODE = "increment"; // 默认行为:端口递增
process.argv.forEach((arg) => {
  if (arg.startsWith("--port=")) {
    TARGET_PORT = parseInt(arg.split("=")[1], 10);
  } else if (arg.startsWith("--mode=")) {
    const m = arg.split("=")[1];
    if (m === "increment" || m === "kill") MODE = m;
  }
});

// ===== 2️⃣ 获取本机局域网 IP =====
function getLocalIP() {
  const nets = os.networkInterfaces();
  for (const name of Object.keys(nets)) {
    for (const net of nets[name]) {
      if (net.family === "IPv4" && !net.internal) return net.address;
    }
  }
  return "localhost";
}

// ===== 3️⃣ 启动逻辑 =====
(async () => {
  try {
    let port = TARGET_PORT;

    if (MODE === "increment") {
      // 自动递增模式
      const freePort = await portfinder.getPortPromise({ port });
      if (freePort !== port) {
        console.log(`⚠️ 端口 ${port} 已被占用,自动切换到 ${freePort}`);
        port = freePort;
      }
    } else if (MODE === "kill") {
      // 强制 kill 模式
      try {
        const pidResult = execSync(`lsof -ti tcp:${port} || true`)
          .toString()
          .trim();
        if (pidResult) {
          const pidList = pidResult.split("\n");
          pidList.forEach((pid) => {
            if (pid) {
              execSync(`kill -9 ${pid}`);
              console.log(`✅ 已终止占用进程 PID: ${pid}`);
            }
          });
        } else {
          console.log(`⚠️ 端口 ${port} 未被占用`);
        }
      } catch (e) {
        console.warn("⚠️ 自动关闭进程失败,请手动检查");
      }
    }

    const localIP = getLocalIP();
    console.log(`🚀 启动 webpack 开发服务器 (端口 ${port})...`);
    console.log(`🔗 本地访问:http://localhost:${port}`);
    console.log(`🔗 局域网访问:http://${localIP}:${port}`);

    execSync(`npx webpack serve --port ${port}`, { stdio: "inherit" });
  } catch (err) {
    console.error("❌ 启动失败:", err.message);
  }
})();
相关推荐
于慨1 天前
Lambda 表达式、方法引用(Method Reference)语法
java·前端·servlet
石小石Orz1 天前
油猴脚本实现生产环境加载本地qiankun子应用
前端·架构
从前慢丶1 天前
前端交互规范(Web 端)
前端
CHU7290351 天前
便捷约玩,沉浸推理:线上剧本杀APP功能版块设计详解
前端·小程序
GISer_Jing1 天前
Page-agent MCP结构
前端·人工智能
王霸天1 天前
💥别再抄网上的Scale缩放代码了!50行源码教你写一个永不翻车的大屏适配
前端·vue.js·数据可视化
小领航1 天前
用 Three.js + Vue 3 打造炫酷的 3D 行政地图可视化组件
前端·github
@大迁世界1 天前
2026年React大洗牌:React Hooks 将迎来重大升级
前端·javascript·react.js·前端框架·ecmascript
PieroPc1 天前
一个功能强大的 Web 端标签设计和打印工具,支持服务器端直接打印到局域网打印机。Fastapi + html
前端·html·fastapi
悟空瞎说1 天前
深入 Vue3 响应式:为什么有的要加.value,有的不用?从设计到源码彻底讲透
前端·vue.js