Skip to content

第8章:性能优化与安全

学习 Electron 应用的性能优化技巧和安全最佳实践,构建高质量的桌面应用

8.1 性能优化策略

Electron 应用的性能优化需要从多个维度考虑,包括启动速度、内存使用、CPU 占用和用户体验。

应用启动优化

javascript
// 主进程启动优化
const { app, BrowserWindow } = require('electron')
const path = require('path')

class StartupOptimizer {
  constructor() {
    this.preloadedWindows = new Map()
    this.setupOptimizations()
  }

  setupOptimizations() {
    // 1. 禁用不必要的功能
    app.disableHardwareAcceleration() // 如果不需要硬件加速
    app.disableDomainBlockingFor3DAPIs() // 禁用 3D API 域名阻止
    
    // 2. 设置应用用户模型 ID (Windows)
    if (process.platform === 'win32') {
      app.setAppUserModelId('com.yourcompany.yourapp')
    }

    // 3. 预加载关键资源
    app.whenReady().then(() => {
      this.preloadCriticalResources()
      this.createMainWindow()
    })
  }

  async preloadCriticalResources() {
    // 预加载字体
    await this.preloadFonts()
    
    // 预加载图片资源
    await this.preloadImages()
    
    // 预热 V8 引擎
    this.warmupV8()
  }

  async preloadFonts() {
    // 预加载自定义字体
    const fontPaths = [
      path.join(__dirname, 'assets/fonts/custom-font.woff2')
    ]
    
    // 在主进程中预加载字体文件
    for (const fontPath of fontPaths) {
      try {
        const fs = require('fs')
        await fs.promises.readFile(fontPath)
      } catch (error) {
        console.warn('字体预加载失败:', fontPath)
      }
    }
  }

  async preloadImages() {
    // 预加载关键图片
    const { nativeImage } = require('electron')
    const imagePaths = [
      path.join(__dirname, 'assets/icons/app-icon.png'),
      path.join(__dirname, 'assets/images/splash.png')
    ]
    
    for (const imagePath of imagePaths) {
      try {
        nativeImage.createFromPath(imagePath)
      } catch (error) {
        console.warn('图片预加载失败:', imagePath)
      }
    }
  }

  warmupV8() {
    // 执行一些 JavaScript 代码来预热 V8 引擎
    const warmupCode = `
      const arr = new Array(1000).fill(0).map((_, i) => i)
      const sum = arr.reduce((a, b) => a + b, 0)
      JSON.stringify({ sum, timestamp: Date.now() })
    `
    
    try {
      eval(warmupCode)
    } catch (error) {
      console.warn('V8 预热失败:', error)
    }
  }

  createMainWindow() {
    const mainWindow = new BrowserWindow({
      width: 1200,
      height: 800,
      show: false, // 延迟显示
      webPreferences: {
        nodeIntegration: false,
        contextIsolation: true,
        preload: path.join(__dirname, 'preload.js'),
        // 性能优化选项
        backgroundThrottling: false, // 禁用后台节流
        offscreen: false, // 禁用离屏渲染
        enableWebSQL: false, // 禁用 WebSQL
        webgl: false, // 如果不需要 WebGL
        plugins: false, // 禁用插件
        experimentalFeatures: false // 禁用实验性功能
      }
    })

    // 页面准备就绪后再显示
    mainWindow.once('ready-to-show', () => {
      mainWindow.show()
      
      // 可选:添加淡入效果
      mainWindow.setOpacity(0)
      mainWindow.show()
      
      let opacity = 0
      const fadeIn = setInterval(() => {
        opacity += 0.1
        mainWindow.setOpacity(opacity)
        if (opacity >= 1) {
          clearInterval(fadeIn)
        }
      }, 16) // 60fps
    })

    mainWindow.loadFile('index.html')
    return mainWindow
  }

