From 31988ed6377a902704670ade1ef63d452a04fe19 Mon Sep 17 00:00:00 2001 From: LaoShui <79132480+laoshuikaixue@users.noreply.github.com> Date: Sat, 27 Jun 2026 12:26:29 +0800 Subject: [PATCH 1/3] =?UTF-8?q?perf(build):=20=E4=BC=98=E5=8C=96=E5=8E=9F?= =?UTF-8?q?=E7=94=9F=E6=A8=A1=E5=9D=97=E6=9E=84=E5=BB=BA=E7=BC=93=E5=AD=98?= =?UTF-8?q?=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现基于文件哈希的原生模块构建缓存,避免重复编译 - 添加 .build-native-cache.json 文件到 .gitignore - 重构构建脚本支持增量构建检测 - 延迟自动更新检查以避免抢占首屏资源 - 添加组件懒加载优化页面启动性能 - 增加详细的启动时序日志便于性能监控 - 优化流媒体存储初始化逻辑提升启动速度 --- .gitignore | 1 + electron/main/core/index.ts | 18 ++++-- electron/main/window/main.ts | 15 ++++- scripts/build-native.ts | 120 ++++++++++++++++++++++++++++++++++- src/core/player/index.ts | 13 +++- src/layouts/MainLayout.vue | 11 ++++ src/main.ts | 12 +++- src/stores/streaming.ts | 7 +- src/stores/update.ts | 9 ++- 9 files changed, 188 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index dd1c5290..5528b170 100644 --- a/.gitignore +++ b/.gitignore @@ -17,5 +17,6 @@ skills-lock.json # Rust build artifacts **/target native/*/*.node +native/*/.build-native-cache.json # native/*/index.d.ts package-lock.json diff --git a/electron/main/core/index.ts b/electron/main/core/index.ts index 753adc01..b11e498b 100644 --- a/electron/main/core/index.ts +++ b/electron/main/core/index.ts @@ -44,6 +44,12 @@ const configureMemoryOptimizations = (): void => { const MEMORY_LOG_INTERVAL_MS = 10 * 60 * 1000; /** 启动后首次采样延迟,避开启动期波动 */ const MEMORY_LOG_FIRST_DELAY_MS = 60 * 1000; +/** 启动后自动更新延迟,避免抢占首屏资源 */ +const UPDATER_INIT_DELAY_MS = 2 * 60 * 1000; +/** 主窗口完成加载后恢复歌词辅助窗口,避免任务栏嵌入阻塞首屏 */ +const LYRIC_WINDOWS_RESTORE_DELAY_MS = 1000; +/** 主窗口加载后再初始化系统媒体控制 */ +const MEDIA_INIT_DELAY_MS = 500; /** 记录各进程内存工作集,用于量化内存表现与防劣化对比 */ const logProcessMemory = (): void => { @@ -107,7 +113,11 @@ export const initApp = (): void => { // 注册 IPC registerIpcHandlers(); // 创建主窗口 - createMainWindow(); + const mainWin = createMainWindow(); + mainWin.webContents.once("did-finish-load", () => { + setTimeout(() => initMedia(), MEDIA_INIT_DELAY_MS); + setTimeout(() => restoreLyricWindows(), LYRIC_WINDOWS_RESTORE_DELAY_MS); + }); // 注册 orpheus 协议并处理冷启动唤起 initOrpheusRegistration(); const coldOrpheusUrl = extractOrpheusUrl(process.argv); @@ -118,21 +128,17 @@ export const initApp = (): void => { void initSongCache(); // 启动下载服务 void initDownload(); - initMedia(); // 初始化 Last.fm 集成 initLastfm(); // 初始化插件系统 pluginRegistry.init(); // 初始化播放事件桥(需在 pluginRegistry.init 之后,读 hasEnabledControlPlugin) initPlaybackBridge(); - // 恢复歌词相关窗口 - restoreLyricWindows(); // 注册全局快捷键 initGlobalHotkey(); // 启动外部 API 服务 void startServer(); - // 初始化自动更新 - initUpdater(); + setTimeout(initUpdater, UPDATER_INIT_DELAY_MS); // 周期记录各进程内存 setTimeout(logProcessMemory, MEMORY_LOG_FIRST_DELAY_MS); setInterval(logProcessMemory, MEMORY_LOG_INTERVAL_MS); diff --git a/electron/main/window/main.ts b/electron/main/window/main.ts index 4cfeb395..e9e05fa6 100644 --- a/electron/main/window/main.ts +++ b/electron/main/window/main.ts @@ -9,6 +9,7 @@ import { store } from "@main/store"; import { handleCacheProtocolOnPartition } from "@main/utils/protocol"; import { isAppQuitting } from "@main/utils/lifecycle"; import { broadcast } from "@main/utils/broadcast"; +import { coreLog } from "@main/utils/logger"; /** 主窗口 session */ const MAIN_PARTITION = "persist:main"; @@ -26,6 +27,7 @@ let mainWindow: BrowserWindow | null = null; * 创建主窗口 */ export const createMainWindow = (): BrowserWindow => { + const createdAt = Date.now(); const remember = store.get("system.rememberWindowState") ?? true; const saved = remember ? store.get("windowStates.main") : undefined; @@ -51,14 +53,21 @@ export const createMainWindow = (): BrowserWindow => { // 初始化托盘 initTray(); - // 自定义任务栏缩略图 - enableTaskbarThumbnail(mainWindow); - // 缩略图工具栏 mainWindow.webContents.once("did-finish-load", () => { + coreLog.info(`主窗口 did-finish-load,用时 ${Date.now() - createdAt}ms`); + enableTaskbarThumbnail(mainWindow!); initThumbar(mainWindow!); }); + mainWindow.webContents.once("dom-ready", () => { + coreLog.info(`主窗口 dom-ready,用时 ${Date.now() - createdAt}ms`); + }); + + mainWindow.webContents.once("did-fail-load", (_event, errorCode, errorDescription) => { + coreLog.warn(`主窗口加载失败 ${errorCode}: ${errorDescription}`); + }); + // 每次加载完成应用界面缩放 mainWindow.webContents.on("did-finish-load", () => { applyMainWindowZoom(); diff --git a/scripts/build-native.ts b/scripts/build-native.ts index 8e19a769..9f603b40 100644 --- a/scripts/build-native.ts +++ b/scripts/build-native.ts @@ -1,3 +1,6 @@ +import { createHash } from "node:crypto"; +import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; import { spawnSync } from "node:child_process"; import process from "node:process"; @@ -6,6 +9,11 @@ interface NativeModule { enabled?: boolean; } +interface NativeBuildCache { + mode: "debug" | "release"; + hash: string; +} + const modules: NativeModule[] = [ { name: "audio-engine", @@ -31,6 +39,100 @@ const isRustAvailable = () => { return !result.error && !result.signal && result.status === 0; }; +const readJson = (path: string): T | undefined => { + try { + return JSON.parse(readFileSync(path, "utf8")) as T; + } catch { + return undefined; + } +}; + +const collectFiles = (dir: string): string[] => { + const entries = readdirSync(dir, { withFileTypes: true }); + const files: string[] = []; + + for (const entry of entries) { + const path = join(dir, entry.name); + + if (entry.isDirectory()) { + files.push(...collectFiles(path)); + continue; + } + + if (entry.isFile()) { + files.push(path); + } + } + + return files; +}; + +const hashFile = (hash: ReturnType, path: string) => { + if (!existsSync(path)) { + hash.update(`missing:${path}\n`); + return; + } + + hash.update(`file:${path}\n`); + hash.update(readFileSync(path)); +}; + +const getNativeBuildHash = (cwd: string) => { + const hash = createHash("sha256"); + const inputs = ["Cargo.toml", "Cargo.lock", "package.json", "pnpm-lock.yaml"]; + + hash.update(`platform:${process.platform}\n`); + hash.update(`arch:${process.arch}\n`); + + for (const input of inputs) { + hashFile(hash, input); + } + + for (const input of ["Cargo.toml", "build.rs", "package.json"]) { + hashFile(hash, join(cwd, input)); + } + + for (const file of collectFiles(join(cwd, "src")).sort()) { + hashFile(hash, file); + } + + return hash.digest("hex"); +}; + +const getNativeOutputPath = (cwd: string, name: string) => join(cwd, `${name}.node`); + +const getCachePath = (cwd: string) => join(cwd, ".build-native-cache.json"); + +const shouldSkipNativeBuild = (cwd: string, name: string, mode: NativeBuildCache["mode"]) => { + const outputPath = getNativeOutputPath(cwd, name); + const typePath = join(cwd, "index.d.ts"); + + if (!existsSync(outputPath) || !existsSync(typePath)) { + return false; + } + + const outputStat = statSync(outputPath); + if (outputStat.size === 0) { + return false; + } + + const cache = readJson(getCachePath(cwd)); + if (!cache || cache.mode !== mode) { + return false; + } + + return cache.hash === getNativeBuildHash(cwd); +}; + +const writeNativeBuildCache = (cwd: string, mode: NativeBuildCache["mode"]) => { + const cache: NativeBuildCache = { + mode, + hash: getNativeBuildHash(cwd), + }; + + writeFileSync(getCachePath(cwd), `${JSON.stringify(cache, null, 2)}\n`); +}; + if (process.env.SKIP_NATIVE_BUILD === "true" || process.env.SKIP_NATIVE_BUILD === "1") { console.log("[BuildNative] SKIP_NATIVE_BUILD 已设置,跳过原生模块构建"); process.exit(0); @@ -80,6 +182,10 @@ const parseArgs = () => { const napiArgs = ["--no-const-enum"]; const options = parseArgs(); +const buildType = options.isDev ? "debug" : "release"; +const canUseDevCache = options.isDev && !options.passing; +const projectRoot = process.cwd(); +const napiCliPath = join(projectRoot, "node_modules", "@napi-rs", "cli", "dist", "cli.js"); if (!options.isDev) napiArgs.push("--release"); if (options.passing) napiArgs.push(...options.passing); @@ -90,12 +196,16 @@ for (const mod of modules) { } const cwd = `native/${mod.name}`; - const buildType = options.isDev ? "debug" : "release"; + if (canUseDevCache && shouldSkipNativeBuild(cwd, mod.name, buildType)) { + console.log(`[BuildNative] 跳过 ${mod.name} (${buildType}),原生输入未变化`); + continue; + } + console.log(`[BuildNative] 构建 ${mod.name} (${buildType})`); - const result = spawnSync("napi", ["build", ...napiArgs], { + const result = spawnSync(process.execPath, [napiCliPath, "build", ...napiArgs], { stdio: "inherit", - shell: process.platform === "win32", + shell: false, cwd, }); @@ -111,4 +221,8 @@ for (const mod of modules) { console.error("[BuildNative] 模块构建失败,进程异常退出", result.status); process.exit(result.status ?? 1); } + + if (canUseDevCache) { + writeNativeBuildCache(cwd, buildType); + } } diff --git a/src/core/player/index.ts b/src/core/player/index.ts index 993902d8..734cbabc 100644 --- a/src/core/player/index.ts +++ b/src/core/player/index.ts @@ -805,22 +805,31 @@ export const moveInQueue = (fromIndex: number, toIndex: number): void => { let unsubscribe: (() => void) | null = null; let initialized = false; +const logInitStep = (startedAt: number, step: string): void => { + console.info(`[player] init ${step} ${Math.round(performance.now() - startedAt)}ms`); +}; + /** 初始化播放器 */ export const initPlayer = async (): Promise => { if (initialized) return; initialized = true; - console.log("[player] init"); + const startedAt = performance.now(); + console.info("[player] init"); // 先从主进程同步后端配置,确保 system 设置可用 const settings = useSettingsStore(); await settings.syncSystem(); + logInitStep(startedAt, "settings synced"); // 流媒体 store 必须在恢复队列前就绪,否则队列里的 streaming track 拿不到 cfg await useStreamingStore().init(); + logInitStep(startedAt, "streaming initialized"); // 插件 store 同理:在线歌曲 URL 兜底走插件,列表必须在 loadTrack 前就绪 void usePluginsStore().load(); await queue.restoreQueue(); + logInitStep(startedAt, "queue restored"); const status = useStatusStore(); // 恢复上次的音量和播放模式到主进程 await window.api.player.setVolume(status.volume); + logInitStep(startedAt, "player backend ready"); syncPlayMode(); // 应用渐入渐出配置 const { fadeEnabled, fadeDuration, loudnessNormalization, equalizer } = settings.system.player; @@ -835,6 +844,7 @@ export const initPlayer = async (): Promise => { } // 刷新设备列表并恢复上次选择的输出设备 await refreshDevices(); + logInitStep(startedAt, "devices refreshed"); if (settings.player.outputDevice) { await window.api.player.setOutputDevice(settings.player.outputDevice); } @@ -883,6 +893,7 @@ export const initPlayer = async (): Promise => { } else { status.state = "idle"; } + logInitStep(startedAt, "done"); }; /** 清理事件订阅 */ diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue index ff8f275c..be375def 100644 --- a/src/layouts/MainLayout.vue +++ b/src/layouts/MainLayout.vue @@ -4,6 +4,17 @@ import { useMediaStore } from "@/stores/media"; import { useSettingsStore } from "@/stores/settings"; import { useOrpheusProtocol } from "@/composables/useOrpheusProtocol"; +const PlayerBar = defineAsyncComponent(() => import("@/components/player/PlayerBar.vue")); +const FullPlayer = defineAsyncComponent(() => import("@/components/player/FullPlayer/index.vue")); +const SettingsDialog = defineAsyncComponent( + () => import("@/components/settings/SettingsDialog.vue"), +); +const UpdateDialog = defineAsyncComponent(() => import("@/components/modals/UpdateDialog.vue")); +const SPerformanceMonitor = defineAsyncComponent( + () => import("@/components/SPerformanceMonitor.vue"), +); +const SDialogProvider = defineAsyncComponent(() => import("@/components/ui/SDialogProvider.vue")); + const route = useRoute(); const status = useStatusStore(); const settings = useSettingsStore(); diff --git a/src/main.ts b/src/main.ts index 6ef9d405..a81eb367 100644 --- a/src/main.ts +++ b/src/main.ts @@ -13,6 +13,11 @@ import { initPlayer } from "./core/player"; import { installHotkeyManager } from "./core/hotkey/manager"; import { vRipple } from "./directives/ripple"; +const bootStart = performance.now(); +const logBoot = (stage: string): void => { + console.info(`[boot] ${stage} ${Math.round(performance.now() - bootStart)}ms`); +}; + const pinia = createPinia(); pinia.use(piniaPersistedstate); @@ -40,8 +45,10 @@ const SPLASH_ANIM_MS = 2050; // 初始化程序 router.isReady().then(() => { + logBoot("router ready"); // 挂载应用 app.mount("#app"); + logBoot("app mounted"); // 笔画播完即淡出 const remaining = Math.max(0, SPLASH_ANIM_MS - performance.now()); setTimeout(() => { @@ -50,9 +57,12 @@ router.isReady().then(() => { loading.classList.add("hidden"); loading.addEventListener("transitionend", () => loading.remove(), { once: true }); } + logBoot("splash hidden"); }, remaining); // 初始化播放器 - initPlayer().catch(console.error); + initPlayer() + .then(() => logBoot("player ready")) + .catch(console.error); // 初始化快捷键 useHotkeyStore() .init() diff --git a/src/stores/streaming.ts b/src/stores/streaming.ts index 713f531c..8a3b4a71 100644 --- a/src/stores/streaming.ts +++ b/src/stores/streaming.ts @@ -52,6 +52,8 @@ export const useStreamingStore = defineStore("streaming", () => { const playlists = shallowRef([]); /** 缓存最后更新时间(ms) */ const lastFetchedAt = ref(0); + /** 是否已加载服务器配置 */ + const initialized = ref(false); /** 是否已从 IndexedDB 完成首次水合 */ const hydrated = ref(false); @@ -568,14 +570,15 @@ export const useStreamingStore = defineStore("streaming", () => { }; const init = async (): Promise => { - if (hydrated.value) return; + if (initialized.value) return; + initialized.value = true; const result = await window.api.streaming.loadServers(); servers.value = result.servers; activeServerId.value = result.activeServerId; if (activeServerId.value && !servers.value.find((s) => s.id === activeServerId.value)) { activeServerId.value = null; } - await hydrateFromCache(); + void hydrateFromCache(); if (activeServerId.value) void connectToServer(activeServerId.value); }; diff --git a/src/stores/update.ts b/src/stores/update.ts index 28c75fa5..90445361 100644 --- a/src/stores/update.ts +++ b/src/stores/update.ts @@ -4,6 +4,9 @@ import i18n from "@/i18n"; const { t } = i18n.global; +/** 启动后自动检查更新延迟,避免抢占首屏加载 */ +const AUTO_CHECK_DELAY_MS = 2 * 60 * 1000; + export const useUpdateStore = defineStore("update", () => { /** 当前阶段 */ const phase = ref("idle"); @@ -56,8 +59,10 @@ export const useUpdateStore = defineStore("update", () => { // 订阅主进程推送的更新事件 const unsubscribe = window.api.update.onEvent(handleEvent); onScopeDispose(unsubscribe); - // 触发启动检查 - void window.api.update.check(false); + const autoCheckTimer = window.setTimeout(() => { + void window.api.update.check(false); + }, AUTO_CHECK_DELAY_MS); + onScopeDispose(() => window.clearTimeout(autoCheckTimer)); /** 手动检查更新 */ const checkManually = (): void => { From cc5558329f5c06c102144bac8a943c8bccd81543 Mon Sep 17 00:00:00 2001 From: LaoShui <79132480+laoshuikaixue@users.noreply.github.com> Date: Sat, 27 Jun 2026 12:31:00 +0800 Subject: [PATCH 2/3] =?UTF-8?q?style(docs):=20=E7=A7=BB=E9=99=A4=20PR=20?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E4=B8=AD=E7=9A=84=E5=A4=9A=E4=BD=99=E7=A9=BA?= =?UTF-8?q?=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/PULL_REQUEST_TEMPLATE.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 314b0aa5..e115a7ee 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -22,26 +22,18 @@ - - ## 关联 Issue - - ## 测试情况 - - ## 截图 / 录屏 - - ## 自查清单 - [ ] 本 PR 只包含**一个主要功能 / 修复**,没有夹带无关改动 From a3047117c4cee1691def3d93a38cb59e38c9a5f3 Mon Sep 17 00:00:00 2001 From: LaoShui <79132480+laoshuikaixue@users.noreply.github.com> Date: Sat, 27 Jun 2026 21:02:45 +0800 Subject: [PATCH 3/3] =?UTF-8?q?refactor(build):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=8E=9F=E7=94=9F=E6=A8=A1=E5=9D=97=E6=9E=84=E5=BB=BA=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除了 .gitignore 中的 .build-native-cache.json 文件忽略规则 - 移除了构建脚本中的哈希计算、文件收集和缓存相关函数 - 删除了 NativeBuildCache 接口定义 - 移除了 shouldSkipNativeBuild 和 writeNativeBuildCache 函数 - 清理了构建过程中的缓存检查逻辑 - 简化了构建流程,直接执行构建而无需缓存验证 --- .gitignore | 1 - scripts/build-native.ts | 113 +--------------------------------------- 2 files changed, 1 insertion(+), 113 deletions(-) diff --git a/.gitignore b/.gitignore index 5528b170..dd1c5290 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,5 @@ skills-lock.json # Rust build artifacts **/target native/*/*.node -native/*/.build-native-cache.json # native/*/index.d.ts package-lock.json diff --git a/scripts/build-native.ts b/scripts/build-native.ts index 9f603b40..dd3956a8 100644 --- a/scripts/build-native.ts +++ b/scripts/build-native.ts @@ -1,7 +1,5 @@ -import { createHash } from "node:crypto"; -import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs"; -import { join } from "node:path"; import { spawnSync } from "node:child_process"; +import { join } from "node:path"; import process from "node:process"; interface NativeModule { @@ -9,11 +7,6 @@ interface NativeModule { enabled?: boolean; } -interface NativeBuildCache { - mode: "debug" | "release"; - hash: string; -} - const modules: NativeModule[] = [ { name: "audio-engine", @@ -39,100 +32,6 @@ const isRustAvailable = () => { return !result.error && !result.signal && result.status === 0; }; -const readJson = (path: string): T | undefined => { - try { - return JSON.parse(readFileSync(path, "utf8")) as T; - } catch { - return undefined; - } -}; - -const collectFiles = (dir: string): string[] => { - const entries = readdirSync(dir, { withFileTypes: true }); - const files: string[] = []; - - for (const entry of entries) { - const path = join(dir, entry.name); - - if (entry.isDirectory()) { - files.push(...collectFiles(path)); - continue; - } - - if (entry.isFile()) { - files.push(path); - } - } - - return files; -}; - -const hashFile = (hash: ReturnType, path: string) => { - if (!existsSync(path)) { - hash.update(`missing:${path}\n`); - return; - } - - hash.update(`file:${path}\n`); - hash.update(readFileSync(path)); -}; - -const getNativeBuildHash = (cwd: string) => { - const hash = createHash("sha256"); - const inputs = ["Cargo.toml", "Cargo.lock", "package.json", "pnpm-lock.yaml"]; - - hash.update(`platform:${process.platform}\n`); - hash.update(`arch:${process.arch}\n`); - - for (const input of inputs) { - hashFile(hash, input); - } - - for (const input of ["Cargo.toml", "build.rs", "package.json"]) { - hashFile(hash, join(cwd, input)); - } - - for (const file of collectFiles(join(cwd, "src")).sort()) { - hashFile(hash, file); - } - - return hash.digest("hex"); -}; - -const getNativeOutputPath = (cwd: string, name: string) => join(cwd, `${name}.node`); - -const getCachePath = (cwd: string) => join(cwd, ".build-native-cache.json"); - -const shouldSkipNativeBuild = (cwd: string, name: string, mode: NativeBuildCache["mode"]) => { - const outputPath = getNativeOutputPath(cwd, name); - const typePath = join(cwd, "index.d.ts"); - - if (!existsSync(outputPath) || !existsSync(typePath)) { - return false; - } - - const outputStat = statSync(outputPath); - if (outputStat.size === 0) { - return false; - } - - const cache = readJson(getCachePath(cwd)); - if (!cache || cache.mode !== mode) { - return false; - } - - return cache.hash === getNativeBuildHash(cwd); -}; - -const writeNativeBuildCache = (cwd: string, mode: NativeBuildCache["mode"]) => { - const cache: NativeBuildCache = { - mode, - hash: getNativeBuildHash(cwd), - }; - - writeFileSync(getCachePath(cwd), `${JSON.stringify(cache, null, 2)}\n`); -}; - if (process.env.SKIP_NATIVE_BUILD === "true" || process.env.SKIP_NATIVE_BUILD === "1") { console.log("[BuildNative] SKIP_NATIVE_BUILD 已设置,跳过原生模块构建"); process.exit(0); @@ -183,7 +82,6 @@ const parseArgs = () => { const napiArgs = ["--no-const-enum"]; const options = parseArgs(); const buildType = options.isDev ? "debug" : "release"; -const canUseDevCache = options.isDev && !options.passing; const projectRoot = process.cwd(); const napiCliPath = join(projectRoot, "node_modules", "@napi-rs", "cli", "dist", "cli.js"); @@ -196,11 +94,6 @@ for (const mod of modules) { } const cwd = `native/${mod.name}`; - if (canUseDevCache && shouldSkipNativeBuild(cwd, mod.name, buildType)) { - console.log(`[BuildNative] 跳过 ${mod.name} (${buildType}),原生输入未变化`); - continue; - } - console.log(`[BuildNative] 构建 ${mod.name} (${buildType})`); const result = spawnSync(process.execPath, [napiCliPath, "build", ...napiArgs], { @@ -221,8 +114,4 @@ for (const mod of modules) { console.error("[BuildNative] 模块构建失败,进程异常退出", result.status); process.exit(result.status ?? 1); } - - if (canUseDevCache) { - writeNativeBuildCache(cwd, buildType); - } }