Skip to content
On this page

qiankun 调试与故障排除

调试工具和方法

1. 开发环境配置

启用调试模式

javascript
// 开发环境下启用详细日志
const isDev = process.env.NODE_ENV === 'development';

// qiankun 配置中启用调试
import { start, initGlobalState } from 'qiankun';

if (isDev) {
  // 启用 qiankun 内部日志
  window.__QIANKUN_DEVELOPMENT__ = true;
}

start({
  prefetch: true,
  sandbox: {
    strictStyleIsolation: false, // 开发时可临时关闭样式隔离便于调试
  },
  // 错误处理
  error: (error, app) => {
    console.error('qiankun error:', error, app);
    // 在开发环境显示更详细的错误信息
    if (isDev) {
      alert(`微应用加载错误: ${error.message}`);
    }
  }
});

微应用调试配置

javascript
// 微应用入口文件调试配置
let app = null;

// 开发环境下的额外调试信息
const DEBUG = process.env.NODE_ENV === 'development';

export async function bootstrap(props) {
  if (DEBUG) {
    console.log('[qiankun] App bootstrap:', props);
  }
}

export async function mount(props) {
  if (DEBUG) {
    console.log('[qiankun] App mount:', props);
  }
  
  const { container, history } = props;
  
  app = new Vue({
    router: createRouter(history),
    store,
    render: h => h(App),
  }).$mount(container ? container.querySelector('#app') : '#app');
  
  if (DEBUG) {
    // 添加调试工具到全局
    window.__MICRO_APP__ = app;
    console.log('[qiankun] App mounted successfully');
  }
}

export async function unmount(props) {
  if (DEBUG) {
    console.log('[qiankun] App unmount:', props);
  }
  
  if (app) {
    app.$destroy();
    app.$el.innerHTML = '';
    app = null;
    
    if (DEBUG) {
      delete window.__MICRO_APP__;
      console.log('[qiankun] App unmounted successfully');
    }
  }
}

2. 浏览器开发者工具

控制台调试命令

javascript
// 在浏览器控制台中可用的调试命令

// 检查 qiankun 状态
console.log('qiankun loaded:', !!window.__POWERED_BY_QIANKUN__);

// 检查已注册的微应用
console.log('registered apps:', window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__);

// 手动加载微应用
const { loadMicroApp } = require('qiankun');
loadMicroApp({
  name: 'test-app',
  entry: '//localhost:3001',
  container: '#test-container',
});

// 检查全局状态
const { initGlobalState } = require('qiankun');
const globalState = initGlobalState({});
console.log('global state:', globalState);

// 重新启动 qiankun
const { start } = require('qiankun');
start();

网络请求调试

javascript
// 拦截和记录网络请求
const originalFetch = window.fetch;
window.fetch = function(...args) {
  console.log('[qiankun] Fetch request:', args[0]);
  
  return originalFetch.apply(this, args)
    .then(response => {
      console.log('[qiankun] Fetch response:', response.status, args[0]);
      return response;
    })
    .catch(error => {
      console.error('[qiankun] Fetch error:', error, args[0]);
      throw error;
    });
};

常见问题诊断

1. 微应用加载问题

问题:微应用无法加载

javascript
// 诊断步骤
function diagnoseAppLoading(appName) {
  console.log(`Diagnosing app: ${appName}`);
  
  // 1. 检查入口地址
  const appConfig = getMicroAppConfig(appName);
  console.log('App config:', appConfig);
  
  // 2. 检查网络连接
  fetch(appConfig.entry)
    .then(response => {
      console.log(`Entry response status: ${response.status}`);
      if (response.ok) {
        return response.text();
      } else {
        throw new Error(`HTTP ${response.status}`);
      }
    })
    .then(content => {
      console.log('Entry content loaded successfully');
      // 检查内容是否为有效的 HTML
      if (content.includes('<!DOCTYPE') || content.includes('<html')) {
        console.log('Entry returns HTML - this might be wrong');
      }
    })
    .catch(error => {
      console.error('Entry loading failed:', error);
    });
}

