Appearance
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 在不同浏览器环境中正常工作。