  // 窗口池管理
  createWindowPool(count = 2) {
    for (let i = 0; i < count; i++) {
      const window = new BrowserWindow({
        show: false,
        webPreferences: {
          nodeIntegration: false,
          contextIsolation: true,
          preload: path.join(__dirname, 'preload.js')
        }
      })
      
      this.preloadedWindows.set(`pool_${i}`, window)
    }
  }

  getPooledWindow() {
    const [id, window] = this.preloadedWindows.entries().next().value
    if (window) {
      this.preloadedWindows.delete(id)
      return window
    }
    
    // 池中没有窗口时创建新的
    return this.createMainWindow()
  }
}

const startupOptimizer = new StartupOptimizer()

内存优化

javascript
// 内存监控和优化
class MemoryOptimizer {
  constructor() {
    this.memoryThreshold = 500 * 1024 * 1024 // 500MB
    this.setupMemoryMonitoring()
  }

  setupMemoryMonitoring() {
    // 定期检查内存使用
    setInterval(() => {
      this.checkMemoryUsage()
    }, 30000) // 每30秒检查一次

    // 监听内存警告
    process.on('warning', (warning) => {
      if (warning.name === 'MaxListenersExceededWarning') {
        console.warn('内存泄漏警告:', warning.message)
        this.handleMemoryLeak()
      }
    })
  }

  checkMemoryUsage() {
    const usage = process.memoryUsage()
    const totalMemory = usage.rss + usage.heapUsed + usage.external
    
    console.log('内存使用情况:', {
      rss: this.formatBytes(usage.rss),
      heapTotal: this.formatBytes(usage.heapTotal),
      heapUsed: this.formatBytes(usage.heapUsed),
      external: this.formatBytes(usage.external),
      total: this.formatBytes(totalMemory)
    })

    if (totalMemory > this.memoryThreshold) {
      console.warn('内存使用过高,开始清理...')
      this.performMemoryCleanup()
    }
  }

  performMemoryCleanup() {
    // 1. 强制垃圾回收
    if (global.gc) {
      global.gc()
    }

    // 2. 清理不必要的缓存
    this.clearCaches()

    // 3. 关闭不活跃的窗口
    this.closeInactiveWindows()

    // 4. 清理事件监听器
    this.cleanupEventListeners()
  }

  clearCaches() {
    // 清理 Electron 缓存
    const { session } = require('electron')
    const defaultSession = session.defaultSession
    
    defaultSession.clearCache().then(() => {
      console.log('缓存已清理')
    })

    // 清理存储数据
    defaultSession.clearStorageData({
      storages: ['appcache', 'cookies', 'filesystem', 'indexdb', 'localstorage', 'shadercache', 'websql', 'serviceworkers']
    })
  }

  closeInactiveWindows() {
    const { BrowserWindow } = require('electron')
    const windows = BrowserWindow.getAllWindows()
    
    windows.forEach(window => {
      // 关闭超过1小时未活跃的窗口
      const lastActivity = window.lastActivity || Date.now()
      const inactiveTime = Date.now() - lastActivity
      
      if (inactiveTime > 60 * 60 * 1000 && !window.isFocused()) {
        console.log('关闭不活跃窗口:', window.id)
        window.close()
      }
    })
  }

  cleanupEventListeners() {
    // 清理可能的内存泄漏
    const { ipcMain } = require('electron')
    
    // 移除过期的 IPC 监听器
    const listeners = ipcMain.eventNames()
    listeners.forEach(eventName => {
      const listenerCount = ipcMain.listenerCount(eventName)
      if (listenerCount > 10) {
        console.warn(`事件 ${eventName} 有 ${listenerCount} 个监听器,可能存在内存泄漏`)
      }
    })
  }

  formatBytes(bytes) {
    if (bytes === 0) return '0 Bytes'
    const k = 1024
    const sizes = ['Bytes', 'KB', 'MB', 'GB']
    const i = Math.floor(Math.log(bytes) / Math.log(k))
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
  }

