Skip to content

Performance Optimization

This comprehensive guide covers performance optimization techniques for mini programs, including startup optimization, runtime performance, memory management, and monitoring strategies.

📋 Table of Contents

🚀 Startup Performance

App Launch Optimization

javascript
// app.js - Optimized app initialization
App({
  onLaunch(options) {
    console.time('App Launch')
    
    // 1. Essential initialization only
    this.initializeEssentials()
    
    // 2. Defer non-critical operations
    this.deferNonCriticalInit()
    
    console.timeEnd('App Launch')
  },
  
  initializeEssentials() {
    // Only critical data needed for first page
    this.globalData = {
      systemInfo: wx.getSystemInfoSync(),
      userInfo: null,
      theme: wx.getStorageSync('theme') || 'light'
    }
    
    // Essential configurations
    this.setupErrorHandling()
    this.setupNetworkMonitoring()
  },
  
  deferNonCriticalInit() {
    // Use setTimeout to defer heavy operations
    setTimeout(() => {
      this.loadUserPreferences()
      this.initializeAnalytics()
      this.preloadCommonData()
    }, 100)
  },
  
  setupErrorHandling() {
    // Lightweight error handler
    wx.onError((error) => {
      console.error('App Error:', error)
      // Send to error reporting service
    })
  },
  
  setupNetworkMonitoring() {
    wx.onNetworkStatusChange((res) => {
      this.globalData.isOnline = res.isConnected
    })
  },
  
  async loadUserPreferences() {
    try {
      const preferences = wx.getStorageSync('userPreferences')
      if (preferences) {
        this.globalData.userPreferences = preferences
      }
    } catch (error) {
      console.error('Failed to load user preferences:', error)
    }
  },
  
  initializeAnalytics() {
    // Initialize analytics SDK
    // This is not critical for app startup
  },
  
  preloadCommonData() {
    // Preload frequently used data
    this.preloadConfigData()
    this.preloadUserData()
  }
})

Page Load Optimization

javascript
// Optimized page loading
Page({
  data: {
    // Minimal initial data
    loading: true,
    essentialData: null
  },
  
  onLoad(options) {
    console.time('Page Load')
    
    // 1. Parse options immediately
    this.parseOptions(options)
    
    // 2. Load critical data first
    this.loadCriticalData()
    
    // 3. Defer non-critical operations
    this.deferNonCriticalOperations()
  },
  
  onReady() {
    console.timeEnd('Page Load')
  },
  
  parseOptions(options) {
    // Quick parameter parsing
    this.pageParams = {
      id: options.id,
      type: options.type || 'default'
    }
  },
  
  async loadCriticalData() {
    try {
      // Load only data needed for initial render
      const essentialData = await this.fetchEssentialData()
      
      this.setData({
        essentialData,
        loading: false
      })
      
    } catch (error) {
      this.handleLoadError(error)
    }
  },
  
  deferNonCriticalOperations() {
    // Use requestIdleCallback equivalent
    setTimeout(() => {
      this.loadSecondaryData()
      this.setupEventListeners()
      this.initializeComponents()
    }, 50)
  },
  
  async fetchEssentialData() {
    // Fetch only critical data
    const response = await wx.request({
      url: `/api/essential/${this.pageParams.id}`,
      method: 'GET'
    })
    return response.data
  },
  
  async loadSecondaryData() {
    try {
      const secondaryData = await this.fetchSecondaryData()
      this.setData({ secondaryData })
    } catch (error) {
      console.error('Failed to load secondary data:', error)
    }
  }
})

⚡ Runtime Optimization

Efficient Data Updates

javascript
// Optimized data update strategies
Page({
  data: {
    items: [],
    userInfo: {},
    ui: {
      loading: false,
      error: null
    }
  },
  
  // 1. Batch updates
  batchUpdateData() {
    // Bad: Multiple setData calls
    // this.setData({ 'ui.loading': true })
    // this.setData({ 'ui.error': null })
    // this.setData({ items: newItems })
    
    // Good: Single setData call
    this.setData({
      'ui.loading': false,
      'ui.error': null,
      items: newItems
    })
  },
  
  // 2. Partial updates for large objects
  updateUserInfo(field, value) {
    // Bad: Update entire object
    // const newUserInfo = { ...this.data.userInfo, [field]: value }
    // this.setData({ userInfo: newUserInfo })
    
    // Good: Update specific field
    this.setData({
      [`userInfo.${field}`]: value
    })
  },
  
  // 3. Efficient list updates
  updateListItem(index, updates) {
    // Update specific item without recreating array
    Object.keys(updates).forEach(key => {
      this.setData({
        [`items[${index}].${key}`]: updates[key]
      })
    })
  },
  
  // 4. Conditional updates
  conditionalUpdate(newData) {
    const updates = {}
    
    // Only update changed fields
    if (newData.title !== this.data.title) {
      updates.title = newData.title
    }
    
    if (newData.status !== this.data.status) {
      updates.status = newData.status
    }
    
    // Only call setData if there are changes
    if (Object.keys(updates).length > 0) {
      this.setData(updates)
    }
  },
  
  // 5. Debounced updates for frequent changes
  onSearchInput: debounce(function(e) {
    const query = e.detail.value
    this.performSearch(query)
  }, 300),
  
  // 6. Virtual scrolling for large lists
  setupVirtualScrolling() {
    this.virtualConfig = {
      itemHeight: 100,
      containerHeight: 600,
      visibleCount: 6,
      bufferCount: 2
    }
    
    this.updateVisibleItems(0)
  },
  
  onScroll(e) {
    const scrollTop = e.detail.scrollTop
    const startIndex = Math.floor(scrollTop / this.virtualConfig.itemHeight)
    this.updateVisibleItems(startIndex)
  },
  
  updateVisibleItems(startIndex) {
    const { visibleCount, bufferCount } = this.virtualConfig
    const endIndex = Math.min(
      startIndex + visibleCount + bufferCount,
      this.data.items.length
    )
    
    const visibleItems = this.data.items.slice(startIndex, endIndex)
    
    this.setData({
      visibleItems,
      startIndex,
      offsetY: startIndex * this.virtualConfig.itemHeight
    })
  }
})

