09 · 性能优化清单(官方 8 条详解)
官方性能文档完整整理。核心心法:Profile → 找瓶颈 → 优化 → 重复,清单只是起点,VS Code/Slack 都是这么做的。
总方法论
- 通用 Web 性能技巧 + Node 性能技巧都适用,但”性能”定义不同:桌面端在意启动速度、响应性、内存占用,而非 QPS
- 最可靠的策略:分析运行代码 → 找最耗资源处 → 优化,循环往复
- 工具:Chrome DevTools(Performance/Memory 面板)、Chrome Tracing(多进程分析)
- 推荐:DevTools 性能分析文档;演讲《VS Code - The First Second》
清单总览
| # | 条目 | 一句话 |
|---|---|---|
| 1 | 谨慎加载模块 | 装依赖前先量化:多少传递依赖、加载多少资源 |
| 2 | 别过早加载/执行代码 | 懒加载,按用户操作时序错开 |
| 3 | 别阻塞主进程 | 主进程是控制中心,阻塞 = 整个应用冻结 |
| 4 | 别阻塞渲染进程 | requestIdleCallback + Web Workers |
| 5 | 不必要的 polyfill | Chromium 版本确定,原生特性别用 JS 模拟 |
| 6 | 不必要的网络请求 | 不变的资源打包进应用,离线可用 |
| 7 | 打包你的代码 | 减少 require() 开销,bundle 成单文件 |
| 8 | 不要默认菜单就 Menu.setApplicationMenu(null) | 启动期省掉菜单构建 |
1. 谨慎加载模块
为什么:官方反面案例——一个 isOnline() 小模块,传递依赖链最终加载并解析 10 万行端口信息 JSON,低端机上占几秒。为 Node 服务器设计的模块(启动时全量加载换响应速度)对桌面应用是坏消息。
怎么做:加依赖前查三点——依赖树大小、require() 时加载的资源、资源是否真的需要。量化命令:
node --cpu-prof --heap-prof -e "require('request')"
# 生成 .cpuprofile / .heapprofile,用 DevTools 的 Performance / Memory 面板打开官方实测:加载 request 约 0.5s;node-fetch 内存极少且 <50ms。
2. 过早加载和执行代码
为什么:require() 很重(尤其 Windows)。启动时别做检查更新、预下载、大量磁盘 I/O 等用户当下不需要的事。VS Code 案例:先显示无高亮的文件内容保证可交互,再做语法高亮。
怎么做:懒加载模式——
// 反例:文件一加载就同步读目录 + 引入重模块
const fooParser = require('foo-parser')
class Parser {
constructor () { this.files = fs.readdirSync('.') }
}
// 正例:推迟到真正调用时,且用异步 API
class Parser {
async getFiles () {
this.files = this.files || await fs.promises.readdir('.')
return this.files
}
async getParsedFiles () {
const fooParser = require('foo-parser') // require 有模块缓存,只付一次开销
return fooParser.parse(await this.getFiles())
}
}原则:按需分配资源,不在启动时全量分配。
3. 阻塞主进程(最严重)
为什么:主进程是所有进程的父进程 + 操作系统交互中枢 + UI 线程所在。鼠标点击、窗口动画都要经过它——阻塞主进程 = 整个应用冻结。
怎么做:
- CPU 密集长任务 →
worker_threads,或移到 BrowserWindow,最后手段才起专用进程(UtilityProcess,见 02-process-model) - 避免同步 IPC(
sendSync)和@electron/remote——极易不知不觉阻塞 UI 线程(见 03-ipc 旧方法警示) fs/child_process等有同步+异步双版本的模块,一律用异步版
4. 阻塞渲染进程
为什么:滚动流畅、输入响应、60fps 动画都靠渲染进程不被长任务占据。
怎么做:
- 小任务:
requestIdleCallback()——进程空闲期才执行 - 长任务 / CPU 密集(如解析):Web Workers(独立线程),注意 Electron 的多线程文档里的限制
5. 不必要的 polyfill
为什么:Electron 的渲染引擎版本是确定的,不像 Web 要迁就最老浏览器。JS 版 polyfill 比原生实现慢。
怎么做:
- 假定当前 Electron 不需要 polyfill;存疑查 caniuse.com 对照你的 Electron 对应的 Chromium 版本(
process.versions.chrome) - 审查三方库:
jQuery的大部分功能已是标准 JS(youmightnotneedjquery.com) - TypeScript 编译目标设为 Electron 支持的最新 ECMAScript 版本
6. 不必要的或阻塞的网络请求
为什么:桌面应用应尽量离线可用。典型反例:Google Fonts 走 CDN——字体下载下来打包进应用更好。
怎么做:
- DevTools → Network → 勾选
Disable cache→ 重载(Cmd+R),盘点所有请求,优先处理大文件;不变的图片/字体/媒体打包进应用 - 开 Network Throttling(选
Fast 3G),重载看应用是否白等不必要的资源 - 需要不发版更新的内容才从网络加载,进一步可用 Service Worker 控制
7. 打包你的代码
为什么:require() 开销重(见第 2 条)。把代码打包成单文件,require 开销只在应用加载时付一次。
怎么做:选能同时处理 Node 与浏览器两种环境的打包器(Electron 特殊性)。官方提及:webpack / Parcel / rollup。本 wiki 工具链里 electron-vite(三进程统一)是 Electron 专属的现代选择。
8. 不用默认菜单就 Menu.setApplicationMenu(null)
为什么:Electron 启动时默认构建一套标准菜单;自建菜单或无边框窗口时这是纯浪费。
怎么做:在 app.on('ready') 之前调用 Menu.setApplicationMenu(null)(见 electron#35512)。
排查顺序(应用”慢”时)
- 启动慢 → 第 1、2、7 条(依赖/懒加载/打包)
- 交互卡顿 → 第 3、4 条(阻塞排查:DevTools Performance 看长任务,主进程用
--inspect) - 体积/内存大 → 第 1、5 条 +
npm ls审依赖 - 白屏等网络 → 第 6 条(Network 面板 + Throttling)
相关
- 06-security-performance — 安全清单 + 发布前 Checklist(性能节已链到本页)
- 02-process-model — UtilityProcess / worker_threads 的运行环境
- 03-ipc — 避免
sendSync阻塞 - electron-vite — 第 7 条的现代打包方案