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

Electron Performance Optimization Techniques

2月18日 10:39

Electron applications consume significant system resources by default due to the integration of Chromium and Node.js. Through reasonable optimization strategies, you can significantly improve application performance and user experience.

Reducing Application Size

1. Exclude Unnecessary Files

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

2. Use Production Dependencies

bash
# Development environment npm install --save-dev electron electron-builder # Production environment - only install necessary dependencies npm install --production

3. Compress Resources

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}" ] } }

Memory Optimization

1. Lazy Load Modules

javascript
// Bad practice - Load all modules at the top const heavyModule = require('heavy-module') function useHeavyModule() { heavyModule.doSomething() } // Good practice - Load on demand function useHeavyModule() { const heavyModule = require('heavy-module') heavyModule.doSomething() }

2. Use Dynamic Import

javascript
// Renderer process async function loadFeature() { const { feature } = await import('./heavy-feature.js') feature.init() }

3. Clean Up Unused Objects

javascript
// Clean up event listeners in time function setupListeners() { const handler = () => console.log('Event') window.addEventListener('resize', handler) // Clean up when component unmounts return () => window.removeEventListener('resize', handler) } // Usage const cleanup = setupListeners() // When no longer needed cleanup()

4. Limit Cache Size

javascript
// Use LRU cache const LRU = require('lru-cache') const cache = new LRU({ max: 500, // Maximum number of cache items maxAge: 1000 * 60 * 5 // Expire after 5 minutes }) function getData(key) { const cached = cache.get(key) if (cached) return cached const data = fetchData(key) cache.set(key, data) return data }

Rendering Performance Optimization

1. Use Virtual Lists

javascript
// Use react-window or 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. Avoid Frequent DOM Operations

javascript
// Bad practice for (let i = 0; i < 1000; i++) { document.body.appendChild(createElement(i)) } // Good practice - Use document fragment const fragment = document.createDocumentFragment() for (let i = 0; i < 1000; i++) { fragment.appendChild(createElement(i)) } document.body.appendChild(fragment)

3. Use requestAnimationFrame

javascript
// Bad practice function animate() { element.style.left = position + 'px' position += 1 setTimeout(animate, 16) } // Good practice function animate() { element.style.left = position + 'px' position += 1 requestAnimationFrame(animate) }

4. Optimize Image Loading

javascript
// Use lazy loading const img = new Image() img.loading = 'lazy' img.src = 'image.jpg' // Use WebP format const supportsWebP = document.createElement('canvas') .toDataURL('image/webp') .indexOf('data:image/webp') === 0 const imageFormat = supportsWebP ? 'webp' : 'jpg' img.src = `image.${imageFormat}`

Process Optimization

1. Share Resources When Using Multiple Windows

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

2. Use Worker Threads for Intensive Tasks

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. Use Child Processes for Independent Tasks

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}`)) } }) }) }

Network Optimization

1. Use Service Worker Caching

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. Use 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. Optimize API Requests

javascript
// Use request batching 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) } // Periodic flush setInterval(flushRequests, 100)

Startup Optimization

1. Lazy Load Non-Critical Resources

javascript
// main.js app.whenReady().then(() => { const mainWindow = new BrowserWindow({ show: false // Don't show initially }) mainWindow.loadFile('index.html') // Show after page loads mainWindow.once('ready-to-show', () => { mainWindow.show() }) })

2. Preload Common Data

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() // Use initial data })

3. Use Code Splitting

javascript
// Use dynamic import for code splitting async function loadFeature() { const { default: Feature } = await import('./features/feature.js') new Feature() }

Monitoring and Debugging

1. Use Chrome DevTools

javascript
// main.js const mainWindow = new BrowserWindow({ webPreferences: { devTools: true } }) // Auto-open DevTools in development if (process.env.NODE_ENV === 'development') { mainWindow.webContents.openDevTools() }

2. Performance Profiling

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. Memory Monitoring

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)

Best Practices Summary

  1. Reduce Application Size: Exclude unnecessary files, use production dependencies, compress resources
  2. Memory Optimization: Lazy loading, timely cleanup, limit cache
  3. Rendering Optimization: Virtual lists, reduce DOM operations, use RAF
  4. Process Optimization: Share resources, use Worker Threads
  5. Network Optimization: Service Worker, HTTP/2, request batching
  6. Startup Optimization: Lazy loading, preload data, code splitting
  7. Monitoring and Debugging: DevTools, performance profiling, memory monitoring

Common Questions

Q: What to do if Electron application uses too much memory?A: Check for memory leaks, use lazy loading, limit cache size, clean up unused objects in time.

Q: How to improve application startup speed?A: Lazy load non-critical resources, preload common data, use code splitting, optimize initialization logic.

Q: How to implement virtual lists?A: Use libraries like react-window or react-virtualized to only render elements within the visible area.

Q: How to detect memory leaks?A: Use Chrome DevTools Memory panel, regularly monitor memory usage, check if event listeners and timers are properly cleaned up.

标签:Electron