Lesson 06-Micro Frontend Performance Optimization

Load Performance Optimization

Preloading Techniques

Resource Preloading

<!-- Preload critical resources -->
<link rel="preload" href="critical.css" as="style">
<link rel="preload" href="main.js" as="script">

<!-- Preconnect to important domains -->
<link rel="preconnect" href="https://cdn.example.com">
<link rel="dns-prefetch" href="https://api.example.com">

Route-Level Preloading

// React Router v6 preloading example
import { lazy, Suspense } from 'react';

const LazyComponent = lazy(() => import('./LazyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  );
}

// Preloading strategy
window.addEventListener('load', () => {
  // Preload components for potential routes
  import(/* webpackPrefetch: true */ './PossibleComponent');
});

Module Federation Preloading

// Configure preloading for remote modules in main application
const mf = require('@angular-architects/module-federation/webpack');
const { ModuleFederationPlugin } = require('webpack').container;

module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'host',
      remotes: {
        app1: 'app1@http://localhost:3001/remoteEntry.js',
      },
      shared: {
        react: { singleton: true, eager: true }, // Force preload shared dependencies
      },
    }),
  ],
};

Resource Compression and Bundling

Webpack Resource Optimization Configuration

// webpack.config.js
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
        },
      },
    },
    minimizer: [
      new TerserPlugin({
        parallel: true,
        terserOptions: {
          compress: {
            drop_console: true,
          },
        },
      }),
      new CssMinimizerPlugin(),
    ],
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react'],
            plugins: ['syntax-dynamic-import'],
          },
        },
      },
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [require('autoprefixer')],
              },
            },
          },
        ],
      },
    ],
  },
};

Vite Resource Optimization

// vite.config.js
import { defineConfig } from 'vite';
import viteCompression from 'vite-plugin-compression';

export default defineConfig({
  build: {
    chunkSizeWarningLimit: 2000,
    rollupOptions: {
      output: {
        manualChunks: {
          'vendor': ['react', 'react-dom', 'lodash'],
        },
      },
    },
    cssCodeSplit: true,
    sourcemap: false,
  },
  plugins: [
    viteCompression({
      algorithm: 'gzip',
      ext: '.gz',
    }),
  ],
});

CDN Acceleration

Static Resource CDN Configuration

// webpack.config.js CDN configuration
module.exports = {
  output: {
    publicPath: 'https://cdn.example.com/assets/',
  },
};

Dynamic Import of CDN Paths

const module = await import(
  /* webpackIgnore: true */
  'https://cdn.example.com/modules/my-module.js'
);

Multi-CDN Strategy

// Select CDN based on geographic location
function getCDNUrl(path) {
  const region = getUserRegion(); // Get user region
  const cdnMap = {
    'us': 'https://us.cdn.example.com',
    'eu': 'https://eu.cdn.example.com',
    'cn': 'https://cn.cdn.example.com',
  };
  return `${cdnMap[region] || cdnMap['us']}/${path}`;
}

Runtime Performance Optimization

Virtual List Techniques

Basic Virtual List Implementation

function VirtualList({ items, itemHeight, containerHeight }) {
  const [scrollTop, setScrollTop] = useState(0);
  const containerRef = useRef(null);
  
  const visibleCount = Math.ceil(containerHeight / itemHeight);
  const startIndex = Math.floor(scrollTop / itemHeight);
  const endIndex = startIndex + visibleCount;
  
  const visibleItems = items.slice(startIndex, endIndex);
  
  const paddingTop = startIndex * itemHeight;
  const paddingBottom = (items.length - endIndex) * itemHeight;
  
  return (
    <div 
      ref={containerRef}
      style={{ height: containerHeight, overflowY: 'auto' }}
      onScroll={(e) => setScrollTop(e.target.scrollTop)}
    >
      <div style={{ height: items.length * itemHeight, position: 'relative' }}>
        <div style={{ height: paddingTop, position: 'absolute', top: 0 }} />
        {visibleItems.map((item, index) => (
          <div 
            key={startIndex + index}
            style={{ 
              position: 'absolute', 
              top: (startIndex + index) * itemHeight,
              width: '100%'
            }}
          >
            {item}
          </div>
        ))}
        <div style={{ height: paddingBottom, position: 'absolute', bottom: 0 }} />
      </div>
    </div>
  );
}

