Skip to content
On this page

qiankun 浏览器兼容性

兼容性概述

qiankun 微前端框架对浏览器有一定的兼容性要求,主要依赖于现代浏览器特性如 Proxy、Web Components 等。了解兼容性有助于在不同环境中正确部署和使用 qiankun。

核心兼容性要求

1. 基础兼容性

qiankun 的最低浏览器要求:

javascript
// qiankun 需要的浏览器特性检测
const requiredFeatures = {
  // Proxy 对象(用于 JS 沙箱)
  proxy: typeof Proxy !== 'undefined',
  // Promise(用于异步操作)
  promise: typeof Promise !== 'undefined',
  // fetch API(用于资源加载)
  fetch: typeof fetch !== 'undefined',
  // History API(用于路由管理)
  history: !!(window.history && window.history.pushState),
  // Web Components(用于某些高级功能)
  webComponents: !!(window.customElements && window.ShadowRoot)
};

function checkBrowserCompatibility() {
  const missingFeatures = Object.entries(requiredFeatures)
    .filter(([feature, isSupported]) => !isSupported)
    .map(([feature]) => feature);
  
  if (missingFeatures.length > 0) {
    console.warn('Browser compatibility issues:', missingFeatures);
    return false;
  }
  
  return true;
}

// 检查兼容性
if (!checkBrowserCompatibility()) {
  console.error('Current browser does not support qiankun requirements');
  // 可以显示降级页面或错误信息
  showCompatibilityWarning();
}

2. Proxy 兼容性

Proxy 是 qiankun 沙箱机制的核心:

javascript
// Proxy 兼容性检测和降级
function getProxySupportLevel() {
  if (typeof Proxy === 'undefined') {
    return {
      level: 'none',
      message: 'Proxy not supported, sandbox will not work',
      fallback: 'snapshot' // 使用快照沙箱
    };
  }
  
  try {
    // 测试 Proxy 的基本功能
    const testProxy = new Proxy({}, {
      get(target, prop) {
        if (prop === 'test') return 'success';
        return target[prop];
      }
    });
    
    if (testProxy.test === 'success') {
      return {
        level: 'full',
        message: 'Full Proxy support available',
        fallback: null
      };
    }
  } catch (e) {
    // 某些浏览器实现不完整
    return {
      level: 'partial',
      message: 'Partial Proxy support',
      fallback: 'snapshot'
    };
  }
  
  return {
    level: 'none',
    message: 'Proxy not functional',
    fallback: 'snapshot'
  };
}

const proxySupport = getProxySupportLevel();
console.log('Proxy support level:', proxySupport);

各浏览器兼容性详情

1. Chrome 兼容性

javascript
// Chrome 版本检测
function checkChromeCompatibility() {
  const userAgent = navigator.userAgent;
  const chromeMatch = userAgent.match(/Chrome\/(\d+)/);
  
  if (chromeMatch) {
    const version = parseInt(chromeMatch[1], 10);
    
    if (version >= 49) {
      return {
        supported: true,
        version,
        features: {
          proxy: true,
          es6: true,
          fetch: true,
          webComponents: true
        }
      };
    } else {
      return {
        supported: false,
        version,
        requiredVersion: 49,
        missingFeatures: ['proxy', 'fetch']
      };
    }
  }
  
  return { supported: false, message: 'Not Chrome' };
}

2. Firefox 兼容性

javascript
// Firefox 版本检测
function checkFirefoxCompatibility() {
  const userAgent = navigator.userAgent;
  const firefoxMatch = userAgent.match(/Firefox\/(\d+)/);
  
  if (firefoxMatch) {
    const version = parseInt(firefoxMatch[1], 10);
    
    if (version >= 18) {
      return {
        supported: true,
        version,
        features: {
          proxy: version >= 18,
          es6: version >= 21,
          fetch: version >= 39,
          webComponents: version >= 63
        }
      };
    } else {
      return {
        supported: false,
        version,
        requiredVersion: 18,
        missingFeatures: ['proxy']
      };
    }
  }
  
  return { supported: false, message: 'Not Firefox' };
}

3. Safari 兼容性

