fix: 替换 execSync 为异步 runAsync 避免打包时主进程阻塞白屏

This commit is contained in:
gmh01
2026-07-23 20:53:11 +08:00
parent 90d3404cd8
commit 482a79f1c7
2 changed files with 42 additions and 15 deletions

View File

@@ -1,16 +1,37 @@
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
const { execSync, spawn } = require('child_process') const { exec, spawn } = require('child_process')
const { getConfigPath, getSrcDir } = require('./project') const { getConfigPath, getSrcDir } = require('./project')
let isCancelled = false let isCancelled = false
let buildingProcess = null
function cancelPack() { function cancelPack() {
isCancelled = true isCancelled = true
if (buildingProcess) {
try { buildingProcess.kill() } catch (e) {}
buildingProcess = null
}
}
function runAsync(cmd, args, options) {
return new Promise((resolve, reject) => {
const child = spawn(cmd, args, { ...options, shell: true, stdio: ['pipe', 'pipe', 'pipe'] })
let stdout = ''
let stderr = ''
child.stdout.on('data', (d) => { stdout += d.toString() })
child.stderr.on('data', (d) => { stderr += d.toString() })
child.on('error', reject)
child.on('close', (code) => {
if (code === 0) resolve(stdout)
else reject(new Error(stderr || stdout || `进程退出码 ${code}`))
})
})
} }
async function startPack(projectPath, outputPath, onProgress) { async function startPack(projectPath, outputPath, onProgress) {
isCancelled = false isCancelled = false
buildingProcess = null
const config = JSON.parse(fs.readFileSync(getConfigPath(projectPath), 'utf-8')) const config = JSON.parse(fs.readFileSync(getConfigPath(projectPath), 'utf-8'))
const srcDir = getSrcDir(projectPath) const srcDir = getSrcDir(projectPath)
const tempDir = path.join(projectPath, '.web2app', 'temp-build') const tempDir = path.join(projectPath, '.web2app', 'temp-build')
@@ -91,17 +112,14 @@ async function startPack(projectPath, outputPath, onProgress) {
fs.writeFileSync(templatePackagePath, JSON.stringify(templatePackage, null, 2)) fs.writeFileSync(templatePackagePath, JSON.stringify(templatePackage, null, 2))
if (isCancelled) return { success: false, cancelled: true } if (isCancelled) return { success: false, cancelled: true }
onProgress({ stage: 'install', message: '安装依赖 (首次较慢)...', percent: 50 }) onProgress({ stage: 'install', message: '正在安装依赖...', percent: 45 })
try { try {
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm' const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm'
execSync(`${npmCmd} install --production`, { onProgress({ stage: 'install', message: '正在安装依赖 (首次可能需要几分钟)...', percent: 50 })
cwd: tempDir, await runAsync(npmCmd, ['install', '--production'], { cwd: tempDir, timeout: 300000 })
stdio: 'pipe',
timeout: 300000
})
} catch (err) { } catch (err) {
onProgress({ stage: 'install', message: `依赖安装失败: ${err.message}`, percent: 50 }) onProgress({ stage: 'error', message: `依赖安装失败: ${err.message}`, percent: 0 })
return { success: false, error: err.message } return { success: false, error: err.message }
} }
@@ -112,7 +130,7 @@ async function startPack(projectPath, outputPath, onProgress) {
const builderPath = path.join(tempDir, 'node_modules', '.bin', 'electron-builder') const builderPath = path.join(tempDir, 'node_modules', '.bin', 'electron-builder')
const builderCmd = process.platform === 'win32' ? `${builderPath}.cmd` : builderPath const builderCmd = process.platform === 'win32' ? `${builderPath}.cmd` : builderPath
const builderProcess = spawn(builderCmd, ['--config', templatePackagePath], { buildingProcess = spawn(builderCmd, ['--config', templatePackagePath], {
cwd: tempDir, cwd: tempDir,
shell: true, shell: true,
stdio: ['pipe', 'pipe', 'pipe'] stdio: ['pipe', 'pipe', 'pipe']
@@ -120,7 +138,7 @@ async function startPack(projectPath, outputPath, onProgress) {
let buildOutput = '' let buildOutput = ''
builderProcess.stdout.on('data', (data) => { buildingProcess.stdout.on('data', (data) => {
buildOutput += data.toString() buildOutput += data.toString()
const msg = data.toString() const msg = data.toString()
@@ -128,16 +146,24 @@ async function startPack(projectPath, outputPath, onProgress) {
onProgress({ stage: 'build', message: '正在打包...', percent: 75 }) onProgress({ stage: 'build', message: '正在打包...', percent: 75 })
} else if (msg.includes('signing')) { } else if (msg.includes('signing')) {
onProgress({ stage: 'sign', message: '正在签名...', percent: 85 }) onProgress({ stage: 'sign', message: '正在签名...', percent: 85 })
} else if (msg.includes('downloading')) {
onProgress({ stage: 'download', message: msg.trim(), percent: 55 })
} else if (msg.includes('building')) {
onProgress({ stage: 'build', message: msg.trim(), percent: 70 })
} else { } else {
onProgress({ stage: 'build', message: msg.trim(), percent: 75 }) const line = msg.trim()
if (line) {
onProgress({ stage: 'build', message: line, percent: 65 })
}
} }
}) })
builderProcess.stderr.on('data', (data) => { buildingProcess.stderr.on('data', (data) => {
buildOutput += data.toString() buildOutput += data.toString()
}) })
builderProcess.on('close', (code) => { buildingProcess.on('close', (code) => {
buildingProcess = null
if (isCancelled) { if (isCancelled) {
resolve({ success: false, cancelled: true }) resolve({ success: false, cancelled: true })
return return
@@ -173,7 +199,8 @@ async function startPack(projectPath, outputPath, onProgress) {
resolve({ success: true, outputPath: installerPath }) resolve({ success: true, outputPath: installerPath })
}) })
builderProcess.on('error', (err) => { buildingProcess.on('error', (err) => {
buildingProcess = null
resolve({ success: false, error: err.message }) resolve({ success: false, error: err.message })
}) })
}) })

View File

@@ -6,7 +6,7 @@
<div class="progress-bar" :style="{ width: progress.percent + '%' }"></div> <div class="progress-bar" :style="{ width: progress.percent + '%' }"></div>
</div> </div>
<div v-if="progress.percent > 0" class="progress-info"> <div v-if="progress.stage" class="progress-info">
<span class="progress-label">{{ stageLabel }}</span> <span class="progress-label">{{ stageLabel }}</span>
<span class="progress-percent">{{ Math.round(progress.percent) }}%</span> <span class="progress-percent">{{ Math.round(progress.percent) }}%</span>
</div> </div>