// Debounce utility
function debounce(func, wait) {
  let timeout
  return function executedFunction(...args) {
    const later = () => {
      clearTimeout(timeout)
      func.apply(this, args)
    }
    clearTimeout(timeout)
    timeout = setTimeout(later, wait)
  }
}

Component Optimization

javascript
// Optimized component implementation
Component({
  options: {
    // Enable data observation optimization
    pureDataPattern: /^_/,
    // Enable virtual host
    virtualHost: true
  },
  
  properties: {
    items: {
      type: Array,
      value: []
    },
    config: {
      type: Object,
      value: {}
    }
  },
  
  data: {
    // Pure data (not used in template) - prefix with _
    _cache: {},
    _computedValues: {},
    
    // Template data
    displayItems: [],
    loading: false
  },
  
  observers: {
    // Efficient observers
    'items, config': function(items, config) {
      // Only recompute when necessary
      if (this._shouldRecompute(items, config)) {
        this._computeDisplayItems(items, config)
      }
    }
  },
  
  lifetimes: {
    attached() {
      // Minimal initialization
      this._initializeCache()
    },
    
    detached() {
      // Clean up resources
      this._cleanup()
    }
  },
  
  methods: {
    _shouldRecompute(items, config) {
      const cache = this.data._cache
      return (
        !cache.items ||
        !cache.config ||
        JSON.stringify(items) !== JSON.stringify(cache.items) ||
        JSON.stringify(config) !== JSON.stringify(cache.config)
      )
    },
    
    _computeDisplayItems(items, config) {
      // Expensive computation
      const displayItems = items.map(item => ({
        ...item,
        computed: this._computeItemValue(item, config)
      }))
      
      this.setData({
        displayItems,
        '_cache.items': items,
        '_cache.config': config
      })
    },
    
    _computeItemValue(item, config) {
      // Complex computation logic
      return item.value * config.multiplier
    },
    
    _initializeCache() {
      this.setData({
        '_cache': {}
      })
    },
    
    _cleanup() {
      // Clear timers, remove listeners, etc.
    }
  }
})

💾 Memory Management

Memory Leak Prevention

javascript
// Memory-efficient page management
Page({
  onLoad() {
    this.timers = []
    this.listeners = []
    this.requests = []
    
    this.initializePage()
  },
  
  onUnload() {
    this.cleanup()
  },
  
  onHide() {
    // Pause operations to save memory
    this.pauseOperations()
  },
  
  onShow() {
    // Resume operations
    this.resumeOperations()
  },
  
  // Timer management
  createTimer(callback, interval) {
    const timer = setInterval(callback, interval)
    this.timers.push(timer)
    return timer
  },
  
  clearTimer(timer) {
    clearInterval(timer)
    const index = this.timers.indexOf(timer)
    if (index > -1) {
      this.timers.splice(index, 1)
    }
  },
  
  // Event listener management
  addEventListener(element, event, handler) {
    element.addEventListener(event, handler)
    this.listeners.push({ element, event, handler })
  },
  
  removeEventListener(element, event, handler) {
    element.removeEventListener(event, handler)
    this.listeners = this.listeners.filter(
      listener => !(listener.element === element && 
                   listener.event === event && 
                   listener.handler === handler)
    )
  },
  
  // Request management
  makeRequest(options) {
    const requestTask = wx.request({
      ...options,
      success: (res) => {
        this.removeRequest(requestTask)
        if (options.success) options.success(res)
      },
      fail: (error) => {
        this.removeRequest(requestTask)
        if (options.fail) options.fail(error)
      }
    })
    
    this.requests.push(requestTask)
    return requestTask
  },
  
  removeRequest(requestTask) {
    const index = this.requests.indexOf(requestTask)
    if (index > -1) {
      this.requests.splice(index, 1)
    }
  },
  
  pauseOperations() {
    // Pause timers
    this.timers.forEach(timer => clearInterval(timer))
    
    // Cancel pending requests
    this.requests.forEach(request => {
      if (request.abort) {
        request.abort()
      }
    })
    
    // Clear large data structures
    this.setData({
      largeDataSet: null,
      cachedImages: null
    })
  },
  
  resumeOperations() {
    // Resume necessary operations
    this.initializeTimers()
    this.loadEssentialData()
  },
  
  cleanup() {
    // Clear all timers
    this.timers.forEach(timer => clearInterval(timer))
    this.timers = []
    
    // Remove all event listeners
    this.listeners.forEach(({ element, event, handler }) => {
      element.removeEventListener(event, handler)
    })
    this.listeners = []
    
    // Cancel all requests
    this.requests.forEach(request => {
      if (request.abort) {
        request.abort()
      }
    })
    this.requests = []
    
    // Clear page data
    this.setData({
      items: null,
      cache: null,
      largeDataSet: null
    })
  }
})