javascript
// Safari 版本检测
function checkSafariCompatibility() {
  const userAgent = navigator.userAgent;
  const safariMatch = userAgent.match(/Version\/(\d+).*Safari/);
  
  if (safariMatch) {
    const version = parseInt(safariMatch[1], 10);
    
    if (version >= 10) {
      return {
        supported: true,
        version,
        features: {
          proxy: version >= 10,
          es6: true,
          fetch: version >= 10.1,
          webComponents: version >= 14
        }
      };
    } else {
      return {
        supported: false,
        version,
        requiredVersion: 10,
        missingFeatures: ['proxy', 'fetch']
      };
    }
  }
  
  return { supported: false, message: 'Not Safari' };
}

4. Internet Explorer 兼容性

javascript
// IE 兼容性处理
function checkIECompatibility() {
  const userAgent = navigator.userAgent;
  
  // 检测 IE
  const isIE = userAgent.indexOf('MSIE') !== -1 || 
               userAgent.indexOf('Trident') !== -1;
  
  if (isIE) {
    const version = getIEVersion(userAgent);
    
    if (version >= 11) {
      // IE 11 支持有限,需要降级
      return {
        supported: 'limited',
        version,
        features: {
          proxy: false, // IE11 不支持 Proxy
          es6: false,   // 部分支持
          fetch: false, // 需要 polyfill
          webComponents: false
        },
        fallback: {
          sandbox: 'snapshot', // 使用快照沙箱
          polyfills: ['fetch', 'promise', 'proxy']
        }
      };
    } else {
      return {
        supported: false,
        version,
        requiredVersion: 11,
        message: 'IE 10 and below are not supported'
      };
    }
  }
  
  return { supported: false, message: 'Not IE' };
}

function getIEVersion(userAgent) {
  const msie = userAgent.indexOf('MSIE ');
  if (msie > 0) {
    return parseInt(userAgent.substring(msie + 5, userAgent.indexOf('.', msie)), 10);
  }
  
  const trident = userAgent.indexOf('Trident/');
  if (trident > 0) {
    const rv = userAgent.indexOf('rv:');
    return parseInt(userAgent.substring(rv + 3, userAgent.indexOf('.', rv)), 10);
  }
  
  return -1;
}

兼容性检测工具

1. 综合兼容性检测

javascript
// 综合浏览器兼容性检测
class BrowserCompatibilityChecker {
  constructor() {
    this.checks = [
      this.checkES6Support.bind(this),
      this.checkProxySupport.bind(this),
      this.checkFetchSupport.bind(this),
      this.checkHistorySupport.bind(this),
      this.checkCustomElementsSupport.bind(this)
    ];
  }
  
  runAllChecks() {
    const results = {
      browser: this.getBrowserInfo(),
      features: {},
      compatibility: 'unknown',
      recommendations: []
    };
    
    for (const check of this.checks) {
      const result = check();
      results.features[result.feature] = result;
    }
    
    // 计算整体兼容性
    results.compatibility = this.calculateOverallCompatibility(results.features);
    results.recommendations = this.generateRecommendations(results);
    
    return results;
  }
  
  checkES6Support() {
    const supported = (
      typeof Symbol !== 'undefined' &&
      typeof Promise !== 'undefined' &&
      typeof Map !== 'undefined' &&
      typeof Set !== 'undefined' &&
      typeof Array.prototype.includes !== 'undefined' &&
      typeof Object.assign !== 'undefined'
    );
    
    return {
      feature: 'es6',
      supported,
      required: true,
      details: {
        symbol: typeof Symbol !== 'undefined',
        promise: typeof Promise !== 'undefined',
        map: typeof Map !== 'undefined',
        set: typeof Set !== 'undefined'
      }
    };
  }
  
  checkProxySupport() {
    const supported = typeof Proxy !== 'undefined';
    
    return {
      feature: 'proxy',
      supported,
      required: true,
      details: { proxyAvailable: supported }
    };
  }
  
  checkFetchSupport() {
    const supported = typeof fetch !== 'undefined';
    
    return {
      feature: 'fetch',
      supported,
      required: true,
      details: { fetchAvailable: supported }
    };
  }
  
