乐闻世界logo
搜索文章和话题

Electron 性能优化技巧

2月18日 10:39

Electron 应用由于集成了 Chromium 和 Node.js,默认情况下会占用较多系统资源。通过合理的优化策略,可以显著提升应用性能和用户体验。

减少应用体积

1. 排除不必要的文件

json
// package.json { "build": { "files": [ "build/**/*", "node_modules/**/*", "package.json" ], "asar": true, "asarUnpack": [ "node_modules/some-native-module/**/*" ] } }

2. 使用生产依赖

bash
# 开发环境 npm install --save-dev electron electron-builder # 生产环境只安装必要的依赖 npm install --production

3. 压缩资源

json
{ "build": { "compression": "maximum", "files": [ "!**/node_modules/*/{TEST,test,tests,__tests__,examples,example}/**", "!**/node_modules/.bin", "!**/*.{iml,o,hprof,orig,pyc,pyo,rbc,swp,csproj,sln,xproj}", "!.editorconfig", "!**/._*", "!**/{.DS_Store,.git,.hg,.svn,CVS,RCS,SCCS,.gitignore,.gitattributes}", "!**/{__pycache__,thumbs.db,.flowconfig,.idea,.vs,.nyc_output}", "!**/{appveyor.yml,.travis.yml,circle.yml}", "!**/{npm-debug.log,yarn.lock,.yarn-integrity,.yarn-metadata.json}" ] } }

内存优化

1. 延迟加载模块

javascript
// 不好的做法 - 在文件顶部加载所有模块 const heavyModule = require('heavy-module') function useHeavyModule() { heavyModule.doSomething() } // 好的做法 - 按需加载 function useHeavyModule() { const heavyModule = require('heavy-module') heavyModule.doSomething() }

2. 使用动态 import

javascript
// 渲染进程 async function loadFeature() { const { feature } = await import('./heavy-feature.js') feature.init() }

3. 清理不再使用的对象

javascript
// 及时清理事件监听器 function setupListeners() { const handler = () => console.log('Event') window.addEventListener('resize', handler) // 组件卸载时清理 return () => window.removeEventListener('resize', handler) } // 使用 const cleanup = setupListeners() // 不再需要时 cleanup()

4. 限制缓存大小

javascript
// 使用 LRU 缓存 const LRU = require('lru-cache') const cache = new LRU({ max: 500, // 最大缓存项数 maxAge: 1000 * 60 * 5 // 5分钟过期 }) function getData(key) { const cached = cache.get(key) if (cached) return cached const data = fetchData(key) cache.set(key, data) return data }

渲染性能优化

1. 使用虚拟列表

javascript
// 使用 react-window 或 react-virtualized import { FixedSizeList as List } from 'react-window' const Row = ({ index, style }) => ( <div style={style}>Row {index}</div> ) const VirtualList = () => ( <List height={600} itemCount={10000} itemSize={35} width={300} > {Row} </List> )

2. 避免频繁的 DOM 操作

javascript
// 不好的做法 for (let i = 0; i < 1000; i++) { document.body.appendChild(createElement(i)) } // 好的做法 - 使用文档片段 const fragment = document.createDocumentFragment() for (let i = 0; i < 1000; i++) { fragment.appendChild(createElement(i)) } document.body.appendChild(fragment)

3. 使用 requestAnimationFrame

javascript
// 不好的做法 function animate() { element.style.left = position + 'px' position += 1 setTimeout(animate, 16) } // 好的做法 function animate() { element.style.left = position + 'px' position += 1 requestAnimationFrame(animate) }

4. 优化图片加载

javascript
// 使用懒加载 const img = new Image() img.loading = 'lazy' img.src = 'image.jpg' // 使用 WebP 格式 const supportsWebP = document.createElement('canvas') .toDataURL('image/webp') .indexOf('data:image/webp') === 0 const imageFormat = supportsWebP ? 'webp' : 'jpg' img.src = `image.${imageFormat}`

进程优化

1. 使用多窗口时共享资源

javascript
// main.js const sharedSession = session.defaultSession const window1 = new BrowserWindow({ webPreferences: { session: sharedSession } }) const window2 = new BrowserWindow({ webPreferences: { session: sharedSession } })

2. 使用 Worker Threads 处理密集任务

javascript
// main.js const { Worker } = require('worker_threads') ipcMain.handle('heavy-computation', async (event, data) => { return new Promise((resolve, reject) => { const worker = new Worker('./worker.js', { workerData: data }) worker.on('message', resolve) worker.on('error', reject) worker.on('exit', (code) => { if (code !== 0) reject(new Error(`Worker stopped with exit code ${code}`)) }) }) }) // worker.js const { parentPort, workerData } = require('worker_threads') const result = performHeavyComputation(workerData) parentPort.postMessage(result)

