Electron 性能优化
启动速度、内存占用、渲染卡顿的定位与优化手段。
Key Points先量再改:启动慢用 console.time 分段计时,卡顿用 Performance 面板,内存用 process.memoryUsage。
启动优化
JavaScript
// 主进程里分段计时,找出真正的瓶颈
console.time('app-ready');
app.whenReady().then(() => {
console.timeEnd('app-ready');
console.time('window-created');
const win = createWindow();
win.once('ready-to-show', () => console.timeEnd('window-created'));
});- 延后非关键工作 —— 检查更新、读配置、建数据库连接放到窗口显示之后
- 先显示骨架 ——
show: false+ready-to-show只解决白屏,界面本身要轻 - 减少预加载工作量 —— 预加载里不要
require大库 - 用 V8 代码缓存 —— 打包时生成
v8-compile-cache可省解析时间
JavaScript
// 主进程不要阻塞:任何耗时超过 50ms 的同步操作都要挪走
app.whenReady().then(() => {
const win = createWindow(); // 先把窗口弄出来
win.once('ready-to-show', () => {
win.show();
// 再做次要的事
checkForUpdates();
warmUpDatabase();
});
});内存
JavaScript
// 主进程
console.log(process.memoryUsage());
// { rss, heapTotal, heapUsed, external, arrayBuffers }
// 渲染进程:DevTools → Memory → Take heap snapshot| Symptom | Likely cause |
|---|---|
| Memory grows on navigation | 监听器没解绑、定时器没清 |
| Huge after opening files | readFile 整读进内存 |
| Never released | 闭包持有 DOM 或大对象 |
| Multiple windows | 每个窗口一个渲染进程,天然占内存 |
渲染性能
JavaScript
// 长列表用虚拟滚动,只渲染可见的几十行
// 大计算挪到 Web Worker,别占主线程
const worker = new Worker('heavy.js');
worker.postMessage({ file: path });
worker.onmessage = (e) => render(e.data);
// 批量 DOM 更新,避免逐条改
const frag = document.createDocumentFragment();
rows.forEach(r => frag.appendChild(makeRow(r)));
list.appendChild(frag);The renderer main thread handles both JS and layout, so tens of milliseconds of synchronous work drops frames and lags input. Moving computation into a Worker is the direct fix.
打包体积
JavaScript
// electron-builder 的 files 字段:只打需要的
{
"build": {
"files": [
"dist/**/*",
"main.js",
"preload.js",
"!**/*.map",
"!**/node_modules/*/{test,__tests__,docs,example}/**"
],
"asar": true
}
}- **
asar: true** —— 把源码打成一个归档,减少小文件 IO - 排除 source map 与测试目录 —— 常常能省几十 MB
- 按平台裁剪 —— 只为目标平台下载 Electron 二进制
- 依赖审查 ——
dependencies里的每个包都会进产物
asar is packaging, not encryption: users can extract the source. Protecting logic needs obfuscation or moving it server-side — asar stops nobody.优化顺序
- 先测 —— 用真实设备、真实数据量,别凭感觉
- 找瓶颈 —— 启动慢还是操作卡,是两个完全不同的问题
- 改一处 —— 一次只改一个变量,否则不知道哪个起了作用
- 复测 —— 优化后必须再量一次,确认真的快了
启动优化
JavaScript
// 主进程里分段计时,找出真正的瓶颈
console.time('app-ready');
app.whenReady().then(() => {
console.timeEnd('app-ready');
console.time('window-created');
const win = createWindow();
win.once('ready-to-show', () => console.timeEnd('window-created'));
});- 延后非关键工作 —— 检查更新、读配置、建数据库连接放到窗口显示之后
- 先显示骨架 ——
show: false+ready-to-show只解决白屏,界面本身要轻 - 减少预加载工作量 —— 预加载里不要
require大库 - 用 V8 代码缓存 —— 打包时生成
v8-compile-cache可省解析时间
JavaScript
// 主进程不要阻塞:任何耗时超过 50ms 的同步操作都要挪走
app.whenReady().then(() => {
const win = createWindow(); // 先把窗口弄出来
win.once('ready-to-show', () => {
win.show();
// 再做次要的事
checkForUpdates();
warmUpDatabase();
});
});内存
JavaScript
// 主进程
console.log(process.memoryUsage());
// { rss, heapTotal, heapUsed, external, arrayBuffers }
// 渲染进程:DevTools → Memory → Take heap snapshot| 现象 | 可能原因 |
|---|---|
| 切换页面内存涨 | 监听器没解绑、定时器没清 |
| 打开大文件后暴涨 | readFile 整读进内存 |
| 一直不释放 | 闭包持有 DOM 或大对象 |
| 多窗口 | 每个窗口一个渲染进程,天然占内存 |
渲染性能
JavaScript
// 长列表用虚拟滚动,只渲染可见的几十行
// 大计算挪到 Web Worker,别占主线程
const worker = new Worker('heavy.js');
worker.postMessage({ file: path });
worker.onmessage = (e) => render(e.data);
// 批量 DOM 更新,避免逐条改
const frag = document.createDocumentFragment();
rows.forEach(r => frag.appendChild(makeRow(r)));
list.appendChild(frag);渲染进程的主线程同时负责 JS 和布局。一段几十毫秒的同步计算就会让动画掉帧、输入卡顿——把计算放进 Worker 是最直接的解法。
打包体积
JavaScript
// electron-builder 的 files 字段:只打需要的
{
"build": {
"files": [
"dist/**/*",
"main.js",
"preload.js",
"!**/*.map",
"!**/node_modules/*/{test,__tests__,docs,example}/**"
],
"asar": true
}
}- **
asar: true** —— 把源码打成一个归档,减少小文件 IO - 排除 source map 与测试目录 —— 常常能省几十 MB
- 按平台裁剪 —— 只为目标平台下载 Electron 二进制
- 依赖审查 ——
dependencies里的每个包都会进产物
asar 只是打包不是加密,用户能直接解出源码。需要保护逻辑就得用代码混淆或把关键计算放服务端——asar 挡不住任何人。优化顺序
- 先测 —— 用真实设备、真实数据量,别凭感觉
- 找瓶颈 —— 启动慢还是操作卡,是两个完全不同的问题
- 改一处 —— 一次只改一个变量,否则不知道哪个起了作用
- 复测 —— 优化后必须再量一次,确认真的快了