  // 内存使用报告
  generateMemoryReport() {
    const usage = process.memoryUsage()
    const { BrowserWindow } = require('electron')
    const windows = BrowserWindow.getAllWindows()
    
    return {
      process: {
        rss: usage.rss,
        heapTotal: usage.heapTotal,
        heapUsed: usage.heapUsed,
        external: usage.external
      },
      windows: windows.map(window => ({
        id: window.id,
        title: window.getTitle(),
        url: window.webContents.getURL(),
        memory: window.webContents.getProcessMemoryInfo ? 
                window.webContents.getProcessMemoryInfo() : null
      })),
      timestamp: Date.now()
    }
  }
}

const memoryOptimizer = new MemoryOptimizer()

8.2 渲染进程优化

前端性能优化

javascript
// 渲染进程性能优化
class RendererOptimizer {
  constructor() {
    this.setupPerformanceMonitoring()
    this.optimizeRendering()
  }

  setupPerformanceMonitoring() {
    // 监控页面性能
    window.addEventListener('load', () => {
      this.measurePerformance()
    })

    // 监控长任务
    if ('PerformanceObserver' in window) {
      const observer = new PerformanceObserver((list) => {
        list.getEntries().forEach((entry) => {
          if (entry.duration > 50) { // 超过50ms的任务
            console.warn('长任务检测:', {
              name: entry.name,
              duration: entry.duration,
              startTime: entry.startTime
            })
          }
        })
      })
      
      observer.observe({ entryTypes: ['longtask'] })
    }
  }

  measurePerformance() {
    const navigation = performance.getEntriesByType('navigation')[0]
    const paint = performance.getEntriesByType('paint')
    
    const metrics = {
      domContentLoaded: navigation.domContentLoadedEventEnd - navigation.domContentLoadedEventStart,
      loadComplete: navigation.loadEventEnd - navigation.loadEventStart,
      firstPaint: paint.find(p => p.name === 'first-paint')?.startTime,
      firstContentfulPaint: paint.find(p => p.name === 'first-contentful-paint')?.startTime
    }
    
    console.log('页面性能指标:', metrics)
    
    // 发送性能数据到主进程
    window.electronAPI?.sendPerformanceMetrics?.(metrics)
  }

  optimizeRendering() {
    // 1. 虚拟滚动优化
    this.setupVirtualScrolling()
    
    // 2. 图片懒加载
    this.setupLazyLoading()
    
    // 3. 防抖和节流
    this.setupDebouncing()
    
    // 4. 内存泄漏防护
    this.setupMemoryLeakPrevention()
  }

  setupVirtualScrolling() {
    // 虚拟滚动实现
    class VirtualScroll {
      constructor(container, itemHeight, items) {
        this.container = container
        this.itemHeight = itemHeight
        this.items = items
        this.visibleStart = 0
        this.visibleEnd = 0
        this.scrollTop = 0
        
        this.init()
      }

      init() {
        this.container.style.height = `${this.items.length * this.itemHeight}px`
        this.container.style.position = 'relative'
        this.container.style.overflow = 'auto'
        
        this.container.addEventListener('scroll', this.handleScroll.bind(this))
        this.updateVisibleItems()
      }

      handleScroll() {
        this.scrollTop = this.container.scrollTop
        this.updateVisibleItems()
      }

      updateVisibleItems() {
        const containerHeight = this.container.clientHeight
        this.visibleStart = Math.floor(this.scrollTop / this.itemHeight)
        this.visibleEnd = Math.min(
          this.visibleStart + Math.ceil(containerHeight / this.itemHeight) + 1,
          this.items.length
        )
        
        this.renderVisibleItems()
      }

      renderVisibleItems() {
        // 清空容器
        this.container.innerHTML = ''
        
        // 渲染可见项
        for (let i = this.visibleStart; i < this.visibleEnd; i++) {
          const item = document.createElement('div')
          item.style.position = 'absolute'
          item.style.top = `${i * this.itemHeight}px`
          item.style.height = `${this.itemHeight}px`
          item.textContent = this.items[i]
          
          this.container.appendChild(item)
        }
      }
    }

    // 使用虚拟滚动
    const container = document.getElementById('large-list')
    if (container) {
      new VirtualScroll(container, 50, Array.from({ length: 10000 }, (_, i) => `Item ${i}`))
    }
  }

