引言:为什么需要关注 IE11 兼容性
尽管 Internet Explorer 11 (IE11) 已经逐渐退出历史舞台,但在许多企业环境、政府机构和特定行业中,IE11 仍然被广泛使用。根据 StatCounter 的数据,截至 2023 年,IE11 在全球浏览器市场份额中仍占约 0.5%-1%,在某些特定地区和行业(如金融、医疗、政府)中,这一比例可能更高。
Bootstrap 4 是一个现代化的前端框架,主要针对现代浏览器进行优化。虽然 Bootstrap 4 官方声称支持 IE11,但在实际使用中,开发者经常会遇到各种兼容性问题,包括 CSS 属性不支持、JavaScript API 缺失、Flexbox 布局异常等。
本文将详细介绍如何让 Bootstrap 4 在 IE11 中完美运行,包括必要的 Polyfill、构建工具配置、CSS 前缀处理以及常见问题的解决方案。
一、IE11 与 Bootstrap 4 的兼容性挑战
1.1 IE11 的主要限制
IE11 发布于 2013 年,其核心限制包括:
CSS 支持不完整
:不支持 CSS Grid、部分 Flexbox 特性、CSS 自定义属性(变量)、
calc()
函数的部分用法等
ES5 JavaScript 限制
:不支持 ES6+ 语法(如箭头函数、Promise、Class 等)
缺失现代 Web API
:不支持 Fetch API、Intersection Observer、Custom Events 等
CSS 前缀要求
:需要
-ms-
前缀来支持某些特性(如 Flexbox)
1.2 Bootstrap 4 的依赖项
Bootstrap 4 依赖以下技术,这些在 IE11 中需要特殊处理:
jQuery
:Bootstrap 4 的 JavaScript 组件依赖 jQuery 3.x
Popper.js
:用于工具提示、弹出框等组件的定位
Flexbox
:Bootstrap 4 的网格系统和许多组件使用 Flexbox
CSS 变量
:Bootstrap 4.2+ 开始使用 CSS 变量(但可以降级)
二、核心解决方案:Polyfill 与构建配置
2.1 必需的 Polyfill 列表
为了让 Bootstrap 4 在 IE11 中运行,我们需要引入以下 Polyfill:
2.1.1 core-js(ES6+ Polyfill)
core-js
提供了 ES6+ 特性的向下兼容实现,包括:
Promise
Array.prototype.includes
Object.assign
String.prototype.startsWith
等
2.1.2 regenerator-runtime(Generator 支持)
如果使用了 async/await 或 Generator 函数,需要此运行时。
2.1.3 whatwg-fetch(Fetch API)
提供 Fetch API 的兼容实现。
2.1.4 classlist-polyfill
IE11 虽然支持
classList
,但存在一些 bug,建议引入。
2.1.5 svg4everybody
解决 SVG 图标在 IE11 中的显示问题。
2.2 HTML 模板配置
以下是一个完整的 HTML 模板,包含所有必要的 Polyfill:
Bootstrap 4 IE11 兼容测试
这是一个测试卡片,用于验证 Bootstrap 4 在 IE11 中的显示效果。
2.3 构建工具配置(Webpack)
如果你使用 Webpack 构建项目,以下是详细的配置方案:
2.3.1 安装必要的依赖
npm install –save-dev @babel/core @babel/preset-env @babel/polyfill babel-loader
npm install –save-dev postcss postcss-loader postcss-preset-env autoprefixer
npm install –save-dev core-js@3 regenerator-runtime whatwg-fetch
2.3.2 Webpack 配置文件
// webpack.config.js
const path = require(‘path’);
const HtmlWebpackPlugin = require(‘html-webpack-plugin’);
module.exports = {
// 入口文件配置:为 IE11 提供完整的 Polyfill
entry: {
// 将 polyfill 放在最前面,确保在应用代码之前加载
polyfills: ‘./src/polyfills.js’,
// 应用入口
app: ‘./src/index.js’
},
// 输出配置
output: {
path: path.resolve(__dirname, ‘dist’),
filename: ‘[name].[contenthash].js’,
clean: true
},
// 模式:生产环境
mode: ‘production’,
// 模块规则
module: {
rules: [
// Babel 转换:将 ES6+ 代码转换为 ES5
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: ‘babel-loader’,
options: {
presets: [
[
‘@babel/preset-env’,
{
// 目标浏览器:IE11
targets: {
ie: ’11’
},
// 使用 core-js 3
useBuiltIns: ‘usage’,
corejs: 3,
// 确保所有必要的 polyfill
debug: true
}
]
],
plugins: [
// 如果需要,添加其他插件
‘@babel/plugin-transform-runtime’
]
}
}
},
// CSS 处理:添加浏览器前缀
{
test: /\.css$/,
use: [
‘style-loader’,
‘css-loader’,
{
loader: ‘postcss-loader’,
options: {
postcssOptions: {
plugins: [
// 自动添加 CSS 前缀
require(‘autoprefixer’)({
overrideBrowserslist: [‘ie >= 11’]
}),
// 更好的 IE11 支持
require(‘postcss-preset-env’)({
stage: 3,
browsers: [‘ie >= 11’]
})
]
}
}
}
]
}
]
},
// 插件配置
plugins: [
new HtmlWebpackPlugin({
template: ‘./src/index.html’,
// 在 HTML 中自动注入必要的脚本
scriptLoading: ‘defer’,
// 添加 IE 条件注释
meta: {
‘X-UA-Compatible’: { ‘http-equiv’: ‘X-UA-Compatible’, ‘content’: ‘IE=edge’ }
}
})
],
// 解析配置
resolve: {
// 确保使用正确的模块版本
alias: {
// 强制使用兼容版本的 jQuery
‘jquery’: require.resolve(‘jquery’)
}
}
};
2.3.3 Polyfills 文件
创建
src/polyfills.js
文件:
// src/polyfills.js
// 这个文件必须在所有其他代码之前加载
// 1. 核心 ES6+ Polyfill(使用 core-js)
// 会根据实际使用情况自动引入必要的 polyfill
import ‘core-js/stable’;
import ‘regenerator-runtime/runtime’;
// 2. Fetch API Polyfill
import ‘whatwg-fetch’;
// 3. 修复 IE11 的一些已知问题
// 3.1 修复 Object.assign 在 IE11 中的问题
if (typeof Object.assign !== ‘function’) {
Object.assign = function(target) {
‘use strict’;
if (target == null) {
throw new TypeError(‘Cannot convert undefined or null to object’);
}
target = Object(target);
for (var index = 1; index < arguments.length; index++) {
var source = arguments[index];
if (source != null) {
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
}
return target;
};
}
// 3.2 修复 Element.prototype.matches 在 IE11 中的问题
if (!Element.prototype.matches) {
Element.prototype.matches = Element.prototype.msMatchesSelector ||
Element.prototype.webkitMatchesSelector;
}
// 3.3 修复 Element.prototype.closest 在 IE11 中的问题
if (!Element.prototype.closest) {
Element.prototype.closest = function(s) {
var el = this;
do {
if (el.matches(s)) return el;
el = el.parentElement || el.parentNode;
} while (el !== null && el.nodeType === 1);
return null;
};
}
// 3.4 修复 NodeList.prototype.forEach 在 IE11 中的问题
if (window.NodeList && !NodeList.prototype.forEach) {
NodeList.prototype.forEach = Array.prototype.forEach;
}
// 3.5 修复 SVG 图标在 IE11 中的问题
// 如果使用 SVG 图标,需要 svg4everybody
if (typeof svg4everybody === 'function') {
svg4everybody();
}
// 4. 控制台保护(避免 IE11 无开发者工具时出错)
if (!window.console) {
window.console = {
log: function() {},
error: function() {},
warn: function() {},
info: function() {}
};
}
// 5. 性能优化:只在需要时加载
// 检测是否为 IE11
var isIE11 = !!window.MSInputMethodContext && !!document.documentMode;
if (isIE11) {
console.log('检测到 IE11,已加载所有必要的 Polyfill');
// 可以在这里添加额外的 IE11 特定修复
// 例如:修复 flexbox 布局问题
document.documentElement.className += ' is-ie11';
}
2.4 Babel 配置详解
2.4.1
.babelrc
配置文件
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"ie": "11"
},
"useBuiltIns": "usage",
"corejs": 3,
"modules": false,
"debug": false
}
]
],
"plugins": [
"@babel/plugin-transform-runtime",
// 如果需要支持装饰器等高级特性
// "@babel/plugin-proposal-decorators"
],
"env": {
"test": {
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
]
]
}
}
}
2.4.2
useBuiltIns: 'usage'
的工作原理
当设置为
'usage'
时,Babel 会:
分析你的代码
只引入实际使用的 ES6+ 特性的 polyfill
自动按需加载,减少 bundle 体积
示例代码分析:
// 源代码
const arr = [1, 2, 3];
const doubled = arr.map(x => x * 2);
const hasOne = arr.includes(1);
// Babel 转换后(简化版)
// 会自动引入 Array.prototype.includes 的 polyfill
// 但不会引入 Promise 等未使用的 polyfill
// 实际引入的 polyfill 可能包括:
// – Array.prototype.map (如果需要)
// – Array.prototype.includes
// – Symbol (因为 includes 依赖 Symbol)
三、CSS 兼容性处理
3.1 Flexbox 在 IE11 中的问题与修复
IE11 对 Flexbox 的支持存在多个已知问题:
3.1.1 IE11 Flexbox 常见问题
不支持
flex-wrap: wrap
与
min-height
结合
flex-basis
计算错误
不支持
flex: none
align-items: stretch
在某些情况下失效
3.1.2 修复方案
方案 1:使用 Autoprefixer 自动添加
-ms-
前缀
/* 源代码 */
.container {
display: flex;
flex-wrap: wrap;
align-items: center;
}
/* Autoprefixer 转换后 */
.container {
display: -ms-flexbox; /* IE10 */
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
-ms-flex-align: center; /* IE10 */
align-items: center;
}
方案 2:针对 IE11 的特定修复
/* 创建 IE11 专用样式 */
.is-ie11 .row {
/* 修复 flex 容器宽度问题 */
width: 100% !important;
margin-left: 0 !important;
margin-right: 0 !important;
}
.is-ie11 .card-deck {
/* 修复卡片组布局 */
display: block;
}
.is-ie11 .card-deck .card {
display: inline-block;
width: calc(33.333% – 30px);
margin: 15px;
vertical-align: top;
}
/* 修复下拉菜单箭头 */
.is-ie11 .dropdown-toggle::after {
/* IE11 可能无法正确显示伪元素箭头 */
border-top-color: #007bff;
}
3.2 CSS 变量降级处理
Bootstrap 4.2+ 开始使用 CSS 变量,但 IE11 不支持。解决方案:
3.2.1 使用 PostCSS 插件
// postcss.config.js
module.exports = {
plugins: [
// 将 CSS 变量转换为静态值
require(‘postcss-css-variables’)({
// 保留变量定义,但转换使用处
preserve: false,
// 为 IE11 提供降级值
variables: {
‘–primary’: ‘#007bff’,
‘–secondary’: ‘#6c757d’,
‘–success’: ‘#28a745’,
‘–danger’: ‘#dc3545’,
‘–warning’: ‘#ffc107’,
‘–info’: ‘#17a2b8’,
‘–light’: ‘#f8f9fa’,
‘–dark’: ‘#343a40’,
‘–white’: ‘#fff’,
‘–gray-100’: ‘#f8f9fa’,
‘–gray-200’: ‘#e9ecef’,
‘–gray-300’: ‘#dee2e6’,
‘–gray-400’: ‘#ced4da’,
‘–gray-500’: ‘#adb5bd’,
‘–gray-600’: ‘#6c757d’,
‘–gray-700’: ‘#495057’,
‘–gray-800’: ‘#343a40’,
‘–gray-900’: ‘#212529’,
‘–border-width’: ‘1px’,
‘–border-color’: ‘#dee2e6’,
‘–border-radius’: ‘0.25rem’,
‘–border-radius-lg’: ‘0.3rem’,
‘–border-radius-sm’: ‘0.2rem’,
‘–font-family-sans-serif’: ‘-apple-system, BlinkMacSystemFont, “Segoe UI”, Roboto, “Helvetica Neue”, Arial, sans-serif’,
‘–font-family-monospace’: ‘SFMono-Regular, Menlo, Monaco, Consolas, “Liberation Mono”, “Courier New”, monospace’,
‘–font-size-base’: ‘1rem’,
‘–line-height-base’: ‘1.5’,
‘–headings-font-weight’: ‘500’,
‘–headings-line-height’: ‘1.2’,
‘–headings-color’: ‘#212529’,
‘–display1-size’: ‘6rem’,
‘–display2-size’: ‘5.5rem’,
‘–display3-size’: ‘4.5rem’,
‘–display4-size’: ‘3.5rem’,
‘–link-color’: ‘#007bff’,
‘–link-decoration’: ‘none’,
‘–link-hover-color’: ‘#0056b3’,
‘–link-hover-decoration’: ‘underline’,
‘–enable-shadows’: false,
‘–shadow-sm’: ‘0 0.125rem 0.25rem rgba(0,0,0,0.075)’,
‘–shadow’: ‘0 0.5rem 1rem rgba(0,0,0,0.15)’,
‘–shadow-lg’: ‘0 1rem 3rem rgba(0,0,0,0.175)’,
‘–component-offset-y’: ‘0.125rem’,
‘–component-offset-x’: ‘0.25rem’,
‘–component-bg’: ‘#fff’,
‘–component-border-color’: ‘rgba(0,0,0,0.1)’,
‘–component-box-shadow’: ‘0 0.5rem 1rem rgba(0,0,0,0.15)’,
‘–component-border-radius’: ‘0.25rem’,
‘–btn-font-weight’: ‘400’,
‘–btn-border-radius’: ‘0.25rem’,
‘–btn-padding-y’: ‘0.375rem’,
‘–btn-padding-x’: ‘0.75rem’,
‘–btn-line-height’: ‘1.5’,
‘–btn-transition’: ‘all 0.15s ease-in-out’,
‘–btn-focus-box-shadow’: ‘0 0 0 0.2rem rgba(0,123,255,0.25)’,
‘–btn-primary-color’: ‘#fff’,
‘–btn-primary-bg’: ‘#007bff’,
‘–btn-primary-border’: ‘#007bff’,
‘–btn-secondary-color’: ‘#fff’,
‘–btn-secondary-bg’: ‘#6c757d’,
‘–btn-secondary-border’: ‘#6c757d’,
‘–btn-success-color’: ‘#fff’,
‘–btn-success-bg’: ‘#28a745’,
‘–btn-success-border’: ‘#28a745’,
‘–btn-info-color’: ‘#fff’,
‘–btn-info-bg’: ‘#17a2b8’,
‘–btn-info-border’: ‘#17a2b8’,
‘–btn-warning-color’: ‘#212529’,
‘–btn-warning-bg’: ‘#ffc107’,
‘–btn-warning-border’: ‘#ffc107’,
‘–btn-danger-color’: ‘#fff’,
‘–btn-danger-bg’: ‘#dc3545’,
‘–btn-danger-border’: ‘#dc3545’,
‘–btn-light-color’: ‘#212529’,
‘–btn-light-bg’: ‘#f8f9fa’,
‘–btn-light-border’: ‘#f8f9fa’,
‘–btn-dark-color’: ‘#fff’,
‘–btn-dark-bg’: ‘#343a40’,
‘–btn-dark-border’: ‘#343a40’,
‘–btn-outline-color’: ‘#007bff’,
‘–btn-outline-bg’: ‘transparent’,
‘–btn-outline-border’: ‘#007bff’,
‘–input-bg’: ‘#fff’,
‘–input-border-color’: ‘#ced4da’,
‘–input-border-width’: ‘1px’,
‘–input-focus-border-color’: ‘#80bdff’,
‘–input-focus-box-shadow’: ‘0 0 0 0.2rem rgba(0,123,255,0.25)’,
‘–input-border-radius’: ‘0.25rem’,
‘–input-padding-y’: ‘0.375rem’,
‘–input-padding-x’: ‘0.75rem’,
‘–input-line-height’: ‘1.5’,
‘–input-placeholder-color’: ‘#6c757d’,
‘–input-disabled-bg’: ‘#e9ecef’,
‘–input-disabled-color’: ‘#6c757d’,
‘–form-text-margin-top’: ‘0.25rem’,
‘–form-check-input-gutter’: ‘1.25rem’,
‘–form-check-input-margin-y’: ‘0.3125rem’,
‘–form-check-input-margin-x’: ‘0.25rem’,
‘–form-check-inline-margin-right’: ‘0.75rem’,
‘–input-group-addon-bg’: ‘#e9ecef’,
‘–input-group-addon-border-color’: ‘#ced4da’,
‘–card-border-width’: ‘1px’,
‘–card-border-color’: ‘rgba(0,0,0,0.125)’,
‘–card-border-radius’: ‘0.25rem’,
‘–card-inner-border-radius’: ‘calc(0.25rem – 1px)’,
‘–card-cap-bg’: ‘rgba(0,0,0,0.03)’,
‘–card-cap-color’: ”,
‘–card-spacer-y’: ‘1.25rem’,
‘–card-spacer-x’: ‘1.25rem’,
‘–card-title-spacer-y’: ‘0.75rem’,
‘–card-border-radius-lg’: ‘0.3rem’,
‘–card-border-radius-sm’: ‘0.2rem’,
‘–card-columns-gap’: ‘1.25rem’,
‘–card-columns-count’: ‘3’,
‘–card-group-margin’: ‘0.75rem’,
‘–nav-link-padding-y’: ‘0.5rem’,
‘–nav-link-padding-x’: ‘1rem’,
‘–nav-link-disabled-color’: ‘#6c757d’,
‘–navbar-padding-y’: ‘0.5rem’,
‘–navbar-padding-x’: ‘1rem’,
‘–navbar-nav-link-padding-x’: ‘0.5rem’,
‘–navbar-brand-font-size’: ‘1.25rem’,
‘–navbar-brand-margin-end’: ‘1rem’,
‘–navbar-toggler-padding-y’: ‘0.25rem’,
‘–navbar-toggler-padding-x’: ‘0.75rem’,
‘–navbar-toggler-border-radius’: ‘0.25rem’,
‘–dropdown-min-width’: ’10rem’,
‘–dropdown-padding-y’: ‘0.5rem’,
‘–dropdown-border-width’: ‘1px’,
‘–dropdown-border-color’: ‘rgba(0,0,0,0.15)’,
‘–dropdown-border-radius’: ‘0.25rem’,
‘–dropdown-box-shadow’: ‘0 0.5rem 1rem rgba(0,0,0,0.175)’,
‘–dropdown-link-color’: ‘#212529’,
‘–dropdown-link-hover-color’: ‘#16181b’,
‘–dropdown-link-hover-bg’: ‘#f8f9fa’,
‘–dropdown-link-active-color’: ‘#fff’,
‘–dropdown-link-active-bg’: ‘#007bff’,
‘–dropdown-link-disabled-color’: ‘#6c757d’,
‘–dropdown-header-color’: ‘#6c757d’,
‘–dropdown-item-padding-y’: ‘0.25rem’,
‘–dropdown-item-padding-x’: ‘1.5rem’,
‘–dropdown-divider-bg’: ‘#e9ecef’,
‘–pagination-padding-y’: ‘0.5rem’,
‘–pagination-padding-x’: ‘0.75rem’,
‘–pagination-border-width’: ‘1px’,
‘–pagination-border-color’: ‘#dee2e6’,
‘–pagination-border-radius’: ‘0.25rem’,
‘–pagination-hover-color’: ‘#0056b3’,
‘–pagination-hover-bg’: ‘#e9ecef’,
‘–pagination-hover-border-color’: ‘#dee2e6’,
‘–pagination-focus-box-shadow’: ‘0 0 0 0.2rem rgba(0,123,255,0.25)’,
‘–pagination-active-bg’: ‘#007bff’,
‘–pagination-active-border-color’: ‘#007bff’,
‘–pagination-disabled-color’: ‘#6c757d’,
‘–pagination-disabled-bg’: ‘#fff’,
‘–pagination-disabled-border-color’: ‘#dee2e6’,
‘–badge-padding-y’: ‘0.25em’,
‘–badge-padding-x’: ‘0.4em’,
‘–badge-font-size’: ‘75%’,
‘–badge-font-weight’: ‘700’,
‘–badge-border-radius’: ‘0.25rem’,
‘–alert-padding-y’: ‘0.75rem’,
‘–alert-padding-x’: ‘1.25rem’,
‘–alert-margin-bottom’: ‘1rem’,
‘–alert-border-radius’: ‘0.25rem’,
‘–alert-border-width’: ‘1px’,
‘–alert-bg-level’: -10,
‘–alert-border-level’: -9,
‘–alert-color-level’: 6,
‘–progress-height’: ‘1rem’,
‘–progress-font-size’: ‘0.75rem’,
‘–progress-border-radius’: ‘0.25rem’,
‘–progress-bg’: ‘#e9ecef’,
‘–progress-bar-color’: ‘#fff’,
‘–progress-bar-transition’: ‘width 0.6s ease’,
‘–list-group-border-width’: ‘1px’,
‘–list-group-border-color’: ‘rgba(0,0,0,0.125)’,
‘–list-group-border-radius’: ‘0.25rem’,
‘–list-group-item-padding-y’: ‘0.75rem’,
‘–list-group-item-padding-x’: ‘1.25rem’,
‘–list-group-hover-bg’: ‘#f8f9fa’,
‘–list-group-active-color’: ‘#fff’,
‘–list-group-active-bg’: ‘#007bff’,
‘–list-group-active-border-color’: ‘#007bff’,
‘–list-group-disabled-color’: ‘#6c757d’,
‘–list-group-disabled-bg’: ‘#e9ecef’,
‘–modal-inner-padding’: ‘1rem’,
‘–modal-footer-padding’: ‘1rem’,
‘–modal-header-padding’: ‘1rem’,
‘–modal-title-line-height’: ‘1.5’,
‘–modal-content-border-width’: ‘1px’,
‘–modal-content-border-color’: ‘rgba(0,0,0,0.2)’,
‘–modal-content-box-shadow-xs’: ‘0 0.25rem 0.5rem rgba(0,0,0,0.5)’,
‘–modal-content-box-shadow-sm-up’: ‘0 0.5rem 1rem rgba(0,0,0,0.5)’,
‘–modal-backdrop-bg’: ‘#000’,
‘–modal-backdrop-opacity’: ‘0.5’,
‘–modal-header-border-color’: ‘#e9ecef’,
‘–modal-footer-border-color’: ‘#e9ecef’,
‘–tooltip-padding-y’: ‘0.25rem’,
‘–tooltip-padding-x’: ‘0.5rem’,
‘–tooltip-font-size’: ‘0.875rem’,
‘–tooltip-border-radius’: ‘0.25rem’,
‘–tooltip-bg’: ‘#000’,
‘–tooltip-opacity’: ‘0.9’,
‘–tooltip-border-radius’: ‘0.25rem’,
‘–toast-max-width’: ‘350px’,
‘–toast-padding-x’: ‘0.75rem’,
‘–toast-padding-y’: ‘0.5rem’,
‘–toast-font-size’: ‘0.875rem’,
‘–toast-border-width’: ‘1px’,
‘–toast-border-color’: ‘rgba(0,0,0,0.1)’,
‘–toast-border-radius’: ‘0.25rem’,
‘–toast-box-shadow’: ‘0 0.5rem 1rem rgba(0,0,0,0.15)’,
‘–toast-header-bg’: ‘rgba(0,0,0,0.05)’,
‘–toast-header-color’: ‘#6c757d’,
‘–toast-header-border-color’: ‘rgba(0,0,0,0.05)’,
‘–placeholder-padding-y’: ‘0.5rem’,
‘–placeholder-font-weight’: ‘500’,
‘–placeholder-border-color’: ‘#ced4da’,
‘–placeholder-opacity’: ‘0.65’,
‘–popover-max-width’: ‘276px’,
‘–popover-border-width’: ‘1px’,
‘–popover-border-color’: ‘rgba(0,0,0,0.2)’,
‘–popover-border-radius’: ‘0.25rem’,
‘–popover-box-shadow’: ‘0 0.5rem 1rem rgba(0,0,0,0.2)’,
‘–popover-header-bg’: ‘#f7f7f7’,
‘–popover-header-padding-y’: ‘0.5rem’,
‘–popover-header-padding-x’: ‘0.75rem’,
‘–popover-body-padding-y’: ‘0.5rem’,
‘–popover-body-padding-x’: ‘0.75rem’,
‘–popover-arrow-width’: ‘0.8rem’,
‘–popover-arrow-height’: ‘0.4rem’,
‘–popover-arrow-color’: ‘#fff’,
‘–carousel-control-width’: ‘15%’,
‘–carousel-control-opacity’: ‘0.5’,
‘–carousel-control-hover-opacity’: ‘0.9’,
‘–carousel-indicator-width’: ’30px’,
‘–carousel-indicator-height’: ‘3px’,
‘–carousel-indicator-spacer’: ‘3px’,
‘–carousel-indicator-active-bg’: ‘#fff’,
‘–carousel-caption-width’: ‘70%’,
‘–carousel-caption-color’: ‘#fff’,
‘–carousel-control-icon-width’: ’20px’,
‘–carousel-control-prev-icon-bg’: ‘url(“data:image/svg+xml,%3csvg xmlns=\’http://www.w3.org/2000/svg\’ fill=\’%23fff\’ viewBox=\’0 0 8 8\’%3e%3cpath d=\’M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z\’/%3e%3c/svg%3e”)’,
‘–carousel-control-next-icon-bg’: ‘url(“data:image/svg+xml,%3csvg xmlns=\’http://www.w3.org/2000/svg\’ fill=\’%23fff\’ viewBox=\’0 0 8 8\’%3e%3cpath d=\’M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z\’/%3e%3c/svg%3e”)’,
‘–spin-width’: ‘2rem’,
‘–spin-height’: ‘2rem’,
‘–spin-border-width’: ‘0.25em’,
‘–spin-border-color’: ‘#fff’,
‘–spinner-width’: ‘2rem’,
‘–spinner-height’: ‘2rem’,
‘–spinner-border-width’: ‘0.25em’,
‘–spinner-border-color’: ‘currentColor’,
‘–spinner-width-sm’: ‘1rem’,
‘–spinner-height-sm’: ‘1rem’,
‘–spinner-border-width-sm’: ‘0.2em’,
‘–offcanvas-padding-y’: ‘1rem’,
‘–offcanvas-padding-x’: ‘1rem’,
‘–offcanvas-transition-duration’: ‘0.3s’,
‘–offcanvas-border-color’: ‘#dee2e6’,
‘–offcanvas-border-width’: ‘1px’,
‘–offcanvas-bg-color’: ‘#f8f9fa’,
‘–offcanvas-box-shadow’: ‘0 0.5rem 1rem rgba(0,0,0,0.15)’,
‘–offcanvas-zindex’: ‘1040’,
‘–offcanvas-padding-y’: ‘1rem’,
‘–offcanvas-padding-x’: ‘1rem’,
‘–offcanvas-transition-duration’: ‘0.3s’,
‘–offcanvas-border-color’: ‘#dee2e6’,
‘–offcanvas-border-width’: ‘1px’,
‘–offcanvas-bg-color’: ‘#f8f9fa’,
‘–offcanvas-box-shadow’: ‘0 0.5rem 1rem rgba(0,0,0,0.15)’,
‘–offcanvas-zindex’: ‘1040’
}
}),
// 自动添加浏览器前缀
require(‘autoprefixer’)({
overrideBrowserslist: [‘ie >= 11’]
})
]
};
3.2.2 手动降级方案
如果你不使用构建工具,可以手动创建降级 CSS:
/* bootstrap-ie11-fallback.css */
/* 这个文件应该在 Bootstrap 之后加载 */
/* 为所有使用 CSS 变量的元素提供静态值 */
/* 这是一个简化的示例,实际需要覆盖所有 Bootstrap 组件 */
/* 按钮 */
.btn-primary {
background-color: #007bff;
border-color: #007bff;
color: #fff;
}
.btn-primary:hover {
background-color: #0069d9;
border-color: #0062cc;
}
.btn-secondary {
background-color: #6c757d;
border-color: #6c757d;
color: #fff;
}
/* 表单控件 */
.form-control {
border: 1px solid #ced4da;
border-radius: 0.25rem;
padding: 0.375rem 0.75rem;
line-height: 1.5;
color: #495057;
background-color: #fff;
}
.form-control:focus {
border-color: #80bdff;
outline: 0;
box-shadow: 0 0 0 0.2rem rgba(0,123,255,0.25);
}
/* 卡片 */
.card {
border: 1px solid rgba(0,0,0,0.125);
border-radius: 0.25rem;
background-color: #fff;
}
.card-header {
padding: 0.75rem 1.25rem;
background-color: rgba(0,0,0,0.03);
border-bottom: 1px solid rgba(0,0,0,0.125);
}
/* 下拉菜单 */
.dropdown-menu {
border: 1px solid rgba(0,0,0,0.15);
border-radius: 0.25rem;
background-color: #fff;
box-shadow: 0 0.5rem 1rem rgba(0,0,0,0.175);
}
.dropdown-item {
display: block;
width: 100%;
padding: 0.25rem 1.5rem;
clear: both;
font-weight: 400;
color: #212529;
text-align: inherit;
white-space: nowrap;
background-color: transparent;
border: 0;
}
.dropdown-item:hover {
color: #16181b;
background-color: #f8f9fa;
}
/* 工具提示 */
.tooltip {
position: absolute;
z-index: 1070;
display: block;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, “Segoe UI”, Roboto, “Helvetica Neue”, Arial, sans-serif;
font-style: normal;
font-weight: 400;
line-height: 1.5;
text-align: left;
text-decoration: none;
text-shadow: none;
text-transform: none;
letter-spacing: normal;
word-break: normal;
word-spacing: normal;
white-space: normal;
line-break: auto;
font-size: 0.875rem;
word-wrap: break-word;
opacity: 0;
}
.tooltip.show {
opacity: 0.9;
}
.tooltip-inner {
max-width: 200px;
padding: 0.25rem 0.5rem;
color: #fff;
text-align: center;
background-color: #000;
border-radius: 0.25rem;
}
/* 网格系统修复 */
.is-ie11 .container {
width: 100%;
padding-right: 15px;
padding-left: 15px;
margin-right: auto;
margin-left: auto;
}
.is-ie11 .row {
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin-right: -15px;
margin-left: -15px;
}
.is-ie11 [class*=”col-“] {
position: relative;
width: 100%;
padding-right: 15px;
padding-left: 15px;
}
/* 为 IE11 特定的列宽 */
.is-ie11 .col-md-6 {
-ms-flex: 0 0 50%;
flex: 0 0 50%;
max-width: 50%;
}
.is-ie11 .col-12 {
-ms-flex: 0 0 100%;
flex: 0 0 100%;
max-width: 100%;
}
3.3 CSS Grid 降级
Bootstrap 4 主要使用 Flexbox,但某些组件可能使用 Grid。IE11 不支持 Grid,需要降级:
/* 如果使用了 Grid 布局 */
.is-ie11 .grid-container {
display: block; /* 降级为块级布局 */
}
.is-ie11 .grid-item {
width: 100%;
margin-bottom: 1rem;
}
/* 或者使用 Flexbox 作为降级 */
.is-ie11 .grid-container {
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
margin: -10px;
}
.is-ie11 .grid-item {
-ms-flex: 0 0 33.333%;
flex: 0 0 33.333%;
max-width: 33.333%;
padding: 10px;
}
四、JavaScript 兼容性处理
4.1 jQuery 版本选择
Bootstrap 4 需要 jQuery 3.x,但 IE11 对 jQuery 3.x 的支持有细微差异:
4.1.1 推荐版本
4.1.2 jQuery 配置优化
// 在 jQuery 加载后立即配置
$(document).ready(function() {
// 1. 禁用 jQuery 的某些特性以提高 IE11 性能
$.ajaxPrefilter(function(options) {
// IE11 对某些 AJAX 请求有缓存问题
options.cache = false;
});
// 2. 修复 IE11 的事件处理问题
// IE11 对某些事件(如 change, input)的冒泡行为不同
$(document).on(‘change input’, ‘input, select, textarea’, function(e) {
// 确保事件目标正确
e.target = e.target || e.srcElement;
});
// 3. 修复 IE11 的动画问题
// IE11 对某些 CSS 动画支持不佳,可以禁用 jQuery 动画
if (navigator.userAgent.indexOf(‘MSIE 11’) > -1) {
$.fx.off = true; // 禁用 jQuery 动画
}
});
4.2 Bootstrap JavaScript 组件的 IE11 问题
4.2.1 工具提示(Tooltip)和弹出框(Popover)
IE11 对 SVG 支持不佳,可能影响箭头显示:
// 初始化工具提示时添加修复
$(function() {
// 检测 IE11
var isIE11 = !!window.MSInputMethodContext && !!document.documentMode;
if (isIE11) {
// 为所有工具提示添加特殊类
$(‘[data-toggle=”tooltip”]’).each(function() {
$(this).on(‘show.bs.tooltip’, function() {
// 强制重新计算位置
setTimeout(function() {
$(‘.tooltip’).each(function() {
var $tooltip = $(this);
// 修复 IE11 中的定位问题
var top = $tooltip.css(‘top’);
var left = $tooltip.css(‘left’);
// 确保数值是整数,避免 sub-pixel 渲染问题
$tooltip.css({
‘top’: Math.round(parseFloat(top)) + ‘px’,
‘left’: Math.round(parseFloat(left)) + ‘px’
});
});
}, 10);
});
});
}
// 正常初始化
$(‘[data-toggle=”tooltip”]’).tooltip();
});
4.2.2 模态框(Modal)
IE11 对模态框的滚动锁定和 backdrop 有特殊处理:
// 修复 IE11 模态框问题
$(document).ready(function() {
var isIE11 = !!window.MSInputMethodContext && !!document.documentMode;
if (isIE11) {
// 修复模态框打开时 body 滚动问题
$(‘#myModal’).on(‘show.bs.modal’, function() {
// IE11 需要手动锁定滚动
var $body = $(‘body’);
var scrollWidth = window.innerWidth – document.documentElement.clientWidth;
$body.css({
‘overflow’: ‘hidden’,
‘padding-right’: scrollWidth + ‘px’
});
});
$(‘#myModal’).on(‘hidden.bs.modal’, function() {
$(‘body’).css({
‘overflow’: ”,
‘padding-right’: ”
});
});
// 修复模态框垂直居中问题
$(‘#myModal’).on(‘shown.bs.modal’, function() {
var $modal = $(this).find(‘.modal-dialog’);
var windowHeight = $(window).height();
var modalHeight = $modal.outerHeight();
if (modalHeight > windowHeight) {
// 如果模态框比窗口高,调整内边距
$modal.css(‘margin-top’, ’20px’);
$modal.css(‘margin-bottom’, ’20px’);
} else {
// 正常居中
$modal.css(‘margin-top’, ”);
$modal.css(‘margin-bottom’, ”);
}
});
}
});
4.2.3 下拉菜单(Dropdown)
IE11 对下拉菜单的定位和事件处理有细微差异:
// 修复 IE11 下拉菜单问题
$(document).ready(function() {
var isIE11 = !!window.MSInputMethodContext && !!document.documentMode;
if (isIE11) {
// 修复下拉菜单点击问题
// IE11 对某些元素的点击事件处理不同
$(document).on(‘click’, ‘.dropdown-toggle’, function(e) {
e.preventDefault();
e.stopPropagation();
var $dropdown = $(this).parent();
var isOpen = $dropdown.hasClass(‘show’);
// 关闭其他下拉菜单
$(‘.dropdown’).removeClass(‘show’);
$(‘.dropdown-menu’).removeClass(‘show’);
// 切换当前下拉菜单
if (!isOpen) {
$dropdown.addClass(‘show’);
$dropdown.find(‘.dropdown-menu’).addClass(‘show’);
}
});
// 点击其他地方关闭下拉菜单
$(document).on(‘click’, function(e) {
if (!$(e.target).closest(‘.dropdown’).length) {
$(‘.dropdown’).removeClass(‘show’);
$(‘.dropdown-menu’).removeClass(‘show’);
}
});
}
});
4.3 现代 JavaScript 特性的 Polyfill
4.3.1 Fetch API
// 在 polyfills.js 中已经引入了 whatwg-fetch
// 但需要确保正确使用
// 使用示例
function fetchData(url) {
// 检查是否支持 Fetch
if (window.fetch) {
return fetch(url)
.then(response => {
if (!response.ok) {
throw new Error(‘Network response was not ok’);
}
return response.json();
});
} else {
// 降级到 jQuery AJAX
return $.ajax({
url: url,
method: ‘GET’,
dataType: ‘json’
}).then(data => data);
}
}
// 使用
fetchData(‘/api/data’)
.then(data => {
console.log(‘数据:’, data);
})
.catch(error => {
console.error(‘错误:’, error);
});
4.3.2 Promise
// 在 polyfills.js 中已经引入了 core-js 的 Promise
// 但需要确保代码中正确使用
// 使用示例
function checkIE11Compatibility() {
return new Promise((resolve, reject) => {
var isIE11 = !!window.MSInputMethodContext && !!document.documentMode;
if (isIE11) {
// 检查必要的 API
var checks = {
fetch: typeof window.fetch !== ‘undefined’,
promise: typeof Promise !== ‘undefined’,
classList: typeof document.documentElement.classList !== ‘undefined’,
svg: typeof SVGRect !== ‘undefined’
};
// 模拟异步检查
setTimeout(() => {
var allSupported = Object.values(checks).every(check => check);
if (allSupported) {
resolve({
isIE11: true,
status: ‘compatible’,
details: checks
});
} else {
reject({
isIE11: true,
status: ‘incompatible’,
details: checks
});
}
}, 100);
} else {
resolve({
isIE11: false,
status: ‘modern-browser’
});
}
});
}
// 使用
checkIE11Compatibility()
.then(result => {
console.log(‘兼容性检查结果:’, result);
if (result.isIE11 && result.status === ‘compatible’) {
console.log(‘IE11 环境已准备就绪’);
}
})
.catch(error => {
console.error(‘兼容性问题:’, error);
// 可以在这里显示用户提示
if (error.isIE11) {
alert(‘您的浏览器版本较旧,某些功能可能无法正常使用。建议升级到现代浏览器。’);
}
});
4.3.3 Array.includes 和其他 ES6 方法
// 在 polyfills.js 中已经通过 core-js 自动处理
// 但需要确保代码中正确使用
// 使用示例
function handleUserActions(actions) {
// 使用 includes 而不是 indexOf
if (actions.includes(‘delete’)) {
console.log(‘删除操作’);
}
// 使用 find 方法
var user = users.find(u => u.id === 123);
// 使用 Object.assign
var config = Object.assign({}, defaultConfig, userConfig);
// 使用箭头函数(Babel 会转换为 ES5)
var doubled = numbers.map(x => x * 2);
return { user, config, doubled };
}
4.4 事件处理兼容性
IE11 使用
attachEvent
而不是
addEventListener
,但 jQuery 已经处理了这个问题。如果使用原生 JavaScript:
// 兼容的事件绑定函数
function addEvent(element, event, handler) {
if (element.addEventListener) {
element.addEventListener(event, handler, false);
} else if (element.attachEvent) {
element.attachEvent(‘on’ + event, handler);
} else {
element[‘on’ + event] = handler;
}
}
// 使用示例
var button = document.getElementById(‘testBtn’);
addEvent(button, ‘click’, function(e) {
// 修复 IE11 的事件对象
e = e || window.event;
var target = e.target || e.srcElement;
console.log(‘点击目标:’, target);
alert(‘按钮被点击了!’);
});
// 修复 IE11 的 input 事件
var input = document.getElementById(’email’);
addEvent(input, ‘input’, function(e) {
// IE11 可能不支持 input 事件,需要 fallback 到 keyup, change, paste
var value = e.target.value || e.srcElement.value;
console.log(‘输入值:’, value);
});
// 修复 IE11 的 preventDefault
function fixEvent(e) {
e = e || window.event;
if (e.preventDefault) {
e.preventDefault();
} else {
e.returnValue = false; // IE11
}
if (e.stopPropagation) {
e.stopPropagation();
} else {
e.cancelBubble = true; // IE11
}
return e;
}
五、构建与部署最佳实践
5.1 完整的构建流程
5.1.1 项目结构
project/
├── src/
│ ├── index.html
│ ├── index.js
│ ├── polyfills.js
│ ├── styles/
│ │ ├── main.css
│ │ └── ie11-fixes.css
│ └── components/
│ ├── header.js
│ └── footer.js
├── dist/
│ ├── index.html
│ ├── app.[hash].js
│ ├── polyfills.[hash].js
│ └── styles.[hash].css
├── webpack.config.js
├── postcss.config.js
├── .babelrc
└── package.json
5.1.2 package.json 脚本
{
“scripts”: {
“build”: “webpack –mode production”,
“build:ie11”: “webpack –mode production –env ie11”,
“dev”: “webpack serve –mode development”,
“serve”: “http-server dist -p 8080″
}
}
5.1.3 Webpack 生产配置
// webpack.prod.js
const { merge } = require(‘webpack-merge’);
const common = require(‘./webpack.common.js’);
const MiniCssExtractPlugin = require(‘mini-css-extract-plugin’);
const CssMinimizerPlugin = require(‘css-minimizer-webpack-plugin’);
module.exports = merge(common, {
mode: ‘production’,
// 为 IE11 优化输出
output: {
filename: ‘[name].[contenthash].js’,
chunkFilename: ‘[name].[contenthash].chunk.js’,
// 确保代码兼容 IE11
environment: {
arrowFunction: false,
bigIntLiteral: false,
const: false,
destructuring: false,
dynamicImport: false,
forOf: false,
module: false
}
},
// 优化配置
optimization: {
minimizer: [
new CssMinimizerPlugin({
minimizerOptions: {
preset: [
‘default’,
{
discardComments: { removeAll: true },
// 确保 CSS 优化不破坏 IE11 兼容
mergeLonghand: false,
calc: false
}
]
}
})
],
splitChunks: {
cacheGroups: {
// 提取第三方库(jQuery, Bootstrap)
vendor: {
test: /[\\/]node_modules[\\/]/,
name: ‘vendors’,
chunks: ‘all’,
priority: 10
},
// 提取 Polyfill
polyfills: {
test: /[\\/]polyfills[\\/]/,
name: ‘polyfills’,
chunks: ‘all’,
priority: 20
}
}
}
},
// 插件
plugins: [
new MiniCssExtractPlugin({
filename: ‘[name].[contenthash].css’,
chunkFilename: ‘[name].[contenthash].chunk.css’
})
],
// 模块规则
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
‘css-loader’,
{
loader: ‘postcss-loader’,
options: {
postcssOptions: {
plugins: [
require(‘autoprefixer’)({
overrideBrowserslist: [‘ie >= 11’]
}),
require(‘postcss-preset-env’)({
stage: 3,
browsers: [‘ie >= 11’]
}),
// 压缩 CSS
require(‘cssnano’)({
preset: [‘default’, {
discardComments: { removeAll: true },
// 保留 IE11 需要的语法
safe: true
}]
})
]
}
}
}
]
}
]
}
});
5.2 性能优化
5.2.1 按需加载 Polyfill
// polyfills.js – 按需加载优化版
// 检测 IE11
var isIE11 = !!window.MSInputMethodContext && !!document.documentMode;
if (isIE11) {
console.log(‘IE11 detected, loading polyfills…’);
// 动态加载 polyfill,减少初始 bundle 体积
var polyfills = [];
// 检查需要哪些 polyfill
if (!window.fetch) {
polyfills.push(‘https://cdn.jsdelivr.net/npm/whatwg-fetch@3.6.2/dist/fetch.umd.js’);
}
if (!window.Promise) {
polyfills.push(‘https://cdn.jsdelivr.net/npm/core-js@3.30.2/minified.js’);
}
if (!Object.assign) {
// 如果 core-js 没有加载,加载它
if (!polyfills.includes(‘https://cdn.jsdelivr.net/npm/core-js@3.30.2/minified.js’)) {
polyfills.push(‘https://cdn.jsdelivr.net/npm/core-js@3.30.2/minified.js’);
}
}
// 动态加载脚本
function loadScript(src) {
return new Promise((resolve, reject) => {
var script = document.createElement(‘script’);
script.src = src;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
// 加载所有必要的 polyfill
Promise.all(polyfills.map(loadScript))
.then(() => {
console.log(‘All polyfills loaded’);
// 触发自定义事件,通知应用可以初始化了
window.dispatchEvent(new Event(‘polyfills-ready’));
})
.catch((error) => {
console.error(‘Failed to load polyfills:’, error);
});
} else {
// 现代浏览器,直接触发就绪事件
window.dispatchEvent(new Event(‘polyfills-ready’));
}
// 应用监听
window.addEventListener(‘polyfills-ready’, function() {
console.log(‘初始化应用…’);
// 这里初始化你的应用
if (typeof app !== ‘undefined’) {
app.init();
}
});
5.2.2 缓存策略
// 在 webpack 配置中添加缓存
// webpack.config.js
module.exports = {
// … 其他配置
cache: {
type: ‘filesystem’,
buildDependencies: {
// 随配置文件变化而失效缓存
config: [__filename]
}
},
// 输出文件名使用 hash,便于缓存
output: {
filename: ‘[name].[contenthash].js’,
chunkFilename: ‘[name].[contenthash].chunk.js’
}
};
5.3 测试与验证
5.3.1 自动化测试
// test/ie11-compatibility.test.js
const puppeteer = require(‘puppeteer’);
const { expect } = require(‘chai’);
describe(‘IE11 Compatibility’, function() {
this.timeout(10000);
let browser;
let page;
before(async function() {
// 注意:Puppeteer 默认不支持 IE11
// 这里使用模拟检测
browser = await puppeteer.launch();
page = await browser.newPage();
// 模拟 IE11 User Agent
await page.setUserAgent(‘Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko’);
// 注入 IE11 特征检测
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, ‘userAgent’, {
value: ‘Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko’,
configurable: true,
writable: true
});
window.MSInputMethodContext = true;
document.documentMode = 11;
});
});
after(async function() {
await browser.close();
});
it(‘should load Bootstrap components correctly’, async function() {
await page.goto(‘http://localhost:8080’);
// 检查工具提示
const tooltipButton = await page.$(‘[data-toggle=”tooltip”]’);
await tooltipButton.hover();
await page.waitForTimeout(500);
const tooltipVisible = await page.$(‘.tooltip.show’);
expect(tooltipVisible).to.not.be.null;
});
it(‘should handle dropdowns correctly’, async function() {
await page.goto(‘http://localhost:8080’);
const dropdownToggle = await page.$(‘.dropdown-toggle’);
await dropdownToggle.click();
await page.waitForTimeout(300);
const dropdownMenu = await page.$(‘.dropdown-menu.show’);
expect(dropdownMenu).to.not.be.null;
});
it(‘should support Fetch API’, async function() {
await page.goto(‘http://localhost:8080’);
const hasFetch = await page.evaluate(() => {
return typeof window.fetch === ‘function’;
});
expect(hasFetch).to.be.true;
});
});
5.3.2 手动测试清单
在真实 IE11 环境中测试以下内容:
[ ] 页面布局是否正确(网格、卡片、表单)
[ ] 所有按钮是否可点击
[ ] 下拉菜单是否正常打开/关闭
[ ] 工具提示和弹出框是否显示
[ ] 模态框是否正常工作
[ ] 表单验证是否正常
[ ] AJAX 请求是否成功
[ ] SVG 图标是否显示
[ ] 动画是否流畅(或已禁用)
[ ] 控制台是否有错误
六、常见问题与解决方案
6.1 常见错误及修复
6.1.1 “Object doesn’t support property or method ‘includes’”
原因
:IE11 不支持
Array.prototype.includes
解决方案
:
// 确保 core-js 已加载
import ‘core-js/stable’;
// 或者手动 polyfill
if (!Array.prototype.includes) {
Array.prototype.includes = function(searchElement) {
return this.indexOf(searchElement) !== -1;
};
}
6.1.2 “Promise is undefined”
原因
:缺少 Promise Polyfill
解决方案
:
// 在 polyfills.js 中
import ‘core-js/stable/es/promise’;
// 或者
if (typeof Promise === ‘undefined’) {
// 使用 jQuery 的 Deferred 作为替代
window.Promise = function(executor) {
var deferred = $.Deferred();
try {
executor(deferred.resolve, deferred.reject);
} catch (e) {
deferred.reject(e);
}
return deferred.promise();
};
}
6.1.3 “Flexbox 布局错乱”
原因
:IE11 Flexbox 实现不完整
解决方案
:
/* 添加 -ms- 前缀 */
.container {
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
}
/* 修复特定组件 */
.is-ie11 .card-columns {
column-count: 3; /* 使用多列布局替代 Flexbox */
column-gap: 1.25rem;
}
.is-ie11 .card {
display: inline-block;
width: 100%;
margin-bottom: 1.25rem;
break-inside: avoid;
}
6.1.4 “Bootstrap 模态框无法关闭”
原因
:IE11 对 backdrop 点击事件处理不同
解决方案
:
// 修复模态框关闭
$(document).on(‘click’, ‘.modal’, function(e) {
if (e.target === this) {
$(this).modal(‘hide’);
}
});
// 或者使用 data 属性
$(‘.modal’).on(‘click’, function(e) {
if (e.target === this) {
$(this).modal(‘hide’);
}
});
6.1.5 “SVG 图标不显示”
原因
:IE11 对 SVG 支持不完整
解决方案
:
// 使用 svg4everybody
if (typeof svg4everybody === ‘function’) {
svg4everybody();
}
// 或者使用 PNG 降级
.is-ie11 .icon-svg {
background-image: url(‘icon.png’);
background-size: contain;
background-repeat: no-repeat;
width: 20px;
height: 20px;
}
6.2 调试技巧
6.2.1 IE11 开发者工具
按
F12
打开开发者工具
使用
调试器
选项卡设置断点
使用
控制台
查看错误
使用
网络
选项卡检查资源加载
6.2.2 特征检测
// 在代码中添加调试信息
function debugIE11() {
var isIE11 = !!window.MSInputMethodContext && !!document.documentMode;
if (isIE11) {
console.log(‘=== IE11 调试信息 ===’);
console.log(‘User Agent:’, navigator.userAgent);
console.log(‘Document Mode:’, document.documentMode);
console.log(‘Features:’);
console.log(‘ – fetch:’, typeof window.fetch);
console.log(‘ – Promise:’, typeof Promise);
console.log(‘ – Object.assign:’, typeof Object.assign);
console.log(‘ – Array.includes:’, typeof Array.prototype.includes);
console.log(‘ – classList:’, typeof document.documentElement.classList);
console.log(‘ – SVG:’, typeof SVGRect);
console.log(‘=====================’);
}
}
// 在页面加载时调用
debugIE11();
6.2.3 使用条件注释
七、总结与最佳实践
7.1 核心要点回顾
Polyfill 是关键
:必须加载 core-js、whatwg-fetch 等 Polyfill
构建工具配置
:使用 Babel 和 PostCSS 自动处理兼容性
CSS 前缀
:确保所有 Flexbox 属性都有
-ms-
前缀
JavaScript 降级
:使用 jQuery 处理事件,避免直接使用现代 API
测试验证
:在真实 IE11 环境中全面测试
7.2 性能考虑
按需加载
:只加载必要的 Polyfill
代码分割
:将 polyfill 和应用代码分离
缓存策略
:使用 hash 文件名,利用浏览器缓存
CDN 使用
:使用公共 CDN 加速资源加载
7.3 长期策略
虽然本文提供了完整的 IE11 兼容方案,但建议:
逐步淘汰
:制定 IE11 淘汰计划
用户引导
:提示用户升级浏览器
功能降级
:为 IE11 提供简化版功能
监控分析
:跟踪 IE11 用户比例,评估兼容成本
7.4 推荐的最终方案
通过以上完整的方案,你可以确保 Bootstrap 4 在 IE11 中稳定运行,同时保持代码的现代化和可维护性。记住,IE11 支持是一个渐进的过程,需要持续监控和调整。