Data Structure Optimization

javascript
// Efficient data structures
class OptimizedDataManager {
  constructor() {
    this.cache = new Map()
    this.lruCache = new LRUCache(100) // Limit cache size
    this.weakMap = new WeakMap() // For object associations
  }
  
  // LRU Cache implementation
  get(key) {
    if (this.lruCache.has(key)) {
      // Move to front
      const value = this.lruCache.get(key)
      this.lruCache.delete(key)
      this.lruCache.set(key, value)
      return value
    }
    return null
  }
  
  set(key, value) {
    if (this.lruCache.size >= this.lruCache.maxSize) {
      // Remove oldest item
      const firstKey = this.lruCache.keys().next().value
      this.lruCache.delete(firstKey)
    }
    this.lruCache.set(key, value)
  }
  
  // Object pooling for frequently created objects
  createObjectPool(createFn, resetFn, initialSize = 10) {
    const pool = []
    
    // Pre-populate pool
    for (let i = 0; i < initialSize; i++) {
      pool.push(createFn())
    }
    
    return {
      acquire() {
        return pool.length > 0 ? pool.pop() : createFn()
      },
      
      release(obj) {
        resetFn(obj)
        pool.push(obj)
      }
    }
  }
  
  // Efficient array operations
  binarySearch(arr, target, compareFn) {
    let left = 0
    let right = arr.length - 1
    
    while (left <= right) {
      const mid = Math.floor((left + right) / 2)
      const comparison = compareFn(arr[mid], target)
      
      if (comparison === 0) return mid
      if (comparison < 0) left = mid + 1
      else right = mid - 1
    }
    
    return -1
  }
  
  // Batch operations
  batchProcess(items, batchSize, processFn) {
    return new Promise((resolve) => {
      let index = 0
      const results = []
      
      const processBatch = () => {
        const batch = items.slice(index, index + batchSize)
        const batchResults = batch.map(processFn)
        results.push(...batchResults)
        
        index += batchSize
        
        if (index < items.length) {
          // Use setTimeout to prevent blocking
          setTimeout(processBatch, 0)
        } else {
          resolve(results)
        }
      }
      
      processBatch()
    })
  }
}

// Usage in pages
Page({
  onLoad() {
    this.dataManager = new OptimizedDataManager()
    
    // Create object pool for list items
    this.itemPool = this.dataManager.createObjectPool(
      () => ({ id: null, title: '', data: null }),
      (obj) => { obj.id = null; obj.title = ''; obj.data = null }
    )
  },
  
  async processLargeDataSet(items) {
    // Process in batches to prevent blocking
    const results = await this.dataManager.batchProcess(
      items,
      100, // Process 100 items at a time
      (item) => this.processItem(item)
    )
    
    this.setData({ processedItems: results })
  }
})

🌐 Network Optimization

Request Optimization

javascript
// Network performance optimization
class NetworkOptimizer {
  constructor() {
    this.requestQueue = []
    this.activeRequests = new Set()
    this.maxConcurrentRequests = 3
    this.cache = new Map()
    this.retryConfig = {
      maxRetries: 3,
      baseDelay: 1000,
      maxDelay: 10000
    }
  }
  
  // Request deduplication
  async request(options) {
    const key = this.generateRequestKey(options)
    
    // Check if same request is already in progress
    if (this.activeRequests.has(key)) {
      return this.waitForActiveRequest(key)
    }
    
    // Check cache
    if (this.shouldUseCache(options)) {
      const cached = this.cache.get(key)
      if (cached && !this.isCacheExpired(cached)) {
        return cached.data
      }
    }
    
    return this.executeRequest(key, options)
  }
  
  generateRequestKey(options) {
    return `${options.method || 'GET'}:${options.url}:${JSON.stringify(options.data || {})}`
  }
  
  async executeRequest(key, options) {
    this.activeRequests.add(key)
    
    try {
      const response = await this.requestWithRetry(options)
      
      // Cache successful responses
      if (this.shouldCache(options, response)) {
        this.cache.set(key, {
          data: response,
          timestamp: Date.now(),
          ttl: options.cacheTTL || 300000 // 5 minutes default
        })
      }
      
      return response
    } finally {
      this.activeRequests.delete(key)
    }
  }
  