  checkHistorySupport() {
    const supported = !!(window.history && window.history.pushState);
    
    return {
      feature: 'history',
      supported,
      required: true,
      details: { historyAPI: supported }
    };
  }
  
  checkCustomElementsSupport() {
    const supported = !!(window.customElements && window.customElements.define);
    
    return {
      feature: 'customElements',
      supported,
      required: false, // 不是必需的,但推荐
      details: { customElementsAvailable: supported }
    };
  }
  
  getBrowserInfo() {
    const ua = navigator.userAgent;
    let browser = 'unknown';
    let version = 'unknown';
    
    if (ua.indexOf('Chrome') > -1) {
      browser = 'Chrome';
      version = ua.match(/Chrome\/(\d+)/)?.[1];
    } else if (ua.indexOf('Firefox') > -1) {
      browser = 'Firefox';
      version = ua.match(/Firefox\/(\d+)/)?.[1];
    } else if (ua.indexOf('Safari') > -1 && ua.indexOf('Chrome') === -1) {
      browser = 'Safari';
      version = ua.match(/Version\/(\d+)/)?.[1];
    } else if (ua.indexOf('Edge') > -1) {
      browser = 'Edge';
      version = ua.match(/Edge\/(\d+)/)?.[1] || ua.match(/Edg\/(\d+)/)?.[1];
    } else if (ua.indexOf('MSIE') > -1 || ua.indexOf('Trident') > -1) {
      browser = 'IE';
      if (ua.indexOf('MSIE') > -1) {
        version = ua.match(/MSIE (\d+)/)?.[1];
      } else {
        version = ua.match(/rv:(\d+)/)?.[1];
      }
    }
    
    return { name: browser, version };
  }
  
  calculateOverallCompatibility(features) {
    const requiredChecks = Object.values(features).filter(f => f.required);
    const supportedRequired = requiredChecks.filter(f => f.supported);
    
    if (supportedRequired.length === requiredChecks.length) {
      return 'full';
    } else if (supportedRequired.length > 0) {
      return 'partial';
    } else {
      return 'none';
    }
  }
  
  generateRecommendations(results) {
    const recommendations = [];
    
    if (results.compatibility === 'none') {
      recommendations.push('Browser does not support qiankun requirements');
      recommendations.push('Consider using a modern browser');
    } else if (results.compatibility === 'partial') {
      recommendations.push('Some features are not supported');
      recommendations.push('qiankun may work with limited functionality');
      
      // 建议的 polyfills
      const missingFeatures = Object.entries(results.features)
        .filter(([key, value]) => value.required && !value.supported)
        .map(([key]) => key);
      
      if (missingFeatures.length > 0) {
        recommendations.push(`Missing features: ${missingFeatures.join(', ')}`);
        recommendations.push('Consider adding polyfills');
      }
    }
    
    return recommendations;
  }
}

// 使用兼容性检查器
const checker = new BrowserCompatibilityChecker();
const compatibilityResult = checker.runAllChecks();
console.log('Browser compatibility:', compatibilityResult);

Polyfill 支持

1. 必需的 Polyfills

javascript
// qiankun 兼容性 polyfills
const requiredPolyfills = [
  {
    name: 'Proxy',
    check: () => typeof Proxy !== 'undefined',
    polyfill: () => {
      // Proxy 无法完全 polyfill,使用快照沙箱
      console.warn('Proxy not supported, qiankun will use snapshot sandbox');
      return { sandboxType: 'snapshot' };
    },
    required: true
  },
  {
    name: 'Promise',
    check: () => typeof Promise !== 'undefined',
    polyfill: () => import('es6-promise').then(pkg => pkg.polyfill()),
    required: true
  },
  {
    name: 'fetch',
    check: () => typeof fetch !== 'undefined',
    polyfill: () => import('whatwg-fetch'),
    required: true
  },
  {
    name: 'Object.assign',
    check: () => typeof Object.assign !== 'undefined',
    polyfill: () => {
      if (!Object.assign) {
        Object.assign = function(target) {
          if (target === null || target === undefined) {
            throw new TypeError('Cannot convert undefined or null to object');
          }
          
          const output = Object(target);
          for (let index = 1; index < arguments.length; index++) {
            const source = arguments[index];
            if (source !== null) {
              for (const nextKey in source) {
                if (Object.prototype.hasOwnProperty.call(source, nextKey)) {
                  output[nextKey] = source[nextKey];
                }
              }
            }
          }
          return output;
        };
      }
    },
    required: true
  }
];

