Leading the development of electron-react-boilerplate for over 5 years has taught me quite a bit about performance challenges with electron and optimizing electron apps. Contrary to popular belief, I believe electron apps can perform as well as native desktop apps.
Before reading this, it's important to recognize that applications will certainly benefit a lot more from fundamental architectural improvements as opposed to the optimizations discussed here. If you'd like to read more about architectural improvements, read the slack posts in the "Classic Electron and JS Perf Readings" section below.
Strategies for Performance Improvement
Over the course of maintaining electron-react-boilerplate, I've noted the following strategies as the most impactful improvements to be made to electron apps.
1. Always use a bundler
require() calls often bottleneck the initialization time of your app. The
require algorithm is recursive and synchronous (so it blocks the main thread).
Sam Saccone, a performance engineer at Google, debugged Atom's startup performance:
Most of the startup time is spent requiring modules and deadlocked in node.js execution before the initial paint in the browser is ever fired, resulting in slow feeling startup experience for end users, by potentially bundling all of the dependencies into one file there could be a sizeable perf savings.
Don't go down this path. Use a bundler!
2. Defer non critical imports
This can be done in a number of ways. One way of reducing the up front cost of loading your application is to use route-based code splitting and an app shell architecture. While working as a consultant for companies that use electron-react-boilerplate, I've used route-based code splitting optimizations to bring down startup time from ~10sec to ~3sec.
3. Debug Devtools and Content Tracing
Applying traditional browser performance debugging techniques to Electron apps is a good starting point.
4. Fallback to WebAssembly or native node modules
5. V8 Snapshots
Snpashots are a feature of V8, Chrome's JS engine, which allows snapshotting an initialized heap and reusing it. This avoids reinitializing objects on the heap every time your app starts up. The V8 team wrote a great article on V8 Snapshots, which I highly recommend reading. I'd also recommend checking out electron-link, a tool that allows leveraging V8 Snapshots to improve electron's startup performance.
📚 More Readings
For those who want to read more in-depth articles on optimizing the performance of Electron and browser apps, I recommend checking out the case studies and perf readings below.
🙇♀️ Case Studies
📖 Classic Electron and JS Perf Readings
- VSCode's Text Buffer Reimplementation
- Seena Burns: Debugging Electron Memory Usage
- Seena Burns: Thumbnailing is Isolate: Background Workers in Electron
- Maybe you don't need Rust and WASM to speed up your JS
- Oxidizing Source Maps with Rust and WebAssembly
- Slack: Chrome Tracing for Fun and Profit
- Performance Calendar: A collection of front end performance posts
- How we parallelized our node service by 30x
- 600k concurrent websocket connections on AWS using Node.js
- I have been underestimating JS Performance