浏览器兼容性问题

常见浏览器兼容性问题及解决方案

问题

前端开发中常见的浏览器兼容性问题有哪些?如何解决?

解答

CSS 兼容性问题

1. 厂商前缀

/* 不同浏览器需要不同前缀 */
.box {
  -webkit-transform: rotate(45deg); /* Chrome, Safari */
  -moz-transform: rotate(45deg);    /* Firefox */
  -ms-transform: rotate(45deg);     /* IE */
  -o-transform: rotate(45deg);      /* Opera */
  transform: rotate(45deg);         /* 标准写法放最后 */
}

/* 使用 Autoprefixer 自动添加前缀(推荐) */
/* 只需写标准写法,构建时自动补全 */
.box {
  transform: rotate(45deg);
}

2. CSS Reset / Normalize

/* 简单的 CSS Reset */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* 或使用 Normalize.css 保留有用的默认样式 */

3. Flexbox 兼容写法

.container {
  display: -webkit-box;      /* 旧版 Safari */
  display: -webkit-flex;     /* Chrome 21-28 */
  display: -ms-flexbox;      /* IE 10 */
  display: flex;
}

JavaScript 兼容性问题

1. 特性检测

// 检测特性是否存在,而不是检测浏览器
if ('querySelector' in document) {
  document.querySelector('.box');
} else {
  document.getElementById('box');
}

// 检测 localStorage
if (typeof Storage !== 'undefined') {
  localStorage.setItem('key', 'value');
} else {
  // 降级方案:使用 cookie
}

// 检测 fetch
if (window.fetch) {
  fetch('/api/data');
} else {
  // 使用 XMLHttpRequest 或引入 polyfill
}

2. Polyfill 示例

// Array.prototype.includes polyfill
if (!Array.prototype.includes) {
  Array.prototype.includes = function(searchElement, fromIndex) {
    if (this == null) {
      throw new TypeError('this is null or undefined');
    }
    
    const o = Object(this);
    const len = o.length >>> 0;
    
    if (len === 0) return false;
    
    const n = fromIndex | 0;
    let k = Math.max(n >= 0 ? n : len + n, 0);
    
    while (k < len) {
      if (o[k] === searchElement) return true;
      k++;
    }
    return false;
  };
}

// 使用 core-js 按需引入 polyfill(推荐)
import 'core-js/features/array/includes';
import 'core-js/features/promise';

3. 事件兼容

// 事件绑定兼容
function addEvent(element, type, handler) {
  if (element.addEventListener) {
    // 标准方式
    element.addEventListener(type, handler, false);
  } else if (element.attachEvent) {
    // IE8 及以下
    element.attachEvent('on' + type, handler);
  } else {
    element['on' + type] = handler;
  }
}

// 事件对象兼容
function getEvent(e) {
  return e || window.event;
}

function getTarget(e) {
  return e.target || e.srcElement;
}

function preventDefault(e) {
  if (e.preventDefault) {
    e.preventDefault();
  } else {
    e.returnValue = false; // IE
  }
}

function stopPropagation(e) {
  if (e.stopPropagation) {
    e.stopPropagation();
  } else {
    e.cancelBubble = true; // IE
  }
}

工程化解决方案

Babel 配置

// babel.config.js
module.exports = {
  presets: [
    ['@babel/preset-env', {
      targets: {
        browsers: ['> 1%', 'last 2 versions', 'not dead']
      },
      useBuiltIns: 'usage', // 按需引入 polyfill
      corejs: 3
    }]
  ]
};

Browserslist 配置

# .browserslistrc
> 1%
last 2 versions
not dead
not ie <= 10

PostCSS + Autoprefixer

// postcss.config.js
module.exports = {
  plugins: [
    require('autoprefixer')
  ]
};

常见兼容问题速查

问题解决方案
IE 不支持 ES6+Babel 转译 + polyfill
CSS3 属性不生效Autoprefixer 添加前缀
默认样式不一致Normalize.css
图片在 IE 下有边框img { border: 0; }
IE 盒模型差异box-sizing: border-box
移动端 300ms 延迟FastClick 或 touch-action: manipulation

关键点

  • 特性检测优于浏览器检测:检测功能是否存在,而非判断浏览器类型
  • 使用构建工具:Babel 处理 JS,Autoprefixer 处理 CSS 前缀
  • 按需 Polyfill:配置 useBuiltIns: 'usage' 避免打包冗余代码
  • 渐进增强:先保证基础功能,再为现代浏览器添加增强体验
  • 明确目标浏览器:通过 Browserslist 统一配置支持范围