// 使用示例
diagnoseAppLoading('my-micro-app');

问题:微应用加载后立即卸载

javascript
// 调试微应用生命周期
export async function mount(props) {
  console.log('[qiankun] Mount called at:', new Date().toISOString());
  console.log('[qiankun] Props:', props);
  
  // 记录挂载时间
  window.__MOUNT_TIME__ = Date.now();
  
  // 应用挂载逻辑
  app = new Vue({
    router,
    store,
    render: h => h(App),
  }).$mount(props.container.querySelector('#app'));
  
  // 设置调试标志
  window.__APP_MOUNTED__ = true;
}

export async function unmount(props) {
  console.log('[qiankun] Unmount called after:', 
    Date.now() - window.__MOUNT_TIME__, 'ms');
  console.log('[qiankun] Unmount props:', props);
  
  // 清理逻辑
  if (app) {
    app.$destroy();
    app.$el.innerHTML = '';
    app = null;
  }
  
  window.__APP_MOUNTED__ = false;
}

2. 通信问题诊断

问题:主应用和微应用通信失败

javascript
// 通信调试工具
class CommunicationDebugger {
  constructor() {
    this.messageLog = [];
    this.lastState = null;
  }
  
  logMessage(type, data, source = 'unknown') {
    const logEntry = {
      type,
      data,
      source,
      timestamp: Date.now(),
      sequence: this.messageLog.length + 1
    };
    
    this.messageLog.push(logEntry);
    console.log(`[qiankun-communication] ${type}:`, data);
    
    // 保留最近100条记录
    if (this.messageLog.length > 100) {
      this.messageLog = this.messageLog.slice(-100);
    }
  }
  
  checkStateSync(newState, oldState) {
    if (JSON.stringify(newState) !== JSON.stringify(oldState)) {
      this.logMessage('STATE_CHANGE', { 
        old: oldState, 
        new: newState,
        diff: this.getDiff(oldState, newState)
      });
      this.lastState = newState;
    }
  }
  
  getDiff(oldState, newState) {
    // 简单的状态差异比较
    const diff = {};
    const allKeys = new Set([...Object.keys(oldState || {}), ...Object.keys(newState || {})]);
    
    for (const key of allKeys) {
      if (oldState?.[key] !== newState?.[key]) {
        diff[key] = {
          old: oldState?.[key],
          new: newState?.[key]
        };
      }
    }
    
    return diff;
  }
  
  getRecentMessages(limit = 10) {
    return this.messageLog.slice(-limit);
  }
  
  clearLogs() {
    this.messageLog = [];
  }
}

const commDebugger = new CommunicationDebugger();

全局状态调试

javascript
// 全局状态调试
import { initGlobalState } from 'qiankun';

const { onGlobalStateChange, setGlobalState, getGlobalState } = initGlobalState({
  debug: true, // 开发时启用调试状态
  user: null,
  theme: 'light',
});

// 监听状态变化并记录
onGlobalStateChange((state, prev) => {
  commDebugger.checkStateSync(state, prev);
  
  if (process.env.NODE_ENV === 'development') {
    console.log('[qiankun] Global state changed:', {
      current: state,
      previous: prev,
      timestamp: new Date().toISOString()
    });
  }
}, true);

// 调试版本的 setGlobalState
const debugSetGlobalState = (state, keepPrevious = true) => {
  const currentState = getGlobalState();
  console.log('[qiankun] Setting global state:', {
    from: currentState,
    to: state,
    keepPrevious
  });
  
  setGlobalState(state, keepPrevious);
  
  // 记录状态变更
  commDebugger.logMessage('GLOBAL_STATE_SET', {
    before: currentState,
    after: getGlobalState(),
    change: state
  });
};

路由问题调试

1. 路由冲突诊断

javascript
// 路由冲突调试工具
class RouteDebugger {
  constructor() {
    this.routeHistory = [];
    this.conflictDetector = new ConflictDetector();
  }
  