  setupLazyLoading() {
    // 图片懒加载
    const imageObserver = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          const img = entry.target
          img.src = img.dataset.src
          img.classList.remove('lazy')
          imageObserver.unobserve(img)
        }
      })
    })

    document.querySelectorAll('img[data-src]').forEach(img => {
      imageObserver.observe(img)
    })
  }

  setupDebouncing() {
    // 防抖函数
    function debounce(func, wait) {
      let timeout
      return function executedFunction(...args) {
        const later = () => {
          clearTimeout(timeout)
          func(...args)
        }
        clearTimeout(timeout)
        timeout = setTimeout(later, wait)
      }
    }

    // 节流函数
    function throttle(func, limit) {
      let inThrottle
      return function(...args) {
        if (!inThrottle) {
          func.apply(this, args)
          inThrottle = true
          setTimeout(() => inThrottle = false, limit)
        }
      }
    }

    // 应用到搜索输入
    const searchInput = document.getElementById('search')
    if (searchInput) {
      searchInput.addEventListener('input', debounce((e) => {
        this.performSearch(e.target.value)
      }, 300))
    }

    // 应用到滚动事件
    window.addEventListener('scroll', throttle(() => {
      this.handleScroll()
    }, 16)) // 60fps
  }

  setupMemoryLeakPrevention() {
    // 清理定时器
    const timers = new Set()
    
    const originalSetTimeout = window.setTimeout
    const originalSetInterval = window.setInterval
    
    window.setTimeout = function(callback, delay) {
      const id = originalSetTimeout(callback, delay)
      timers.add(id)
      return id
    }
    
    window.setInterval = function(callback, delay) {
      const id = originalSetInterval(callback, delay)
      timers.add(id)
      return id
    }
    
    // 页面卸载时清理
    window.addEventListener('beforeunload', () => {
      timers.forEach(id => {
        clearTimeout(id)
        clearInterval(id)
      })
      timers.clear()
    })
  }

  performSearch(query) {
    // 搜索实现
    console.log('搜索:', query)
  }

  handleScroll() {
    // 滚动处理
    console.log('滚动位置:', window.scrollY)
  }
}

// 初始化渲染优化器
document.addEventListener('DOMContentLoaded', () => {
  new RendererOptimizer()
})


// 初始化渲染优化器
document.addEventListener('DOMContentLoaded', () => {
  new RendererOptimizer()
})

8.3 安全最佳实践

Electron 应用的安全性至关重要,需要从多个层面进行防护。

基础安全配置

javascript
// 安全的 BrowserWindow 配置
const { BrowserWindow } = require('electron')
const path = require('path')

class SecurityManager {
  constructor() {
    this.setupSecureDefaults()
  }

  createSecureWindow(options = {}) {
    const secureDefaults = {
      webPreferences: {
        // 核心安全设置
        nodeIntegration: false,           // 禁用 Node.js 集成
        contextIsolation: true,           // 启用上下文隔离
        enableRemoteModule: false,        // 禁用 remote 模块
        allowRunningInsecureContent: false, // 禁止运行不安全内容
        experimentalFeatures: false,      // 禁用实验性功能

        // 沙箱模式
        sandbox: true,                    // 启用沙箱
        preload: path.join(__dirname, 'secure-preload.js'),

        // 网络安全
        webSecurity: true,                // 启用 Web 安全
        allowRunningInsecureContent: false,

        // 其他安全选项
        plugins: false,                   // 禁用插件
        webgl: false,                     // 禁用 WebGL (如果不需要)
        enableWebSQL: false,              // 禁用 WebSQL

        // 权限控制
        permissions: ['notifications'],   // 明确指定允许的权限
      },

      // 窗口安全选项
      show: false,                        // 延迟显示
      titleBarStyle: 'default',          // 使用默认标题栏
    }

    const mergedOptions = this.deepMerge(secureDefaults, options)
    const window = new BrowserWindow(mergedOptions)

    this.setupWindowSecurity(window)
    return window
  }

