Electron+electron-builder打包Laravel后无法自动启动本地服务
Electron+Laravel打包后无法自动启动PHP服务的问题修复
问题描述
我用electron-builder配置了Electron+Laravel项目,编写了main.cjs并完成package.json配置,执行npm run electron-build生成headman-system Setup 1.0.0.exe安装包。本地首次安装后应用可正常运行,但电脑关机重启后,或在其他电脑安装该程序时,启动应用会停留在loading.html,无法自动启动localhost服务加载项目内容。只有回到开发项目执行npm run electron或npm run start后,已安装的应用快捷方式才能正常加载内容,多次尝试修复均无法解决自动启动localhost的问题。
相关代码
main.cjs
const { app, BrowserWindow } = require("electron"); const path = require("path"); const { exec } = require("child_process"); const AutoLaunch = require("auto-launch"); const PORT = 50505; // 自定义本地端口 let phpServer; function createWindow() { const win = new BrowserWindow({ width: 800, height: 600, icon: path.join(app.getAppPath(), "public/assets/img/1.png"), webPreferences: { nodeIntegration: true, contextIsolation: false, }, }); win.setMenuBarVisibility(false); win.loadFile("loading.html"); const checkServer = () => { fetch(`http://localhost:${PORT}`) .then(() => { win.loadURL(`http://localhost:${PORT}`); // 服务器就绪后加载主内容 }) .catch(() => { setTimeout(checkServer, 1000); // 未就绪则1秒后重试 }); }; checkServer(); // 启动服务器状态检测 } function startServices() { const phpPath = path.join(app.getAppPath(), "php", "php.exe"); const publicPath = path.join(app.getAppPath(), "public"); console.log(`PHP路径: ${phpPath}`); console.log(`Public路径: ${publicPath}`); phpServer = exec( `${phpPath} -S localhost:${PORT} -t ${publicPath}`, (error, stdout, stderr) => { if (error) { console.error(`执行错误: ${error}`); return; } console.log(`标准输出: ${stdout}`); console.error(`错误输出: ${stderr}`); } ); phpServer.stdout.on("data", (data) => { console.log(`PHP服务器日志: ${data}`); }); phpServer.stderr.on("data", (data) => { console.error(`PHP服务器错误: ${data}`); }); phpServer.on("close", (code) => { console.log(`PHP服务器退出,代码: ${code}`); }); } app.whenReady().then(() => { startServices(); createWindow(); // 配置开机自启 const autoLauncher = new AutoLaunch({ name: "Headman", path: app.getPath("exe"), }); autoLauncher.isEnabled().then((isEnabled) => { if (!isEnabled) autoLauncher.enable(); }).catch((err) => { console.error(err); }); app.on("activate", () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(); } }); }); app.on("window-all-closed", () => { if (process.platform !== "darwin") { if (phpServer) phpServer.kill(); app.quit(); } });
package.json
{ "private": true, "type": "module", "scripts": { "dev": "vite", "build": "vite build", "electron": "electron .", "electron-build": "electron-builder" }, "devDependencies": { "@popperjs/core": "^2.11.6", "axios": "^1.6.4", "bootstrap": "^5.2.3", "electron": "^31.2.1", "electron-builder": "^24.13.3", "laravel-vite-plugin": "^1.0.0", "sass": "^1.56.1", "vite": "^5.0.0" }, "name": "headman-system", "version": "1.0.0", "description": "<p align=\"center\"><a href=\"https://laravel.com\" target=\"_blank\"><img src=\"https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg\" width=\"400\" alt=\"Laravel Logo\"></a></p>", "main": "electron/main.cjs", "build": { "appId": "com.headman.id", "directories": { "output": "dist" }, "files": [ "app/**/*", "bootstrap/**/*", "config/**/*", "database/**/*", "electron/**/*", "node_modules/**/*", "php/**/*", "public/**/*", "resources/**/*", "routes/**/*", "storage/**/*", "vendor/**/*", ".editorconfig", ".env", "artisan", "composer.json", "composer.lock", "loading.html", "package-lock.json", "package.json", "phpunit.xml", "vite.config.js" ], "win": { "icon": "public\\assets\\img\\icon.ico" }, "nsis": { "oneClick": false, "perMachine": false, "allowToChangeInstallationDirectory": true, "createDesktopShortcut": true, "createStartMenuShortcut": true, "shortcutName": "Headman" }, "extraResources": [ { "from": "electron", "to": "app", "filter": [ "**/*" ] } ] }, "dependencies": { "anymatch": "^3.1.3", "asynckit": "^0.4.0", "auto-launch": "^5.0.6", "binary-extensions": "^2.3.0", "braces": "^3.0.3", "chokidar": "^3.6.0", "combined-stream": "^1.0.8", "delayed-stream": "^1.0.0", "esbuild": "^0.21.5", "fill-range": "^7.1.1", "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "glob-parent": "^5.1.2", "immutable": "^4.3.6", "is-binary-path": "^2.1.0", "is-extglob": "^2.1.1", "is-glob": "^4.0.3", "is-number": "^7.0.0", "mime-db": "^1.52.0", "mime-types": "^2.1.35", "nanoid": "^3.3.7", "normalize-path": "^3.0.0", "picocolors": "^1.0.1", "picomatch": "^2.3.1", "postcss": "^8.4.39", "proxy-from-env": "^1.1.0", "readdirp": "^3.6.0", "rollup": "^4.18.1", "source-map-js": "^1.2.0", "to-regex-range": "^5.0.1", "vite-plugin-full-reload": "^1.2.0" }, "keywords": [], "author": "suli core team", "license": "ISC" }
问题原因分析
- 路径不匹配:开发环境与打包后环境的
app.getAppPath()指向不同,导致PHP执行文件或Laravel public目录路径错误,无法启动PHP内置服务器。 - PHP依赖缺失:打包时未将PHP所需的DLL文件或扩展完全包含,导致PHP进程启动失败。
- 权限不足:程序启动PHP子进程时没有足够权限,尤其是重启后或在非开发环境的电脑上。
- 启动逻辑缺陷:服务启动与状态检测的顺序或错误处理不完善,无法及时发现服务启动失败的情况。
修复步骤
1. 修正路径获取逻辑
区分开发与生产环境的路径,确保打包后能正确找到PHP和public目录:
function startServices() { const isDev = !app.isPackaged; // 打包后资源目录在process.resourcesPath下的app文件夹 const basePath = isDev ? app.getAppPath() : path.join(process.resourcesPath, 'app'); const phpPath = path.join(basePath, "php", "php.exe"); const publicPath = path.join(basePath, "public"); // 检查PHP文件是否存在 const fs = require('fs'); if (!fs.existsSync(phpPath)) { console.error(`PHP可执行文件不存在:${phpPath}`); // 可选:弹出错误提示窗口告知用户 return; } // 给路径加引号,避免路径中有空格导致执行失败 phpServer = exec( `"${phpPath}" -S localhost:${PORT} -t "${publicPath}"`, (error, stdout, stderr) => { if (error) { console.error(`启动错误:${error.message}`); return; } console.log(`输出:${stdout}`); console.error(`错误输出:${stderr}`); } ); // 其余代码保持不变 }
2. 确保PHP目录完整打包
检查package.json的files配置,确认"php/**/*"包含了PHP的所有文件,包括php.exe、php.ini以及ext目录下的所有DLL扩展文件,这些是PHP启动必需的。
3. 提升程序权限
在package.json的nsis配置中添加权限请求,确保程序能启动子进程:
"nsis": { "oneClick": false, "perMachine": false, "allowToChangeInstallationDirectory": true, "createDesktopShortcut": true, "createStartMenuShortcut": true, "shortcutName": "Headman", "requestExecutionLevel": "highestAvailable" // 请求最高权限 }
4. 优化服务检测逻辑
增强服务检测的错误处理,便于排查问题:
const checkServer = () => { fetch(`http://localhost:${PORT}`) .then(response => { if (response.ok) { win.loadURL(`http://localhost:${PORT}`); } else { console.log(`服务器响应异常,状态码:${response.status}`); setTimeout(checkServer, 1000); } }) .catch(err => { console.log(`服务器未就绪:${err.message}`); setTimeout(checkServer, 1000); }); };
5. 验证打包后的文件结构
安装打包后的程序,打开安装目录下的resources/app文件夹,确认:
php/php.exe存在且可执行public目录包含Laravel编译后的静态资源- 所有依赖的PHP扩展DLL文件都在
php/ext目录下
内容的提问来源于stack exchange,提问作者Mister zana
相关产品推荐
相关产品推荐