// 自动加载缺失的 polyfills
async function loadRequiredPolyfills() {
  for (const polyfill of requiredPolyfills) {
    if (!polyfill.check()) {
      if (polyfill.required) {
        console.warn(`Loading required polyfill for ${polyfill.name}`);
        await polyfill.polyfill();
      } else {
        console.info(`Optional feature ${polyfill.name} not supported`);
      }
    }
  }
}

// 在初始化 qiankun 之前加载 polyfills
await loadRequiredPolyfills();

2. 条件加载 Polyfills

javascript
// 根据浏览器特性条件加载 polyfills
class ConditionalPolyfillLoader {
  constructor() {
    this.loadedPolyfills = new Set();
  }
  
  async loadForBrowser(browserInfo) {
    const polyfills = this.getPolyfillsForBrowser(browserInfo);
    
    for (const polyfill of polyfills) {
      if (!this.loadedPolyfills.has(polyfill.name)) {
        await this.loadPolyfill(polyfill);
        this.loadedPolyfills.add(polyfill.name);
      }
    }
  }
  
  getPolyfillsForBrowser(browserInfo) {
    const { name, version } = browserInfo;
    const polyfills = [];
    
    // IE 特定 polyfills
    if (name === 'IE') {
      polyfills.push(
        { name: 'fetch', loader: () => import('whatwg-fetch') },
        { name: 'Promise', loader: () => import('es6-promise') },
        { name: 'Object.assign', loader: () => this.loadObjectAssign() }
      );
    }
    
    // Safari < 10.1
    if (name === 'Safari' && parseInt(version) < 10) {
      polyfills.push(
        { name: 'fetch', loader: () => import('whatwg-fetch') }
      );
    }
    
    // Firefox < 39
    if (name === 'Firefox' && parseInt(version) < 39) {
      polyfills.push(
        { name: 'fetch', loader: () => import('whatwg-fetch') }
      );
    }
    
    return polyfills;
  }
  
  async loadPolyfill(polyfill) {
    try {
      await polyfill.loader();
      console.log(`Loaded polyfill: ${polyfill.name}`);
    } catch (error) {
      console.error(`Failed to load polyfill ${polyfill.name}:`, error);
    }
  }
  
  loadObjectAssign() {
    if (!Object.assign) {
      Object.assign = function(target) {
        const to = Object(target);
        for (let index = 1; index < arguments.length; index++) {
          const nextSource = arguments[index];
          if (nextSource !== null) {
            for (const nextKey in nextSource) {
              if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                to[nextKey] = nextSource[nextKey];
              }
            }
          }
        }
        return to;
      };
    }
  }
}

// 使用条件 polyfill 加载器
const polyfillLoader = new ConditionalPolyfillLoader();
const browserInfo = new BrowserCompatibilityChecker().getBrowserInfo();
await polyfillLoader.loadForBrowser(browserInfo);

沙箱兼容性

1. 沙箱类型选择

javascript
// 根据浏览器能力选择沙箱类型
function selectSandboxType() {
  if (typeof Proxy !== 'undefined') {
    // 支持 Proxy,使用代理沙箱
    return {
      type: 'legacy', // qiankun 中的代理沙箱类型
      features: ['full-js-isolation', 'dynamic-patch'],
      performance: 'high'
    };
  } else {
    // 不支持 Proxy,使用快照沙箱
    return {
      type: 'snapshot',
      features: ['basic-js-isolation'],
      performance: 'medium',
      limitations: ['cannot-track-global-mutations-after-snapshot']
    };
  }
}

// qiankun 配置中使用适当的沙箱
const sandboxConfig = selectSandboxType();

// 启动 qiankun
import { start } from 'qiankun';

