mirror of
https://gitee.com/zhangsken/opencode.git
synced 2026-10-16 10:30:01 +08:00
perf(desktop): serve the renderer's first assets before evaluating the main bundle (#50109)
This commit is contained in:
@@ -2,7 +2,8 @@
|
||||
import { marks } from "./lifecycle/marks"
|
||||
import { app } from "electron"
|
||||
import { acquireApplicationLock, configureApplication } from "./lifecycle/configure"
|
||||
import { createEarlyWindow } from "./windows/early"
|
||||
import { createEarlyWindow } from "./windows/early"
|
||||
import { rendererAssetsServed } from "./windows/protocol"
|
||||
import { registerRendererScheme } from "./windows/scheme"
|
||||
|
||||
// This module stays small on purpose. Electron holds the ready event until the entry module has
|
||||
@@ -14,10 +15,15 @@ if (acquireApplicationLock()) {
|
||||
registerRendererScheme()
|
||||
// Window first, then the bundle: starting the import before ready delays ready itself, because the
|
||||
// module graph evaluates on the same thread Chromium needs to finish initialising.
|
||||
void app.whenReady().then(() => {
|
||||
marks.ready = Date.now()
|
||||
createEarlyWindow()
|
||||
marks.window = Date.now()
|
||||
void app.whenReady().then(async () => {
|
||||
marks.ready = Date.now()
|
||||
createEarlyWindow()
|
||||
marks.window = Date.now()
|
||||
// The window's renderer is already loading. Its HTML and preloaded chunks are served from this
|
||||
// thread, so the bundle waits for that burst to be answered (or a cap) before it evaluates.
|
||||
if (!process.env.ELECTRON_RENDERER_URL) await rendererAssetsServed({ quietMs: 40, capMs: 400 })
|
||||
marks.served = Date.now()
|
||||
return import("./desktop")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -14,11 +14,39 @@ export function setProtocolReporter(reporter: ProtocolReport) {
|
||||
report = reporter
|
||||
}
|
||||
|
||||
// Requests in flight and when the last one arrived. The entry module holds the main bundle back
|
||||
// until the renderer's initial burst of asset requests has been answered, because this handler
|
||||
// runs on the main thread and a 100 ms module evaluation would otherwise sit between the renderer
|
||||
// and its HTML.
|
||||
let inflight = 0
|
||||
let served = 0
|
||||
let lastRequest = 0
|
||||
|
||||
export function rendererAssetsServed(options: { quietMs: number; capMs: number }) {
|
||||
const start = Date.now()
|
||||
return new Promise<void>((resolve) => {
|
||||
const check = () => {
|
||||
const now = Date.now()
|
||||
if (now - start >= options.capMs) return resolve()
|
||||
if (served > 0 && inflight === 0 && now - lastRequest >= options.quietMs) return resolve()
|
||||
setTimeout(check, 5)
|
||||
}
|
||||
check()
|
||||
})
|
||||
}
|
||||
|
||||
export function registerRendererProtocol(rendererRoot: string) {
|
||||
if (protocol.isProtocolHandled(rendererProtocol)) return
|
||||
|
||||
protocol.handle(rendererProtocol, async (request) => {
|
||||
return serve(request, rendererRoot)
|
||||
inflight++
|
||||
lastRequest = Date.now()
|
||||
try {
|
||||
return await serve(request, rendererRoot)
|
||||
} finally {
|
||||
inflight--
|
||||
served++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -60,4 +88,3 @@ function addDocumentPolicy(response: Response, file: string) {
|
||||
headers.set(documentPolicyHeader, jsCallStacksDocumentPolicy)
|
||||
return new Response(response.body, { status: response.status, statusText: response.statusText, headers })
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user