  async requestWithRetry(options) {
    let lastError
    
    for (let attempt = 0; attempt <= this.retryConfig.maxRetries; attempt++) {
      try {
        return await this.makeRequest(options)
      } catch (error) {
        lastError = error
        
        if (attempt < this.retryConfig.maxRetries && this.shouldRetry(error)) {
          const delay = Math.min(
            this.retryConfig.baseDelay * Math.pow(2, attempt),
            this.retryConfig.maxDelay
          )
          await this.sleep(delay)
        }
      }
    }
    
    throw lastError
  }
  
  makeRequest(options) {
    return new Promise((resolve, reject) => {
      wx.request({
        ...options,
        success: (res) => {
          if (res.statusCode >= 200 && res.statusCode < 300) {
            resolve(res.data)
          } else {
            reject(new Error(`HTTP ${res.statusCode}`))
          }
        },
        fail: reject
      })
    })
  }
  
  shouldRetry(error) {
    // Retry on network errors and 5xx status codes
    return error.message.includes('network') || 
           error.message.includes('timeout') ||
           error.message.includes('HTTP 5')
  }
  
  shouldUseCache(options) {
    return options.method === 'GET' && options.useCache !== false
  }
  
  shouldCache(options, response) {
    return options.method === 'GET' && response && options.noCache !== true
  }
  
  isCacheExpired(cached) {
    return Date.now() - cached.timestamp > cached.ttl
  }
  
  sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms))
  }
  
  // Request batching
  batchRequests(requests) {
    return Promise.all(
      requests.map(request => this.request(request))
    )
  }
  
  // Preload critical resources
  preloadResources(urls) {
    urls.forEach(url => {
      this.request({
        url,
        method: 'GET',
        useCache: true,
        cacheTTL: 600000 // 10 minutes
      }).catch(error => {
        console.warn('Preload failed:', url, error)
      })
    })
  }
}

// Usage
const networkOptimizer = new NetworkOptimizer()

Page({
  async loadData() {
    try {
      // Batch multiple requests
      const [userData, configData, contentData] = await networkOptimizer.batchRequests([
        { url: '/api/user', useCache: true },
        { url: '/api/config', useCache: true, cacheTTL: 3600000 },
        { url: '/api/content', useCache: false }
      ])
      
      this.setData({
        userData,
        configData,
        contentData
      })
    } catch (error) {
      console.error('Failed to load data:', error)
    }
  },
  
  onLoad() {
    // Preload critical resources
    networkOptimizer.preloadResources([
      '/api/config',
      '/api/user/preferences',
      '/api/common/data'
    ])
  }
})

🖼️ Image Optimization

Image Loading Optimization

javascript
// Image optimization strategies
class ImageOptimizer {
  constructor() {
    this.imageCache = new Map()
    this.loadingImages = new Set()
    this.lazyLoadObserver = null
    this.setupLazyLoading()
  }
  
  setupLazyLoading() {
    // Simulate intersection observer for lazy loading
    this.lazyLoadObserver = {
      observe: (element) => {
        // Implementation would depend on scroll position
        this.checkVisibility(element)
      },
      unobserve: (element) => {
        // Remove from observation
      }
    }
  }
  
  // Lazy loading implementation
  lazyLoadImage(src, placeholder = '/images/placeholder.png') {
    return new Promise((resolve, reject) => {
      // Return placeholder immediately
      resolve(placeholder)
      
      // Load actual image in background
      this.preloadImage(src).then(() => {
        resolve(src)
      }).catch(reject)
    })
  }
  
  preloadImage(src) {
    if (this.imageCache.has(src)) {
      return Promise.resolve(src)
    }
    
    if (this.loadingImages.has(src)) {
      return this.waitForImageLoad(src)
    }
    
    this.loadingImages.add(src)
    
    return new Promise((resolve, reject) => {
      wx.downloadFile({
        url: src,
        success: (res) => {
          if (res.statusCode === 200) {
            this.imageCache.set(src, res.tempFilePath)
            this.loadingImages.delete(src)
            resolve(res.tempFilePath)
          } else {
            this.loadingImages.delete(src)
            reject(new Error(`Failed to load image: ${res.statusCode}`))
          }
        },
        fail: (error) => {
          this.loadingImages.delete(src)
          reject(error)
        }
      })
    })
  }
  
  // Image compression
  compressImage(filePath, quality = 0.8) {
    return new Promise((resolve, reject) => {
      wx.compressImage({
        src: filePath,
        quality: Math.floor(quality * 100),
        success: resolve,
        fail: reject
      })
    })
  }
  
  // Generate responsive image URLs
  getResponsiveImageUrl(baseUrl, width, height, quality = 80) {
    const params = new URLSearchParams({
      w: width,
      h: height,
      q: quality,
      f: 'webp' // Use WebP format if supported
    })
    
    return `${baseUrl}?${params.toString()}`
  }
  