start({
  sandbox: {
    type: sandboxConfig.type,
    // 根据沙箱类型调整其他配置
    strictStyleIsolation: true
  },
  // 其他配置...
});

2. 兼容性降级策略

javascript
// qiankun 兼容性降级策略
class CompatibilityFallback {
  constructor() {
    this.fallbacks = {
      sandbox: this.determineSandboxFallback(),
      features: this.determineFeatureFallbacks(),
      performance: this.determinePerformanceFallbacks()
    };
  }
  
  determineSandboxFallback() {
    if (typeof Proxy !== 'undefined') {
      return { type: 'legacy', level: 'full' };
    } else if (typeof WeakMap !== 'undefined') {
      return { type: 'snapshot', level: 'basic' };
    } else {
      return { type: 'default', level: 'minimal' };
    }
  }
  
  determineFeatureFallbacks() {
    const fallbacks = {};
    
    // Fetch fallback
    if (typeof fetch === 'undefined') {
      fallbacks.fetch = 'XMLHttpRequest';
    }
    
    // Promise fallback
    if (typeof Promise === 'undefined') {
      fallbacks.promise = 'polyfill';
    }
    
    // History fallback
    if (!window.history || !window.history.pushState) {
      fallbacks.history = 'hash-routing';
    }
    
    return fallbacks;
  }
  
  determinePerformanceFallbacks() {
    const performanceLevel = [];
    
    if (typeof Proxy !== 'undefined') {
      performanceLevel.push('high');
    } else {
      performanceLevel.push('medium');
    }
    
    if (performance.memory) {
      performanceLevel.push('with-memory-tracking');
    } else {
      performanceLevel.push('without-memory-tracking');
    }
    
    return performanceLevel;
  }
  
  applyFallbacks() {
    console.log('Applying compatibility fallbacks:', this.fallbacks);
    
    // 根据降级策略调整 qiankun 配置
    const qiankunConfig = {
      sandbox: {
        type: this.fallbacks.sandbox.type
      },
      prefetch: Object.keys(this.fallbacks.features).length === 0, // 如果没有特性降级,启用预加载
      ...this.getFeatureSpecificConfig()
    };
    
    return qiankunConfig;
  }
  
  getFeatureSpecificConfig() {
    const config = {};
    
    // 根据特性降级调整配置
    if (this.fallbacks.features.fetch === 'XMLHttpRequest') {
      // 使用 XMLHttpRequest 时的特殊配置
      config.fetchWrapper = this.createXHRWrapper();
    }
    
    if (this.fallbacks.features.history === 'hash-routing') {
      // 使用 hash 路由时的特殊配置
      config.routing = 'hash';
    }
    
    return config;
  }
  
  createXHRWrapper() {
    // 创建 XMLHttpRequest 包装器用于替代 fetch
    return (url, options = {}) => {
      return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open(options.method || 'GET', url);
        
        // 设置请求头
        if (options.headers) {
          Object.keys(options.headers).forEach(key => {
            xhr.setRequestHeader(key, options.headers[key]);
          });
        }
        
        xhr.onload = () => {
          if (xhr.status >= 200 && xhr.status < 300) {
            resolve({
              ok: true,
              status: xhr.status,
              text: () => Promise.resolve(xhr.responseText),
              json: () => Promise.resolve(JSON.parse(xhr.responseText))
            });
          } else {
            reject(new Error(`HTTP ${xhr.status}: ${xhr.statusText}`));
          }
        };
        
        xhr.onerror = () => reject(new Error('Network Error'));
        xhr.send(options.body);
      });
    };
  }
}

// 使用兼容性降级
const fallback = new CompatibilityFallback();
const config = fallback.applyFallbacks();

// 启动 qiankun
start(config);

兼容性测试

1. 自动化兼容性测试

javascript
// 兼容性测试套件
class CompatibilityTestSuite {
  constructor() {
    this.tests = [
      this.testProxySandbox.bind(this),
      this.testStyleIsolation.bind(this),
      this.testResourceLoading.bind(this),
      this.testCommunication.bind(this)
    ];
  }
  
