How to Speed Up an Electron App Startup from 10 seconds to 1 second
This article explains how to measure, analyze, and optimize the startup performance of an Electron‑based cash‑register application, covering main‑process timing, renderer white‑screen reduction, code‑splitting, tree‑shaking, require caching, V8 snapshots, and user‑perceived improvements such as skeleton screens.
Background
Some cash‑register terminals run Electron apps that start slowly because of old hardware, low‑end configurations, or antivirus interference. The goal is to make the app launch quickly even on such machines.
Optimization Ideas
Measure to set a concrete target and identify bottlenecks.
Focus on reducing the time to create the main‑process window and to display the renderer as soon as possible.
Make the experience feel faster after the performance improvements.
Report timing for each stage and set up monitoring to catch regressions.
Measurement
Measuring the Main Process
Create a batch file in the app root to launch the program and record the initial start time:
@echo off
set "$=%temp%\Spring"
>%$% Echo WScript.Echo((new Date()).getTime())
for /f %%a in ('cscript -nologo -e:jscript %$%') do set timestamp=%%a
del /f /q %$%
echo %timestamp%
start yourAppName.exe
pauseWithin the project you can log main‑process milestones using:
this.window.webContents.executeJavaScript(`
console.log('start', ${start});
console.log('onReady', ${onReady});
console.log('inCreateWindow', ${inCreateWindow});
console.log('afterCreateWindow', ${afterCreateWindow});
console.log('beforeInitEvents', ${beforeInitEvents});
console.log('afterInitEvents', ${afterInitEvents});
console.log('startLoad', ${startLoad});
`);If abnormal delays appear, use v8‑inspect‑profiler to generate a flame‑graph.
Measuring the Renderer Process
Log timestamps via console or the Performance API, and use Chrome DevTools to measure white‑screen time.
Results
In the simplest case (only window creation logic) the timestamps are:
exe execution 1677666141619
start main code 1677666142152 (+533 ms)
main ready 1677666142224 (+72 ms)
init renderer window 1677666142364 (+140 ms)
load renderer assets 1677666142375 (+11 ms)In the unoptimized state the main‑process delay between exe launch and code start is about 2.8 seconds, and the renderer white‑screen is ~1 second.
The optimization target is to match the simplest case by reducing main‑process start time and renderer white‑screen.
Optimization Steps
1. Make Main‑Process Code Execute Faster
Package, compress, and enable tree‑shaking to keep the bundle size minimal; load dependencies on demand.
Code Compression
Electron ships with a recent Chrome, so you can target es2020 without polyfills.
Tree‑Shaking
Avoid attaching configuration objects to global so they can be eliminated during bundling.
const exendsGlobal = {
__DEV__,
__APP_DIR__,
__RELEASE__,
__TEST__,
__LOCAL__,
__CONFIG_FILE__,
__LOG_DRI__,
GM_BUILD_ENV: JSON.stringify(process.env.GM_BUILD_ENV),
};
// Bad for tree‑shaking
Object.assign(global, exendsGlobal);Avoid Heavy Shortcut Registration
Registering global shortcuts adds overhead; replace with a triple‑click handler or defer registration.
globalShortcut.register('CommandOrControl+I', () => {
this.window.webContents.openDevTools();
});Optimize require
Measure require time with:
node --cpu-prof --heap-prof -e "require('request')"Typical timings (ms): fs‑extra 83, event‑kit 25, electron‑store 197, electron‑log 61, v8‑compile‑cache 29.
Strategies:
Bundle required packages (increases bundle size).
Load packages on demand.
Use v8‑compile‑cache or V8 snapshots.
Reduce startup transactions to lower overall require count.
On‑Demand require Example
const noop = () => {};
const proxyFsExtra = new Proxy({}, {
get(target, property) {
return new Proxy(noop, {
apply(_, __, args) {
const fsEx = require('fs-extra');
return fsEx[property](...args);
}
});
}
});
export default proxyFsExtra;Measured improvement: main‑process start time reduced by ~700 ms.
v8‑compile‑cache
Cache compiled bytecode to skip recompilation. In this project the benefit was negligible because the initial require count is already low.
Module.prototype._compile = function(content, filename) {
// read cache
var buffer = this._cacheStore.get(filename, invalidationKey);
var script = new vm.Script(wrapper, {
filename,
lineOffset: 0,
displayErrors: true,
cachedData: buffer,
produceCachedData: true,
});
if (script.cachedDataProduced) {
this._cacheStore.set(filename, invalidationKey, script.cachedData);
}
var compiledWrapper = script.runInThisContext({ filename, lineOffset: 0, columnOffset: 0, displayErrors: true });
...
};v8‑snapshot
Serialize the memory state after code execution and load it at startup, skipping both compilation and execution. It works well for third‑party libraries but not for code with side effects.
2. Reorder Main‑Process Workflow
Split the bundle and execute only the essential code first, deferring non‑critical work.
New timing shows the main‑process ready event occurs within ~90 ms after code start.
3. Accelerate Renderer Rendering
Use requestIdleCallback for low‑priority tasks.
Remove or refactor synchronous sendSync calls and heavy electron‑store usage.
Load only the CSS required for the first screen; use Tailwind’s purge feature.
Avoid modulepreload when it slows down the first paint.
CSS size directly impacts render performance.
4. Improve Perceived Speed
Show a skeleton screen (a lightweight C++ loading exe) while the renderer initializes, then hide it once the UI is ready.
Adding a single line of text to the DOM can noticeably reduce white‑screen time because the browser prioritises visible information.
Other Considerations
Upgrade Electron to benefit from upstream performance fixes.
Continuously monitor startup metrics; regressions should trigger re‑optimization.
Conclusion
The described optimizations reduced the perceived startup time of the cash‑register app on a low‑end machine from about 10 seconds to roughly 1 second. The most effective measure was workflow reordering and bundle splitting, while techniques like code‑cache and snapshots were only useful after careful measurement.
Goodme Frontend Team
Regularly sharing the team's insights and expertise in the frontend field
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