3. 使用子进程处理独立任务

javascript
// main.js const { spawn } = require('child_process') function runTask(data) { return new Promise((resolve, reject) => { const child = spawn('node', ['task.js', JSON.stringify(data)]) let output = '' child.stdout.on('data', (data) => { output += data.toString() }) child.on('close', (code) => { if (code === 0) { resolve(JSON.parse(output)) } else { reject(new Error(`Process exited with code ${code}`)) } }) }) }

网络优化

1. 使用 Service Worker 缓存

javascript
// sw.js self.addEventListener('install', (event) => { event.waitUntil( caches.open('v1').then((cache) => { return cache.addAll([ '/', '/styles/main.css', '/scripts/main.js' ]) }) ) }) self.addEventListener('fetch', (event) => { event.respondWith( caches.match(event.request).then((response) => { return response || fetch(event.request) }) ) })

2. 使用 HTTP/2

javascript
// main.js app.on('ready', () => { session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => { details.requestHeaders['Upgrade-Insecure-Requests'] = '1' callback({ requestHeaders: details.requestHeaders }) }) })

3. 优化 API 请求

javascript
// 使用请求批处理 const batchRequests = [] function scheduleRequest(request) { batchRequests.push(request) if (batchRequests.length >= 10) { flushRequests() } } function flushRequests() { const requests = batchRequests.splice(0, batchRequests.length) ipcRenderer.invoke('batch-request', requests) } // 定期刷新 setInterval(flushRequests, 100)

启动优化

1. 延迟加载非关键资源

javascript
// main.js app.whenReady().then(() => { const mainWindow = new BrowserWindow({ show: false // 初始不显示 }) mainWindow.loadFile('index.html') // 页面加载完成后再显示 mainWindow.once('ready-to-show', () => { mainWindow.show() }) })

2. 预加载常用数据

javascript
// preload.js const { contextBridge, ipcRenderer } = require('electron') contextBridge.exposeInMainWorld('electronAPI', { getInitialData: () => ipcRenderer.invoke('get-initial-data') }) // renderer.js window.addEventListener('DOMContentLoaded', async () => { const initialData = await window.electronAPI.getInitialData() // 使用初始数据 })

3. 使用代码分割

javascript
// 使用动态 import 进行代码分割 async function loadFeature() { const { default: Feature } = await import('./features/feature.js') new Feature() }

监控和调试

1. 使用 Chrome DevTools

javascript
// main.js const mainWindow = new BrowserWindow({ webPreferences: { devTools: true } }) // 开发环境自动打开 DevTools if (process.env.NODE_ENV === 'development') { mainWindow.webContents.openDevTools() }

2. 性能分析

javascript
// main.js mainWindow.webContents.on('did-finish-load', () => { mainWindow.webContents.executeJavaScript(` const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { console.log(entry.name, entry.duration) } }) observer.observe({ entryTypes: ['measure'] }) `) })

3. 内存监控

javascript
// main.js setInterval(() => { const memoryUsage = process.memoryUsage() console.log('Memory Usage:', { rss: `${Math.round(memoryUsage.rss / 1024 / 1024)} MB`, heapTotal: `${Math.round(memoryUsage.heapTotal / 1024 / 1024)} MB`, heapUsed: `${Math.round(memoryUsage.heapUsed / 1024 / 1024)} MB`, external: `${Math.round(memoryUsage.external / 1024 / 1024)} MB` }) }, 30000)

最佳实践总结

  1. 减少应用体积: 排除不必要文件、使用生产依赖、压缩资源
  2. 内存优化: 延迟加载、及时清理、限制缓存
  3. 渲染优化: 虚拟列表、减少 DOM 操作、使用 RAF
  4. 进程优化: 共享资源、使用 Worker Threads
  5. 网络优化: Service Worker、HTTP/2、请求批处理
  6. 启动优化: 延迟加载、预加载数据、代码分割
  7. 监控调试: DevTools、性能分析、内存监控

常见问题

Q: Electron 应用内存占用过高怎么办?A: 检查是否有内存泄漏、使用延迟加载、限制缓存大小、及时清理不再使用的对象。

Q: 如何提高应用启动速度?A: 延迟加载非关键资源、预加载常用数据、使用代码分割、优化初始化逻辑。

Q: 虚拟列表如何实现?A: 使用 react-window 或 react-virtualized 等库,只渲染可视区域内的元素。

Q: 如何检测内存泄漏?A: 使用 Chrome DevTools 的 Memory 面板、定期监控内存使用情况、检查事件监听器和定时器是否正确清理。

标签:Electron