  async runAllTests() {
    const results = [];
    
    for (const test of this.tests) {
      try {
        const result = await test();
        results.push(result);
      } catch (error) {
        results.push({
          name: test.name,
          passed: false,
          error: error.message,
          details: error.stack
        });
      }
    }
    
    return results;
  }
  
  async testProxySandbox() {
    if (typeof Proxy === 'undefined') {
      return {
        name: 'Proxy Sandbox',
        passed: false,
        reason: 'Proxy not supported',
        suggestion: 'Use snapshot sandbox or upgrade browser'
      };
    }
    
    try {
      const testProxy = new Proxy({}, {
        get(target, prop) {
          return target[prop];
        }
      });
      
      testProxy.test = 'value';
      
      return {
        name: 'Proxy Sandbox',
        passed: testProxy.test === 'value',
        details: 'Proxy functionality works correctly'
      };
    } catch (error) {
      return {
        name: 'Proxy Sandbox',
        passed: false,
        error: error.message
      };
    }
  }
  
  async testStyleIsolation() {
    // 测试样式隔离是否工作
    const testDiv = document.createElement('div');
    testDiv.id = 'qiankun-style-test';
    testDiv.style.color = 'red';
    document.body.appendChild(testDiv);
    
    const computedStyle = window.getComputedStyle(testDiv);
    const hasStyleIsolation = computedStyle.color === 'rgb(255, 0, 0)';
    
    document.body.removeChild(testDiv);
    
    return {
      name: 'Style Isolation',
      passed: hasStyleIsolation,
      details: `Style isolation ${hasStyleIsolation ? 'works' : 'may have issues'}`
    };
  }
  
  async testResourceLoading() {
    // 测试资源加载能力
    try {
      // 检查 fetch 支持
      if (typeof fetch === 'undefined') {
        // 尝试 XMLHttpRequest
        const xhr = new XMLHttpRequest();
        xhr.open('GET', window.location.href, false);
        xhr.send();
        const xhrSupported = xhr.status >= 200 && xhr.status < 300;
        
        return {
          name: 'Resource Loading',
          passed: xhrSupported,
          details: xhrSupported ? 'XMLHttpRequest works' : 'Neither fetch nor XMLHttpRequest works'
        };
      }
      
      // 测试 fetch
      const response = await fetch(window.location.href, { method: 'HEAD' });
      return {
        name: 'Resource Loading',
        passed: response.ok,
        details: `Fetch works: ${response.ok}`
      };
    } catch (error) {
      return {
        name: 'Resource Loading',
        passed: false,
        error: error.message
      };
    }
  }
  
  async testCommunication() {
    // 测试通信机制
    try {
      // 测试 postMessage 或其他通信方式
      const channel = new MessageChannel();
      const testMessage = 'test-communication';
      
      return {
        name: 'Communication',
        passed: !!channel,
        details: 'MessageChannel API available'
      };
    } catch (error) {
      return {
        name: 'Communication',
        passed: false,
        details: 'MessageChannel not available, communication may be limited'
      };
    }
  }
  
  generateReport(results) {
    const passed = results.filter(r => r.passed).length;
    const total = results.length;
    
    return {
      summary: `${passed}/${total} tests passed`,
      details: results,
      compatible: passed === total,
      recommendations: this.generateRecommendations(results)
    };
  }
  
  generateRecommendations(results) {
    const recommendations = [];
    
    const failedTests = results.filter(r => !r.passed);
    if (failedTests.length > 0) {
      recommendations.push('Some compatibility tests failed');
      failedTests.forEach(test => {
        if (test.suggestion) {
          recommendations.push(`- ${test.name}: ${test.suggestion}`);
        }
      });
    }
    
    if (results.every(r => r.passed)) {
      recommendations.push('Browser is fully compatible with qiankun');
    }
    
    return recommendations;
  }
}

// 运行兼容性测试
const testSuite = new CompatibilityTestSuite();
const testResults = await testSuite.runAllTests();
const report = testSuite.generateReport(testResults);

console.log('Compatibility test report:', report);

通过这些兼容性检测和处理机制,可以确保 qiankun 在不同浏览器环境中正常工作。