  // Image format optimization
  getOptimalImageFormat(originalUrl) {
    const systemInfo = wx.getSystemInfoSync()
    
    // Check WebP support
    if (this.supportsWebP(systemInfo)) {
      return originalUrl.replace(/\.(jpg|jpeg|png)$/i, '.webp')
    }
    
    return originalUrl
  }
  
  supportsWebP(systemInfo) {
    // Check if system supports WebP
    const { platform, version } = systemInfo
    
    if (platform === 'ios') {
      return this.compareVersion(version, '14.0.0') >= 0
    } else if (platform === 'android') {
      return this.compareVersion(version, '4.0.0') >= 0
    }
    
    return false
  }
  
  compareVersion(version1, version2) {
    const v1 = version1.split('.').map(Number)
    const v2 = version2.split('.').map(Number)
    
    for (let i = 0; i < Math.max(v1.length, v2.length); i++) {
      const num1 = v1[i] || 0
      const num2 = v2[i] || 0
      
      if (num1 > num2) return 1
      if (num1 < num2) return -1
    }
    
    return 0
  }
  
  // Batch image preloading
  preloadImages(urls, concurrency = 3) {
    return new Promise((resolve) => {
      const results = []
      let completed = 0
      let index = 0
      
      const loadNext = () => {
        if (index >= urls.length) {
          if (completed === urls.length) {
            resolve(results)
          }
          return
        }
        
        const url = urls[index++]
        
        this.preloadImage(url)
          .then((result) => {
            results.push({ url, success: true, result })
          })
          .catch((error) => {
            results.push({ url, success: false, error })
          })
          .finally(() => {
            completed++
            if (completed === urls.length) {
              resolve(results)
            } else {
              loadNext()
            }
          })
      }
      
      // Start concurrent loading
      for (let i = 0; i < Math.min(concurrency, urls.length); i++) {
        loadNext()
      }
    })
  }
}

// Usage in components
Component({
  data: {
    imageSrc: '/images/placeholder.png',
    imageLoaded: false
  },
  
  lifetimes: {
    attached() {
      this.imageOptimizer = new ImageOptimizer()
      this.loadImage()
    }
  },
  
  methods: {
    async loadImage() {
      const { src, width, height } = this.properties
      
      try {
        // Get responsive image URL
        const responsiveUrl = this.imageOptimizer.getResponsiveImageUrl(
          src, width, height
        )
        
        // Get optimal format
        const optimizedUrl = this.imageOptimizer.getOptimalImageFormat(responsiveUrl)
        
        // Lazy load image
        const imageSrc = await this.imageOptimizer.lazyLoadImage(optimizedUrl)
        
        this.setData({
          imageSrc,
          imageLoaded: true
        })
      } catch (error) {
        console.error('Failed to load image:', error)
        this.setData({
          imageSrc: '/images/error-placeholder.png',
          imageLoaded: true
        })
      }
    },
    
    onImageLoad() {
      this.triggerEvent('imageload')
    },
    
    onImageError() {
      this.setData({
        imageSrc: '/images/error-placeholder.png'
      })
      this.triggerEvent('imageerror')
    }
  }
})

📦 Code Splitting

Dynamic Import Implementation

javascript
// Dynamic component loading
class ComponentLoader {
  constructor() {
    this.loadedComponents = new Map()
    this.loadingComponents = new Set()
  }
  
  async loadComponent(componentPath) {
    if (this.loadedComponents.has(componentPath)) {
      return this.loadedComponents.get(componentPath)
    }
    
    if (this.loadingComponents.has(componentPath)) {
      return this.waitForComponent(componentPath)
    }
    
    this.loadingComponents.add(componentPath)
    
    try {
      // Simulate dynamic import
      const component = await this.importComponent(componentPath)
      this.loadedComponents.set(componentPath, component)
      this.loadingComponents.delete(componentPath)
      return component
    } catch (error) {
      this.loadingComponents.delete(componentPath)
      throw error
    }
  }
  
  async importComponent(componentPath) {
    // This would be implemented based on your bundling strategy
    return new Promise((resolve, reject) => {
      // Load component definition
      wx.request({
        url: `/components/${componentPath}.js`,
        success: (res) => {
          try {
            // Evaluate component code
            const component = eval(res.data)
            resolve(component)
          } catch (error) {
            reject(error)
          }
        },
        fail: reject
      })
    })
  }
  
  waitForComponent(componentPath) {
    return new Promise((resolve, reject) => {
      const checkLoaded = () => {
        if (this.loadedComponents.has(componentPath)) {
          resolve(this.loadedComponents.get(componentPath))
        } else if (!this.loadingComponents.has(componentPath)) {
          reject(new Error('Component loading failed'))
        } else {
          setTimeout(checkLoaded, 100)
        }
      }
      checkLoaded()
    })
  }
  
  // Preload components
  preloadComponents(componentPaths) {
    componentPaths.forEach(path => {
      this.loadComponent(path).catch(error => {
        console.warn('Failed to preload component:', path, error)
      })
    })
  }
}