  observeRouteChanges() {
    // 监听浏览器历史变化
    const originalPushState = history.pushState;
    const originalReplaceState = history.replaceState;
    
    history.pushState = (...args) => {
      this.logRouteChange('pushState', args);
      return originalPushState.apply(history, args);
    };
    
    history.replaceState = (...args) => {
      this.logRouteChange('replaceState', args);
      return originalReplaceState.apply(history, args);
    };
    
    // 监听 popstate 事件
    window.addEventListener('popstate', (event) => {
      this.logRouteChange('popstate', { state: event.state, pathname: location.pathname });
    });
  }
  
  logRouteChange(method, args) {
    const logEntry = {
      method,
      args,
      url: location.href,
      timestamp: Date.now(),
      sequence: this.routeHistory.length + 1
    };
    
    this.routeHistory.push(logEntry);
    console.log(`[qiankun-route] ${method}:`, args);
    
    // 检测潜在冲突
    this.conflictDetector.checkForConflicts(logEntry);
    
    // 保留最近50条记录
    if (this.routeHistory.length > 50) {
      this.routeHistory = this.routeHistory.slice(-50);
    }
  }
  
  getRouteConflicts() {
    return this.conflictDetector.getConflicts();
  }
}

class ConflictDetector {
  constructor() {
    this.conflicts = [];
    this.routeMap = new Map();
  }
  
  checkForConflicts(routeChange) {
    const url = this.extractUrl(routeChange);
    const timestamp = routeChange.timestamp;
    
    if (this.routeMap.has(url)) {
      const previous = this.routeMap.get(url);
      if (timestamp - previous.timestamp < 1000) { // 1秒内的重复路由
        this.conflicts.push({
          type: 'rapid-route-change',
          url,
          first: previous,
          second: routeChange,
          timestamp: new Date().toISOString()
        });
        
        console.warn('[qiankun] Potential route conflict detected:', {
          url,
          interval: timestamp - previous.timestamp
        });
      }
    }
    
    this.routeMap.set(url, routeChange);
    
    // 清理过期记录
    this.cleanupOldRoutes();
  }
  
  extractUrl(routeChange) {
    if (routeChange.args && routeChange.args[2]) {
      return routeChange.args[2];
    }
    return routeChange.url || location.pathname;
  }
  
  cleanupOldRoutes() {
    const now = Date.now();
    const expired = [];
    
    for (const [url, record] of this.routeMap) {
      if (now - record.timestamp > 30000) { // 30秒过期
        expired.push(url);
      }
    }
    
    expired.forEach(url => this.routeMap.delete(url));
  }
  
  getConflicts() {
    return this.conflicts;
  }
}

const routeDebugger = new RouteDebugger();
routeDebugger.observeRouteChanges();

性能问题调试

1. 加载性能分析

javascript
// 性能分析工具
class PerformanceDebugger {
  constructor() {
    this.metrics = {
      appLoadTimes: {},
      resourceLoadTimes: {},
      memoryUsage: [],
      renderTimes: {}
    };
  }
  
  startAppLoad(appName) {
    this.metrics.appLoadTimes[appName] = {
      startTime: performance.now(),
      startMemory: this.getMemoryUsage()
    };
    
    console.log(`[qiankun-performance] Starting to load app: ${appName}`);
  }
  
  endAppLoad(appName) {
    if (this.metrics.appLoadTimes[appName]) {
      const loadTime = performance.now() - this.metrics.appLoadTimes[appName].startTime;
      const endMemory = this.getMemoryUsage();
      
      this.metrics.appLoadTimes[appName].loadTime = loadTime;
      this.metrics.appLoadTimes[appName].memoryDelta = 
        endMemory - this.metrics.appLoadTimes[appName].startMemory;
      
      console.log(`[qiankun-performance] App ${appName} loaded in ${loadTime.toFixed(2)}ms`, {
        memoryDelta: this.metrics.appLoadTimes[appName].memoryDelta
      });
      
      return {
        loadTime,
        memoryDelta: this.metrics.appLoadTimes[appName].memoryDelta
      };
    }
  }
  