  setupWindowSecurity(window) {
    // 1. 阻止新窗口创建
    window.webContents.setWindowOpenHandler(({ url }) => {
      // 只允许特定域名的窗口
      const allowedDomains = ['https://yourdomain.com']
      const urlObj = new URL(url)

      if (allowedDomains.includes(urlObj.origin)) {
        return {
          action: 'allow',
          overrideBrowserWindowOptions: {
            webPreferences: {
              nodeIntegration: false,
              contextIsolation: true,
              sandbox: true
            }
          }
        }
      }

      return { action: 'deny' }
    })

    // 2. 阻止导航到外部 URL
    window.webContents.on('will-navigate', (event, navigationUrl) => {
      const parsedUrl = new URL(navigationUrl)

      // 只允许本地文件和特定域名
      if (parsedUrl.protocol !== 'file:' &&
          !this.isAllowedDomain(parsedUrl.origin)) {
        event.preventDefault()
        console.warn('阻止导航到:', navigationUrl)
      }
    })

    // 3. 阻止下载
    window.webContents.session.on('will-download', (event, item, webContents) => {
      // 检查下载文件类型和来源
      const allowedExtensions = ['.txt', '.json', '.csv']
      const fileExtension = path.extname(item.getFilename())

      if (!allowedExtensions.includes(fileExtension)) {
        event.preventDefault()
        console.warn('阻止下载文件:', item.getFilename())
      }
    })

    // 4. 内容安全策略
    window.webContents.session.webRequest.onHeadersReceived((details, callback) => {
      callback({
        responseHeaders: {
          ...details.responseHeaders,
          'Content-Security-Policy': [
            "default-src 'self'; " +
            "script-src 'self' 'unsafe-inline'; " +
            "style-src 'self' 'unsafe-inline'; " +
            "img-src 'self' data: https:; " +
            "connect-src 'self' https:; " +
            "font-src 'self'; " +
            "object-src 'none'; " +
            "media-src 'self'; " +
            "frame-src 'none';"
          ]
        }
      })
    })
  }

  isAllowedDomain(origin) {
    const allowedDomains = [
      'https://yourdomain.com',
      'https://api.yourdomain.com'
    ]
    return allowedDomains.includes(origin)
  }

  setupSecureDefaults() {
    const { app, session } = require('electron')

    // 应用级安全设置
    app.on('web-contents-created', (event, contents) => {
      // 阻止权限请求
      contents.on('permission-request-handled', (event, permission, granted) => {
        console.log(`权限请求: ${permission}, 授权: ${granted}`)
      })

      // 阻止证书错误
      contents.on('certificate-error', (event, url, error, certificate, callback) => {
        event.preventDefault()
        callback(false) // 拒绝不安全的证书
      })
    })

    // 会话安全设置
    app.whenReady().then(() => {
      const defaultSession = session.defaultSession

      // 设置权限处理
      defaultSession.setPermissionRequestHandler((webContents, permission, callback) => {
        const allowedPermissions = ['notifications', 'clipboard-read']
        callback(allowedPermissions.includes(permission))
      })

      // 设置权限检查处理
      defaultSession.setPermissionCheckHandler((webContents, permission, requestingOrigin) => {
        const allowedPermissions = ['notifications', 'clipboard-read']
        return allowedPermissions.includes(permission)
      })
    })
  }