// Route-based code splitting
class RouteManager {
  constructor() {
    this.routes = new Map()
    this.componentLoader = new ComponentLoader()
  }
  
  registerRoute(path, componentPath, preload = false) {
    this.routes.set(path, {
      componentPath,
      preload,
      loaded: false
    })
    
    if (preload) {
      this.componentLoader.preloadComponents([componentPath])
    }
  }
  
  async navigateToRoute(path, options = {}) {
    const route = this.routes.get(path)
    
    if (!route) {
      throw new Error(`Route not found: ${path}`)
    }
    
    // Load component if not already loaded
    if (!route.loaded) {
      await this.componentLoader.loadComponent(route.componentPath)
      route.loaded = true
    }
    
    // Navigate to page
    wx.navigateTo({
      url: path,
      ...options
    })
  }
}

// Usage
const routeManager = new RouteManager()

// Register routes with lazy loading
routeManager.registerRoute('/pages/profile/profile', 'profile-page')
routeManager.registerRoute('/pages/settings/settings', 'settings-page', true) // Preload

// Navigate with lazy loading
routeManager.navigateToRoute('/pages/profile/profile')

📊 Performance Monitoring

Performance Metrics Collection

javascript
// Performance monitoring system
class PerformanceMonitor {
  constructor() {
    this.metrics = []
    this.observers = []
    this.startTime = Date.now()
    this.setupMonitoring()
  }
  
  setupMonitoring() {
    // Monitor page load times
    this.monitorPageLoads()
    
    // Monitor memory usage
    this.monitorMemoryUsage()
    
    // Monitor network requests
    this.monitorNetworkRequests()
    
    // Monitor user interactions
    this.monitorUserInteractions()
  }
  
  monitorPageLoads() {
    const originalPage = Page
    const self = this
    
    Page = function(options) {
      const originalOnLoad = options.onLoad
      const originalOnReady = options.onReady
      const originalOnShow = options.onShow
      
      options.onLoad = function(query) {
        const startTime = Date.now()
        this._performanceStartTime = startTime
        
        if (originalOnLoad) {
          originalOnLoad.call(this, query)
        }
      }
      
      options.onReady = function() {
        const loadTime = Date.now() - this._performanceStartTime
        
        self.recordMetric('page_load_time', {
          page: this.route,
          duration: loadTime,
          timestamp: Date.now()
        })
        
        if (originalOnReady) {
          originalOnReady.call(this)
        }
      }
      
      options.onShow = function() {
        self.recordMetric('page_show', {
          page: this.route,
          timestamp: Date.now()
        })
        
        if (originalOnShow) {
          originalOnShow.call(this)
        }
      }
      
      return originalPage(options)
    }
  }
  
  monitorMemoryUsage() {
    setInterval(() => {
      try {
        const memoryInfo = wx.getSystemInfoSync()
        
        this.recordMetric('memory_usage', {
          available: memoryInfo.memorySize,
          timestamp: Date.now()
        })
      } catch (error) {
        console.error('Failed to get memory info:', error)
      }
    }, 30000) // Every 30 seconds
  }
  
  monitorNetworkRequests() {
    const originalRequest = wx.request
    const self = this
    
    wx.request = function(options) {
      const startTime = Date.now()
      const originalSuccess = options.success
      const originalFail = options.fail
      
      options.success = function(res) {
        const duration = Date.now() - startTime
        
        self.recordMetric('network_request', {
          url: options.url,
          method: options.method || 'GET',
          status: res.statusCode,
          duration,
          success: true,
          timestamp: Date.now()
        })
        
        if (originalSuccess) {
          originalSuccess(res)
        }
      }
      
      options.fail = function(error) {
        const duration = Date.now() - startTime
        
        self.recordMetric('network_request', {
          url: options.url,
          method: options.method || 'GET',
          duration,
          success: false,
          error: error.errMsg,
          timestamp: Date.now()
        })
        
        if (originalFail) {
          originalFail(error)
        }
      }
      
      return originalRequest(options)
    }
  }
  
  monitorUserInteractions() {
    // Monitor tap events
    this.monitorTapEvents()
    
    // Monitor scroll performance
    this.monitorScrollPerformance()
  }
  
  monitorTapEvents() {
    // This would require hooking into the event system
    // Implementation depends on your framework
  }
  
  monitorScrollPerformance() {
    let scrollStartTime = 0
    let frameCount = 0
    
    const onScroll = () => {
      if (scrollStartTime === 0) {
        scrollStartTime = Date.now()
        frameCount = 0
      }
      
      frameCount++
      
      // Calculate FPS after scroll ends
      setTimeout(() => {
        if (Date.now() - scrollStartTime > 100) {
          const duration = Date.now() - scrollStartTime
          const fps = (frameCount / duration) * 1000
          
          this.recordMetric('scroll_performance', {
            fps,
            duration,
            frameCount,
            timestamp: Date.now()
          })
          
          scrollStartTime = 0
        }
      }, 100)
    }
    
    // This would be attached to scroll views
    // Implementation depends on your specific setup
  }
  