  measureRenderTime(appName, renderFunction) {
    const startTime = performance.now();
    const result = renderFunction();
    const renderTime = performance.now() - startTime;
    
    if (!this.metrics.renderTimes[appName]) {
      this.metrics.renderTimes[appName] = [];
    }
    
    this.metrics.renderTimes[appName].push(renderTime);
    
    console.log(`[qiankun-performance] ${appName} render time: ${renderTime.toFixed(2)}ms`);
    
    return result;
  }
  
  getMemoryUsage() {
    if (performance.memory) {
      return performance.memory.usedJSHeapSize;
    }
    return 0;
  }
  
  getPerformanceReport() {
    const report = {
      averageLoadTimes: {},
      renderPerformance: {},
      memoryUsage: this.metrics.memoryUsage.slice(-10) // 最近10次
    };
    
    // 计算平均加载时间
    for (const [appName, times] of Object.entries(this.metrics.appLoadTimes)) {
      if (times.loadTime) {
        report.averageLoadTimes[appName] = times.loadTime;
      }
    }
    
    // 计算平均渲染时间
    for (const [appName, times] of Object.entries(this.metrics.renderTimes)) {
      const avg = times.reduce((a, b) => a + b, 0) / times.length;
      report.renderPerformance[appName] = {
        average: avg,
        count: times.length,
        min: Math.min(...times),
        max: Math.max(...times)
      };
    }
    
    return report;
  }
}

const perfDebugger = new PerformanceDebugger();

2. 内存泄漏检测

javascript
// 内存泄漏检测工具
class MemoryLeakDetector {
  constructor() {
    this.elementCount = new Map();
    this.eventListenerCount = new Map();
    this.intervalCount = 0;
    this.timeoutCount = 0;
    this.observerCount = 0;
  }
  
  startMonitoring() {
    // 监听元素创建和销毁
    this.interceptDOMMethods();
    
    // 定期检查内存使用
    this.memoryCheckInterval = setInterval(() => {
      this.checkMemoryUsage();
    }, 5000); // 每5秒检查一次
    
    console.log('[qiankun] Memory leak detection started');
  }
  
  interceptDOMMethods() {
    // 拦截元素创建
    const originalAppendChild = Element.prototype.appendChild;
    Element.prototype.appendChild = function(child) {
      this._tracked = true;
      if (child.tagName) {
        const count = this.elementCount.get(child.tagName) || 0;
        this.elementCount.set(child.tagName, count + 1);
      }
      return originalAppendChild.call(this, child);
    };
    
    // 拦截事件监听器添加
    const originalAddEventListener = EventTarget.prototype.addEventListener;
    EventTarget.prototype.addEventListener = function(type, listener, options) {
      const key = `${this.constructor.name}-${type}`;
      const count = this.eventListenerCount.get(key) || 0;
      this.eventListenerCount.set(key, count + 1);
      return originalAddEventListener.call(this, type, listener, options);
    };
  }
  
  checkMemoryUsage() {
    if (performance.memory) {
      const memory = performance.memory;
      const usage = {
        used: memory.usedJSHeapSize,
        total: memory.totalJSHeapSize,
        limit: memory.jsHeapSizeLimit,
        timestamp: new Date().toISOString()
      };
      
      // 检查是否接近内存限制
      const usagePercent = (usage.used / usage.limit) * 100;
      if (usagePercent > 80) {
        console.warn('[qiankun] High memory usage detected:', usagePercent.toFixed(2) + '%');
        this.generateMemoryReport();
      }
      
      this.metrics.memoryUsage.push(usage);
    }
  }
  
  generateMemoryReport() {
    const report = {
      elementCounts: Object.fromEntries(this.elementCount),
      eventListenerCounts: Object.fromEntries(this.eventListenerCount),
      intervals: this.intervalCount,
      timeouts: this.timeoutCount,
      observers: this.observerCount
    };
    
    console.table(report);
    console.log('[qiankun] Memory report generated');
    
    return report;
  }
  