  deepMerge(target, source) {
    const output = Object.assign({}, target)
    if (this.isObject(target) && this.isObject(source)) {
      Object.keys(source).forEach(key => {
        if (this.isObject(source[key])) {
          if (!(key in target))
            Object.assign(output, { [key]: source[key] })
          else
            output[key] = this.deepMerge(target[key], source[key])
        } else {
          Object.assign(output, { [key]: source[key] })
        }
      })
    }
    return output
  }

  isObject(item) {
    return item && typeof item === 'object' && !Array.isArray(item)
  }
}

const securityManager = new SecurityManager()

安全的 Preload 脚本

javascript
// secure-preload.js - 安全的预加载脚本
const { contextBridge, ipcRenderer } = require('electron')

// 输入验证函数
function validateInput(input, type, maxLength = 1000) {
  if (typeof input !== type) {
    throw new Error(`Invalid input type. Expected ${type}, got ${typeof input}`)
  }

  if (type === 'string' && input.length > maxLength) {
    throw new Error(`Input too long. Maximum length is ${maxLength}`)
  }

  return true
}

// 安全的 API 暴露
contextBridge.exposeInMainWorld('electronAPI', {
  // 文件操作 - 带输入验证
  readFile: async (filePath) => {
    validateInput(filePath, 'string', 500)
    return ipcRenderer.invoke('secure-read-file', filePath)
  },

  writeFile: async (filePath, content) => {
    validateInput(filePath, 'string', 500)
    validateInput(content, 'string', 1000000) // 1MB 限制
    return ipcRenderer.invoke('secure-write-file', filePath, content)
  },

  // 系统信息 - 只读访问
  getSystemInfo: () => {
    return ipcRenderer.invoke('get-system-info')
  },

  // 通知 - 带内容过滤
  showNotification: (title, body) => {
    validateInput(title, 'string', 100)
    validateInput(body, 'string', 500)

    // 过滤 HTML 标签
    const cleanTitle = title.replace(/<[^>]*>/g, '')
    const cleanBody = body.replace(/<[^>]*>/g, '')

    return ipcRenderer.invoke('show-notification', cleanTitle, cleanBody)
  },

  // 事件监听 - 限制事件类型
  on: (channel, callback) => {
    const allowedChannels = ['app-update', 'system-notification', 'file-changed']

    if (!allowedChannels.includes(channel)) {
      throw new Error(`Channel ${channel} is not allowed`)
    }

    ipcRenderer.on(channel, (event, ...args) => {
      callback(...args)
    })
  },

  // 移除监听器
  removeAllListeners: (channel) => {
    const allowedChannels = ['app-update', 'system-notification', 'file-changed']

    if (!allowedChannels.includes(channel)) {
      throw new Error(`Channel ${channel} is not allowed`)
    }

    ipcRenderer.removeAllListeners(channel)
  }
})

// 安全检查
window.addEventListener('DOMContentLoaded', () => {
  // 检查是否在安全环境中
  if (window.location.protocol !== 'file:') {
    console.warn('应用未在安全的 file:// 协议下运行')
  }

  // 禁用开发者工具快捷键(生产环境)
  if (process.env.NODE_ENV === 'production') {
    document.addEventListener('keydown', (e) => {
      // 禁用 F12, Ctrl+Shift+I, Ctrl+Shift+J, Ctrl+U
      if (e.key === 'F12' ||
          (e.ctrlKey && e.shiftKey && (e.key === 'I' || e.key === 'J')) ||
          (e.ctrlKey && e.key === 'U')) {
        e.preventDefault()
        return false
      }
    })

    // 禁用右键菜单
    document.addEventListener('contextmenu', (e) => {
      e.preventDefault()
      return false
    })
  }
})

数据加密和存储安全

javascript
// 数据安全管理
const crypto = require('crypto')
const { safeStorage } = require('electron')

class DataSecurityManager {
  constructor() {
    this.algorithm = 'aes-256-gcm'
    this.keyLength = 32
    this.ivLength = 16
    this.tagLength = 16
  }