  recordMetric(type, data) {
    this.metrics.push({
      type,
      data,
      timestamp: Date.now()
    })
    
    // Send metrics periodically
    if (this.metrics.length >= 50) {
      this.sendMetrics()
    }
  }
  
  async sendMetrics() {
    if (this.metrics.length === 0) return
    
    const metricsToSend = [...this.metrics]
    this.metrics = []
    
    try {
      await wx.request({
        url: 'https://analytics.example.com/metrics',
        method: 'POST',
        data: {
          appId: getApp().globalData.appId,
          version: getApp().globalData.version,
          metrics: metricsToSend
        }
      })
    } catch (error) {
      console.error('Failed to send metrics:', error)
      // Re-add metrics to queue
      this.metrics.unshift(...metricsToSend)
    }
  }
  
  // Performance analysis
  analyzePerformance() {
    const analysis = {
      pageLoadTimes: this.analyzePageLoadTimes(),
      networkPerformance: this.analyzeNetworkPerformance(),
      memoryUsage: this.analyzeMemoryUsage(),
      userExperience: this.analyzeUserExperience()
    }
    
    return analysis
  }
  
  analyzePageLoadTimes() {
    const pageLoadMetrics = this.metrics.filter(m => m.type === 'page_load_time')
    
    if (pageLoadMetrics.length === 0) return null
    
    const loadTimes = pageLoadMetrics.map(m => m.data.duration)
    const avgLoadTime = loadTimes.reduce((a, b) => a + b, 0) / loadTimes.length
    const maxLoadTime = Math.max(...loadTimes)
    const minLoadTime = Math.min(...loadTimes)
    
    return {
      average: avgLoadTime,
      max: maxLoadTime,
      min: minLoadTime,
      count: loadTimes.length,
      slowPages: pageLoadMetrics
        .filter(m => m.data.duration > 3000)
        .map(m => m.data.page)
    }
  }
  
  analyzeNetworkPerformance() {
    const networkMetrics = this.metrics.filter(m => m.type === 'network_request')
    
    if (networkMetrics.length === 0) return null
    
    const successRate = networkMetrics.filter(m => m.data.success).length / networkMetrics.length
    const avgResponseTime = networkMetrics
      .map(m => m.data.duration)
      .reduce((a, b) => a + b, 0) / networkMetrics.length
    
    return {
      successRate,
      averageResponseTime: avgResponseTime,
      totalRequests: networkMetrics.length,
      slowRequests: networkMetrics
        .filter(m => m.data.duration > 5000)
        .map(m => ({ url: m.data.url, duration: m.data.duration }))
    }
  }
  
  analyzeMemoryUsage() {
    const memoryMetrics = this.metrics.filter(m => m.type === 'memory_usage')
    
    if (memoryMetrics.length === 0) return null
    
    const memoryValues = memoryMetrics.map(m => m.data.available)
    const avgMemory = memoryValues.reduce((a, b) => a + b, 0) / memoryValues.length
    
    return {
      average: avgMemory,
      trend: this.calculateTrend(memoryValues)
    }
  }
  
  analyzeUserExperience() {
    const scrollMetrics = this.metrics.filter(m => m.type === 'scroll_performance')
    
    if (scrollMetrics.length === 0) return null
    
    const avgFPS = scrollMetrics
      .map(m => m.data.fps)
      .reduce((a, b) => a + b, 0) / scrollMetrics.length
    
    return {
      averageFPS: avgFPS,
      smoothScrollPercentage: scrollMetrics.filter(m => m.data.fps >= 30).length / scrollMetrics.length
    }
  }
  
  calculateTrend(values) {
    if (values.length < 2) return 'stable'
    
    const firstHalf = values.slice(0, Math.floor(values.length / 2))
    const secondHalf = values.slice(Math.floor(values.length / 2))
    
    const firstAvg = firstHalf.reduce((a, b) => a + b, 0) / firstHalf.length
    const secondAvg = secondHalf.reduce((a, b) => a + b, 0) / secondHalf.length
    
    const change = (secondAvg - firstAvg) / firstAvg
    
    if (change > 0.1) return 'increasing'
    if (change < -0.1) return 'decreasing'
    return 'stable'
  }
}

// Initialize performance monitoring
const performanceMonitor = new PerformanceMonitor()

// Usage in app.js
App({
  onLaunch() {
    // Start performance monitoring
    performanceMonitor.setupMonitoring()
  },
  
  onHide() {
    // Send metrics when app goes to background
    performanceMonitor.sendMetrics()
  }
})

🎯 Best Practices

Performance Optimization Checklist

markdown
## Startup Performance
- [ ] Minimize app.js initialization code
- [ ] Defer non-critical operations
- [ ] Use lazy loading for pages and components
- [ ] Optimize first page load time
- [ ] Preload critical resources

## Runtime Performance
- [ ] Batch setData operations
- [ ] Use partial updates for large objects
- [ ] Implement virtual scrolling for long lists
- [ ] Debounce frequent operations
- [ ] Optimize component rendering