  stopMonitoring() {
    if (this.memoryCheckInterval) {
      clearInterval(this.memoryCheckInterval);
    }
    console.log('[qiankun] Memory leak detection stopped');
  }
}

const memoryDetector = new MemoryLeakDetector();

错误处理和日志

1. 错误收集和上报

javascript
// 错误收集工具
class ErrorCollector {
  constructor() {
    this.errors = [];
    this.maxErrors = 100;
    this.startCollecting();
  }
  
  startCollecting() {
    // 捕获运行时错误
    window.addEventListener('error', (event) => {
      this.collectError({
        type: 'runtime',
        message: event.error.message,
        stack: event.error.stack,
        filename: event.filename,
        lineno: event.lineno,
        colno: event.colno,
        timestamp: new Date().toISOString()
      });
    });
    
    // 捕获未处理的 Promise 错误
    window.addEventListener('unhandledrejection', (event) => {
      this.collectError({
        type: 'promise',
        message: event.reason.message || event.reason,
        stack: event.reason.stack,
        timestamp: new Date().toISOString()
      });
    });
  }
  
  collectError(error) {
    // 添加额外的上下文信息
    error.context = {
      url: window.location.href,
      userAgent: navigator.userAgent,
      timestamp: new Date().toISOString(),
      qiankunState: {
        poweredBy: window.__POWERED_BY_QIANKUN__,
        apps: this.getRegisteredApps()
      }
    };
    
    this.errors.push(error);
    
    // 限制错误数量
    if (this.errors.length > this.maxErrors) {
      this.errors = this.errors.slice(-this.maxErrors);
    }
    
    console.error('[qiankun-error] Collected error:', error);
    
    // 可选:上报错误
    this.reportError(error);
  }
  
  getRegisteredApps() {
    // 获取已注册的微应用信息
    if (window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__) {
      return Object.keys(window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__);
    }
    return [];
  }
  
  reportError(error) {
    // 发送错误到监控服务
    if (process.env.NODE_ENV === 'production') {
      fetch('/api/errors', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          error,
          type: 'qiankun-micro-frontend'
        })
      }).catch(err => {
        console.error('Failed to report error:', err);
      });
    }
  }
  
  getRecentErrors(limit = 10) {
    return this.errors.slice(-limit);
  }
  
  clearErrors() {
    this.errors = [];
  }
}

const errorCollector = new ErrorCollector();

2. 调试工具面板

javascript
// 调试工具面板
class QiankunDevTools {
  constructor() {
    this.isOpen = false;
    this.createPanel();
  }
  
  createPanel() {
    // 创建调试面板元素
    this.panel = document.createElement('div');
    this.panel.id = 'qiankun-devtools';
    this.panel.innerHTML = `
      <div style="position: fixed; bottom: 20px; right: 20px; width: 300px; 
                  background: white; border: 1px solid #ccc; padding: 10px; 
                  z-index: 10000; font-family: monospace; font-size: 12px;">
        <div style="display: flex; justify-content: space-between; align-items: center;">
          <h4>qiankun DevTools</h4>
          <button onclick="window.qiankunDevTools.toggle()">X</button>
        </div>
        <div id="qiankun-status"></div>
        <div id="qiankun-actions" style="margin-top: 10px;">
          <button onclick="window.qiankunDevTools.refresh()">Refresh</button>
          <button onclick="window.qiankunDevTools.showErrors()">Errors</button>
          <button onclick="window.qiankunDevTools.showPerformance()">Performance</button>
        </div>
        <div id="qiankun-details" style="margin-top: 10px; max-height: 200px; overflow-y: auto;"></div>
      </div>
    `;
    
    document.body.appendChild(this.panel);
    this.panel.style.display = 'none';
    
    // 初始状态更新
    this.updateStatus();
  }
  
  toggle() {
    this.isOpen = !this.isOpen;
    this.panel.style.display = this.isOpen ? 'block' : 'none';
    if (this.isOpen) {
      this.updateStatus();
    }
  }
  