  // 生成安全的随机密钥
  generateKey() {
    return crypto.randomBytes(this.keyLength)
  }

  // 加密数据
  encrypt(data, key) {
    try {
      const iv = crypto.randomBytes(this.ivLength)
      const cipher = crypto.createCipher(this.algorithm, key, iv)

      let encrypted = cipher.update(JSON.stringify(data), 'utf8', 'hex')
      encrypted += cipher.final('hex')

      const tag = cipher.getAuthTag()

      return {
        encrypted,
        iv: iv.toString('hex'),
        tag: tag.toString('hex')
      }
    } catch (error) {
      throw new Error(`加密失败: ${error.message}`)
    }
  }

  // 解密数据
  decrypt(encryptedData, key) {
    try {
      const { encrypted, iv, tag } = encryptedData
      const decipher = crypto.createDecipher(this.algorithm, key, Buffer.from(iv, 'hex'))

      decipher.setAuthTag(Buffer.from(tag, 'hex'))

      let decrypted = decipher.update(encrypted, 'hex', 'utf8')
      decrypted += decipher.final('utf8')

      return JSON.parse(decrypted)
    } catch (error) {
      throw new Error(`解密失败: ${error.message}`)
    }
  }

  // 使用 Electron 的 safeStorage
  encryptWithSafeStorage(data) {
    if (!safeStorage.isEncryptionAvailable()) {
      throw new Error('系统不支持安全存储')
    }

    const buffer = Buffer.from(JSON.stringify(data), 'utf8')
    return safeStorage.encryptString(buffer.toString())
  }

  decryptWithSafeStorage(encryptedData) {
    if (!safeStorage.isEncryptionAvailable()) {
      throw new Error('系统不支持安全存储')
    }

    const decrypted = safeStorage.decryptString(encryptedData)
    return JSON.parse(decrypted)
  }

  // 安全存储敏感数据
  secureStore(key, data) {
    try {
      const Store = require('electron-store')
      const store = new Store({
        encryptionKey: 'your-encryption-key', // 在生产环境中使用更安全的密钥管理
        serialize: (value) => {
          return this.encryptWithSafeStorage(value)
        },
        deserialize: (value) => {
          return this.decryptWithSafeStorage(value)
        }
      })

      store.set(key, data)
      return true
    } catch (error) {
      console.error('安全存储失败:', error)
      return false
    }
  }

  // 安全读取敏感数据
  secureRetrieve(key) {
    try {
      const Store = require('electron-store')
      const store = new Store({
        encryptionKey: 'your-encryption-key',
        serialize: (value) => {
          return this.encryptWithSafeStorage(value)
        },
        deserialize: (value) => {
          return this.decryptWithSafeStorage(value)
        }
      })

      return store.get(key)
    } catch (error) {
      console.error('安全读取失败:', error)
      return null
    }
  }

  // 密码哈希
  hashPassword(password, salt) {
    if (!salt) {
      salt = crypto.randomBytes(16).toString('hex')
    }

    const hash = crypto.pbkdf2Sync(password, salt, 10000, 64, 'sha512')
    return {
      hash: hash.toString('hex'),
      salt: salt
    }
  }

  // 验证密码
  verifyPassword(password, hash, salt) {
    const verifyHash = crypto.pbkdf2Sync(password, salt, 10000, 64, 'sha512')
    return hash === verifyHash.toString('hex')
  }

  // 生成安全的随机令牌
  generateToken(length = 32) {
    return crypto.randomBytes(length).toString('hex')
  }

  // 安全删除敏感数据
  secureDelete(key) {
    try {
      const Store = require('electron-store')
      const store = new Store()

      // 覆写数据
      store.set(key, crypto.randomBytes(1024).toString('hex'))

      // 删除数据
      store.delete(key)

      return true
    } catch (error) {
      console.error('安全删除失败:', error)
      return false
    }
  }
}