## Memory Management
- [ ] Clean up timers and listeners
- [ ] Implement object pooling
- [ ] Use weak references where appropriate
- [ ] Clear large data structures when not needed
- [ ] Monitor memory usage

## Network Optimization
- [ ] Implement request caching
- [ ] Use request deduplication
- [ ] Batch multiple requests
- [ ] Implement retry mechanisms
- [ ] Preload critical data

## Image Optimization
- [ ] Use appropriate image formats
- [ ] Implement lazy loading
- [ ] Compress images
- [ ] Use responsive images
- [ ] Cache images locally

## Code Optimization
- [ ] Split code by routes
- [ ] Remove unused code
- [ ] Minimize bundle size
- [ ] Use tree shaking
- [ ] Optimize dependencies

Performance Testing

javascript
// Performance testing utilities
class PerformanceTester {
  static async testPageLoadTime(pagePath, iterations = 5) {
    const results = []
    
    for (let i = 0; i < iterations; i++) {
      const startTime = Date.now()
      
      await new Promise((resolve) => {
        wx.navigateTo({
          url: pagePath,
          success: () => {
            // Wait for page to be ready
            setTimeout(() => {
              const loadTime = Date.now() - startTime
              results.push(loadTime)
              
              wx.navigateBack({
                success: resolve
              })
            }, 100)
          }
        })
      })
    }
    
    return {
      average: results.reduce((a, b) => a + b, 0) / results.length,
      min: Math.min(...results),
      max: Math.max(...results),
      results
    }
  }
  
  static async testScrollPerformance(scrollViewSelector, duration = 5000) {
    return new Promise((resolve) => {
      let frameCount = 0
      const startTime = Date.now()
      
      const countFrames = () => {
        frameCount++
        if (Date.now() - startTime < duration) {
          requestAnimationFrame(countFrames)
        } else {
          const fps = (frameCount / duration) * 1000
          resolve({ fps, frameCount, duration })
        }
      }
      
      // Start scrolling simulation
      this.simulateScroll(scrollViewSelector)
      countFrames()
    })
  }
  
  static simulateScroll(selector) {
    // Simulate scroll events
    // Implementation would depend on your testing framework
  }
  
  static async testMemoryUsage(testFunction, duration = 10000) {
    const startMemory = this.getCurrentMemoryUsage()
    const memorySnapshots = [startMemory]
    
    const memoryInterval = setInterval(() => {
      memorySnapshots.push(this.getCurrentMemoryUsage())
    }, 1000)
    
    // Run test function
    await testFunction()
    
    // Wait for specified duration
    await new Promise(resolve => setTimeout(resolve, duration))
    
    clearInterval(memoryInterval)
    
    const endMemory = this.getCurrentMemoryUsage()
    
    return {
      startMemory,
      endMemory,
      memoryLeak: endMemory - startMemory,
      snapshots: memorySnapshots,
      peakMemory: Math.max(...memorySnapshots)
    }
  }
  
  static getCurrentMemoryUsage() {
    try {
      const systemInfo = wx.getSystemInfoSync()
      return systemInfo.memorySize || 0
    } catch (error) {
      return 0
    }
  }
  
  static async runPerformanceTest(testName, testFunction) {
    console.log(`Starting performance test: ${testName}`)
    const startTime = Date.now()
    
    try {
      const result = await testFunction()
      const duration = Date.now() - startTime
      
      console.log(`Performance test completed: ${testName}`)
      console.log(`Duration: ${duration}ms`)
      console.log('Result:', result)
      
      return { success: true, duration, result }
    } catch (error) {
      const duration = Date.now() - startTime
      
      console.error(`Performance test failed: ${testName}`)
      console.error(`Duration: ${duration}ms`)
      console.error('Error:', error)
      
      return { success: false, duration, error }
    }
  }
}

// Usage
async function runPerformanceTests() {
  // Test page load performance
  const pageLoadResult = await PerformanceTester.runPerformanceTest(
    'Page Load Time',
    () => PerformanceTester.testPageLoadTime('/pages/home/home')
  )
  
  // Test memory usage
  const memoryResult = await PerformanceTester.runPerformanceTest(
    'Memory Usage',
    () => PerformanceTester.testMemoryUsage(async () => {
      // Simulate heavy operations
      for (let i = 0; i < 1000; i++) {
        await new Promise(resolve => setTimeout(resolve, 10))
      }
    })
  )
  
  // Test scroll performance
  const scrollResult = await PerformanceTester.runPerformanceTest(
    'Scroll Performance',
    () => PerformanceTester.testScrollPerformance('#scroll-view')
  )
  
  return {
    pageLoad: pageLoadResult,
    memory: memoryResult,
    scroll: scrollResult
  }
}

Performance optimization is crucial for providing excellent user experience in mini programs. Implement these strategies systematically, monitor performance metrics continuously, and test regularly to ensure optimal performance across different devices and network conditions.

Connecting Multiple Platforms, Empowering Innovation