Optimized Virtual List (with Buffer)

function OptimizedVirtualList({ items, itemHeight, containerHeight, buffer = 5 }) {
  // ...Basic implementation
  
  const visibleCount = Math.ceil(containerHeight / itemHeight) + buffer * 2;
  const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - buffer);
  const endIndex = Math.min(items.length, startIndex + visibleCount);
  
  // ...Rest of the implementation
}

Throttling and Debouncing

Debounce Implementation

function debounce(func, wait) {
  let timeout;
  return function(...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(this, args);
    }, wait);
  };
}

// Usage example
const handleResize = debounce(() => {
  console.log('Window resized');
}, 300);

window.addEventListener('resize', handleResize);

Throttle Implementation

function throttle(func, limit) {
  let inThrottle;
  return function(...args) {
    if (!inThrottle) {
      func.apply(this, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

// Usage example
const handleScroll = throttle(() => {
  console.log('Scrolling...');
}, 200);

window.addEventListener('scroll', handleScroll);

Web Worker Applications

Basic Web Worker

// worker.js
self.onmessage = function(e) {
  const result = heavyCalculation(e.data);
  self.postMessage(result);
};

function heavyCalculation(data) {
  // Complex computation
  return data * 2;
}

// Main thread
const worker = new Worker('worker.js');
worker.postMessage(42);
worker.onmessage = function(e) {
  console.log('Result:', e.data); // 84
};

Multi-Threaded Task Distribution

// worker-pool.js
class WorkerPool {
  constructor(workerPath, size) {
    this.workerPath = workerPath;
    this.size = size;
    this.workers = [];
    this.queue = [];
    this.activeCount = 0;
    
    for (let i = 0; i < size; i++) {
      this.workers.push(this.createWorker());
    }
  }
  
  createWorker() {
    const worker = new Worker(this.workerPath);
    const taskQueue = [];
    
    worker.onmessage = function(e) {
      const { resolve } = taskQueue.shift();
      resolve(e.data);
      if (this.taskQueue.length > 0) {
        this.runNextTask();
      } else {
        this.activeCount--;
      }
    }.bind(this);
    
    return {
      postMessage: (data) => {
        return new Promise(resolve => {
          taskQueue.push({ data, resolve });
          this.runNextTask();
        });
      }
    };
  }
  
  runNextTask() {
    if (this.activeCount >= this.size || this.taskQueue.length === 0) return;
    
    const { data, resolve } = this.taskQueue.shift();
    const worker = this.workers[this.activeCount % this.size];
    this.activeCount++;
    worker.postMessage(data);
  }
  
  postMessage(data) {
    return this.workers[0].postMessage(data); // Simplified version, should distribute to idle worker
  }
}

// Usage example
const pool = new WorkerPool('worker.js', 4);
pool.postMessage(largeData).then(result => {
  console.log('Worker result:', result);
});

Memory Management

Memory Leak Detection

Chrome DevTools Detection

  1. Open Chrome DevTools -> Memory
  2. Use Heap Snapshot feature
  3. Compare heap snapshots at different times to identify unreleased objects

Code-Level Detection

// Monitor object references
const objectReferences = new WeakMap();

function trackObject(obj, name) {
  if (!objectReferences.has(obj)) {
    objectReferences.set(obj, new Set());
  }
  objectReferences.get(obj).add(name);
}

function checkLeaks() {
  console.log('Active object references:', objectReferences);
}

// Usage example
const bigData = new Array(1000000).fill('data');
trackObject(bigData, 'bigData');

// Manually release reference when no longer needed
bigData = null;
checkLeaks();

Garbage Collection Mechanism Understanding

Garbage Collection Types

  1. Mark-and-Sweep:
    • Marks all reachable objects starting from root objects (window, global)
    • Clears unmarked objects
  2. Reference Counting:
    • Tracks the number of references to each object
    • Recycles when reference count reaches zero
    • Cannot handle circular references (modern browsers have improved)

Manual GC Trigger (Development Environment Only)

// Chrome console command
window.gc(); // Requires enabling chrome://flags/#enable-experimental-web-platform-features

Memory Optimization for Large Applications

Large Array Processing

// Process large arrays in chunks
function processLargeArray(array, chunkSize, processFn) {
  for (let i = 0; i < array.length; i += chunkSize) {
    const chunk = array.slice(i, i + chunkSize);
    processFn(chunk);
    // Manually release references no longer needed
    if (i > 0 && i % (chunkSize * 10) === 0) {
      array = null; // Assist GC
      global.gc?.(); // Attempt to trigger GC
    }
  }
}

Cache Management

class LRUCache {
  constructor(capacity) {
    this.capacity = capacity;
    this.cache = new Map();
  }
  
  get(key) {
    if (!this.cache.has(key)) return -1;
    const value = this.cache.get(key);
    this.cache.delete(key);
    this.cache.set(key, value);
    return value;
  }
  
  put(key, value) {
    if (this.cache.has(key)) {
      this.cache.delete(key);
    } else if (this.cache.size >= this.capacity) {
      const oldestKey = this.cache.keys().next().value;
      this.cache.delete(oldestKey);
    }
    this.cache.set(key, value);
  }
}

Rendering Performance Optimization

React-Specific Optimization Techniques

React.memo and useMemo

const MyComponent = React.memo(({ data }) => {
  const processedData = useMemo(() => heavyProcessing(data), [data]);
  
  return <div>{processedData}</div>;
});

Virtualized Long Lists

import { FixedSizeList as List } from 'react-window';

const Row = ({ index, style }) => (
  <div style={style}>Row {index}</div>
);

const BigList = () => (
  <List
    height={400}
    itemCount={1000}
    itemSize={35}
    width={300}
  >
    {Row}
  </List>
);

Vue-Specific Optimization Techniques

v-once and v-memo

<template>
  <!-- Static content rendered once -->
  <div v-once>This will never change</div>
  
  <!-- Conditional rendering optimization -->
  <div v-memo="[valueA, valueB]">
    ...
  </div>
</template>

Async Components

const AsyncComponent = defineAsyncComponent(() =>
  import('./components/HeavyComponent.vue')
);

Virtual DOM Optimization

Key Optimization

// Bad practice - Using array index as key
{items.map((item, index) => (
  <Item key={index} data={item} />
))}

// Good practice - Using unique identifier
{items.map(item => (
  <Item key={item.id} data={item} />
))}

Batch Update Optimization

// React automatic batch updates
function handleClick() {
  setState1({ ... }); // No immediate render
  setState2({ ... }); // No immediate render
  // Render together
}

// Manual batch updates (React 18+)
import { unstable_batchedUpdates } from 'react-dom';

function handleClick() {
  unstable_batchedUpdates(() => {
    setState1({ ... });
    setState2({ ... });
  });
}

Chunked Rendering Strategies

Time Slicing

// Automatically implemented in React 18
// Manual implementation example
function chunkedRender(items, renderChunk, chunkSize = 10) {
  let index = 0;
  
  function processNextChunk() {
    const chunk = items.slice(index, index + chunkSize);
    renderChunk(chunk);
    index += chunkSize;
    
    if (index < items.length) {
      requestIdleCallback(processNextChunk);
    }
  }
  
  requestIdleCallback(processNextChunk);
}

Priority Rendering

// Sort elements by importance for rendering priority
function prioritizeRender(elements) {
  return elements.sort((a, b) => {
    // Render high visual importance elements first
    return b.priority - a.priority;
  });
}

// Progressive rendering
function progressiveRender(items, renderFn, batchSize = 5) {
  let index = 0;
  
  function renderBatch() {
    const batch = items.slice(index, index + batchSize);
    renderFn(batch);
    index += batchSize;
    
    if (index < items.length) {
      setTimeout(renderBatch, 50); // Delay rendering next batch
    }
  }
  
  renderBatch();
}

Summary

Performance optimization in micro frontend architectures requires comprehensive consideration across multiple dimensions:

  1. Load Performance: Reduce initial load time through CDN, resource compression, and preloading techniques.
  2. Runtime Performance: Enhance interaction experience using virtual lists, throttling/debouncing, and Web Workers.
  3. Memory Management: Prevent memory leaks through detection tools and thoughtful design.
  4. Rendering Performance: Optimize for React/Vue-specific features, leveraging virtual DOM and chunked rendering strategies.

Each optimization point should be balanced based on specific business scenarios and technology stacks. It is recommended to use performance monitoring tools to continuously track optimization results and validate the effectiveness of optimization strategies through A/B testing.

Share your love