const dataSecurityManager = new DataSecurityManager()

网络安全

javascript
// 网络安全管理
class NetworkSecurityManager {
  constructor() {
    this.allowedHosts = [
      'api.yourdomain.com',
      'cdn.yourdomain.com'
    ]
    this.setupNetworkSecurity()
  }

  setupNetworkSecurity() {
    const { session } = require('electron')

    // 设置网络请求过滤
    session.defaultSession.webRequest.onBeforeRequest((details, callback) => {
      const url = new URL(details.url)

      // 检查是否为允许的主机
      if (this.isAllowedHost(url.hostname)) {
        callback({ cancel: false })
      } else {
        console.warn('阻止网络请求:', details.url)
        callback({ cancel: true })
      }
    })

    // 设置请求头
    session.defaultSession.webRequest.onBeforeSendHeaders((details, callback) => {
      const headers = {
        ...details.requestHeaders,
        'User-Agent': 'YourApp/1.0.0', // 自定义 User-Agent
        'X-Requested-With': 'YourApp'   // 添加自定义头
      }

      // 移除可能泄露信息的头
      delete headers['Referer']

      callback({ requestHeaders: headers })
    })
  }

  isAllowedHost(hostname) {
    return this.allowedHosts.includes(hostname) ||
           hostname === 'localhost' ||
           hostname === '127.0.0.1'
  }

  // 安全的 HTTP 请求
  async secureRequest(url, options = {}) {
    const urlObj = new URL(url)

    // 检查协议
    if (urlObj.protocol !== 'https:' && urlObj.hostname !== 'localhost') {
      throw new Error('只允许 HTTPS 请求')
    }

    // 检查主机
    if (!this.isAllowedHost(urlObj.hostname)) {
      throw new Error(`不允许的主机: ${urlObj.hostname}`)
    }

    const defaultOptions = {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
        'User-Agent': 'YourApp/1.0.0'
      },
      timeout: 10000 // 10秒超时
    }

    const mergedOptions = { ...defaultOptions, ...options }

    try {
      const { net } = require('electron')
      const request = net.request({
        url,
        ...mergedOptions
      })

      return new Promise((resolve, reject) => {
        let responseData = ''

        request.on('response', (response) => {
          response.on('data', (chunk) => {
            responseData += chunk
          })

          response.on('end', () => {
            try {
              const data = JSON.parse(responseData)
              resolve(data)
            } catch (error) {
              reject(new Error('响应解析失败'))
            }
          })
        })

        request.on('error', (error) => {
          reject(error)
        })

        // 设置超时
        setTimeout(() => {
          request.abort()
          reject(new Error('请求超时'))
        }, mergedOptions.timeout)

        if (mergedOptions.body) {
          request.write(JSON.stringify(mergedOptions.body))
        }

        request.end()
      })
    } catch (error) {
      throw new Error(`网络请求失败: ${error.message}`)
    }
  }

  // 验证 SSL 证书
  validateCertificate(certificate) {
    // 实现证书验证逻辑
    const validIssuers = [
      'Let\'s Encrypt',
      'DigiCert',
      'GlobalSign'
    ]

    return validIssuers.some(issuer =>
      certificate.issuerName.includes(issuer)
    )
  }
}

const networkSecurityManager = new NetworkSecurityManager()

本章小结

通过本章学习,你应该已经:

  • ✅ 掌握了应用启动优化的技巧
  • ✅ 学会了内存监控和优化方法
  • ✅ 了解了渲染进程的性能优化策略
  • ✅ 掌握了虚拟滚动和懒加载等技术
  • ✅ 学会了 Electron 应用的安全配置
  • ✅ 了解了数据加密和安全存储
  • ✅ 掌握了网络安全最佳实践
  • ✅ 学会了防范常见的安全威胁

在下一章中,我们将通过一个完整的实战项目来综合运用所学知识!

Released under the MIT License.