  updateStatus() {
    const statusDiv = this.panel.querySelector('#qiankun-status');
    statusDiv.innerHTML = `
      <div>Powered by qiankun: ${!!window.__POWERED_BY_QIANKUN__}</div>
      <div>Registered apps: ${this.getRegisteredApps().length}</div>
      <div>Recent errors: ${errorCollector.errors.length}</div>
      <div>Performance checks: ${Object.keys(perfDebugger.metrics.appLoadTimes).length}</div>
    `;
  }
  
  getRegisteredApps() {
    return window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ 
      ? Object.keys(window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__) 
      : [];
  }
  
  refresh() {
    location.reload();
  }
  
  showErrors() {
    const detailsDiv = this.panel.querySelector('#qiankun-details');
    const recentErrors = errorCollector.getRecentErrors(5);
    
    detailsDiv.innerHTML = `
      <h5>Recent Errors:</h5>
      ${recentErrors.map(err => `
        <div style="margin: 5px 0; padding: 5px; background: #ffe6e6; border-radius: 3px;">
          <div><strong>${err.message}</strong></div>
          <div style="font-size: 10px; color: #666;">${err.timestamp}</div>
        </div>
      `).join('')}
    `;
  }
  
  showPerformance() {
    const detailsDiv = this.panel.querySelector('#qiankun-details');
    const perfReport = perfDebugger.getPerformanceReport();
    
    detailsDiv.innerHTML = `
      <h5>Performance Report:</h5>
      <div><strong>Average Load Times:</strong></div>
      ${Object.entries(perfReport.averageLoadTimes).map(([app, time]) => 
        `<div>${app}: ${time.toFixed(2)}ms</div>`
      ).join('')}
      
      <div style="margin-top: 10px;"><strong>Render Performance:</strong></div>
      ${Object.entries(perfReport.renderPerformance).map(([app, metrics]) => 
        `<div>${app}: avg ${metrics.average.toFixed(2)}ms (${metrics.count} samples)</div>`
      ).join('')}
    `;
  }
}

// 全局访问
window.qiankunDevTools = new QiankunDevTools();

// 快捷键打开调试面板
document.addEventListener('keydown', (e) => {
  if (e.ctrlKey && e.shiftKey && e.key === 'Q') {
    window.qiankunDevTools.toggle();
  }
});

生产环境调试

1. 远程调试配置

javascript
// 生产环境调试配置(需要明确启用)
class ProductionDebugger {
  constructor() {
    // 通过 URL 参数或本地存储启用生产环境调试
    this.enabled = this.isProductionDebugEnabled();
    this.sessionId = this.generateSessionId();
  }
  
  isProductionDebugEnabled() {
    // 检查 URL 参数
    const urlParams = new URLSearchParams(window.location.search);
    if (urlParams.get('qiankun-debug') === 'true') {
      return true;
    }
    
    // 检查本地存储
    return localStorage.getItem('qiankun-debug') === 'true';
  }
  
  generateSessionId() {
    return Math.random().toString(36).substr(2, 9) + '-' + Date.now();
  }
  
  log(message, data = null) {
    if (this.enabled) {
      const logEntry = {
        sessionId: this.sessionId,
        timestamp: new Date().toISOString(),
        message,
        data,
        url: window.location.href
      };
      
      // 发送到远程日志服务
      this.sendToRemoteLog(logEntry);
    }
  }
  
  sendToRemoteLog(logEntry) {
    // 发送日志到远程服务
    fetch('/api/qiankun-logs', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(logEntry)
    }).catch(err => {
      console.error('Failed to send production log:', err);
    });
  }
  
  enable() {
    localStorage.setItem('qiankun-debug', 'true');
    this.enabled = true;
    console.log('Production debugging enabled for this session');
  }
  
  disable() {
    localStorage.removeItem('qiankun-debug');
    this.enabled = false;
    console.log('Production debugging disabled');
  }
}

const prodDebugger = new ProductionDebugger();

通过这些调试工具和方法,可以有效地诊断和解决 qiankun 微前端应用中的各种问题。