浏览器兼容性

958 阅读11分钟

什么是浏览器兼容器

浏览器兼容性指的是不用浏览器拥有不同的内核,而不同的浏览器又分为两种:一是渲染引擎,另一个是js引擎。(一般也指css兼容,js兼容)因此在解析html和css,js时就会有不同的处理,因为不同浏览器展示情况也不同。 不过好在微软已经不维护IE浏览器了,以后前端再也不用因为处理低版本的IE浏览器绞尽脑汁,秃头有救了。

浏览器内核

不同的浏览器内核是不一样的,下面我们主要介绍几种常见的浏览器的内核

  1. trident内核:代表就是IE浏览器。百度浏览器,世界之窗浏览器也是用的这个。
  2. webkit内核:苹果的safari浏览器就是这个内核,以前的谷歌浏览器也是这个内核
  3. Blink内核:现在的谷歌浏览器
  4. Gecko内核:火狐浏览器的内核
  5. Presto内核:Opera浏览器,后来加入了谷歌大军,变成了webkit,最后又变成了Blink
  6. IE + Chrome双内核:360浏览器,猎豹浏览器,2345浏览器以前是IE内核,现在也是双内核了
  7. Trident(兼容模式) + Webkit(高速模式): 搜狗,遨游,QQ浏览器
  8. UC浏览器内核:这个众口不一,UC说是他们自己研发的U3内核,但好像还是基于Webkit和Trident,还有说是基于火狐内核。

常见浏览器兼容性问题

上面也提到过,对于浏览器兼容性问题,主要分为两类:css兼容性问题和js兼容性问题。

css兼容性问题

  1. 不同浏览器的标签默认的margin和padding不同
  • 表现: 随便写一个标签,在不同的浏览器中,各自的margin和padding差异很大
  • 解决方式:新建一个重置样式文件,将这种类型的元素统一设置
  1. css3新属性在不同浏览器中效果不同,有的不生效
  2. 块元素设置浮动后,又有水平方向的margin,在IE浏览器下margin加倍
  • 解决方式: 给浮动元素设置display: inline, 将其转换为行内元素
  1. 给元素的高度设置值很小(小于10px), 在遨游,IE6,IE7下高度超过自己甚至的高度
  • 表现:这种情况一般出现在我们设置小圆角背景的标签里。出现这个问题的原因是IE8之前的浏览器都会给标签一个最小默认的行高的高度。即使你的标签是空的,这个标签的高度还是会达到默认的行高。
  • 解决方式:给容器添加overflow: hidden;或者line-height设置为小于我们设置的高度。
  1. 行内元素设置display: black后采用浮动,又有水平方向的margin时,IE间距出现问题
  • 表现:IE6里的间距比超过设置的间距
  • 说明:行内元素,为了设置宽高,需要设置display: block;(除input外),在用float布局并有横向的margin后,在IE6下,他就具有了块属性float后的横向margin的bug。不过因为它本身就是行内属性标签,所以我们再加上display:inline的话,它的高宽就不可设了。这时候我们还需要在display:inline后面加入display:talbe。
  • 解决方式:在display:block;后面加入display:inline;display:table;
  1. IE浏览器div最小高度和宽度的问题
  • 表现:min-height本来就是一个不兼容的属性
  • 解决方式:设置为:{min-height: 200px; height: auto !important; height: 200px; overflow: visable;}
  1. 超链接访问过hover样式就不出现的问题
  • 表现:被点击访问过的超链接样式不再具有hover和active了。
  • 解决方式:改变css属性的排列顺序。L-V-H-A(:link -> :visited -> :hover -> :active)
  1. 图片默认有间距
  • 表现: 几个img标签放在一起的时候,有些浏览器会有默认的间距,通配符清除间距也不起作用
  • 解决方式:通过给所有的图片设置浮动或者给所有的img标签设置display: block;
  1. css hack解决浏览器兼容性问题 不同浏览器,识别不同的样式,css hack本身就是处理浏览器的兼容性问题的。 比如:height: 300px; *height: 200px; _height: 100px;
  2. 百分比的bug
  • 表现:父元素的宽度为100%,子元素宽度各为50%,在IE6下各个元素宽度之和超过100%
  • 解决方式:给右边浮动的元素添加clear: right;
  1. 透明度属性
  • 表现:IE浏览器和其他浏览器对于透明度属性的支持写法不一样
  • 解决方式:针对IE浏览器:filter: alpha(opacity=value); (取值范围1--100)。 兼容其他浏览器:opacity: value; (取值范围0--1)
  1. 上下margin的重叠问题
  • 表现:给上边元素设置margin-bottom,给下边元素设置margin-top,浏览器只会识别最大值
  • 解决方式:其实这个也不算是兼容性问题,我们只设置其中一个就可以了

js兼容性问题

  1. 事件绑定 表现:IE:dom.attachEvent('onclick', function(){}); 标准浏览器:dom.addEventListener('click', function(event){}, false); 解决方式:
var x = document.getElementById('myBtn');
if (x.addEventListener) {
  // 所有主流浏览器, ie9+
  x.addEventListener('click', myFunction);
} else if (x.attachEvent) {
  // IE8及更早版本
  x.attachEvent('onclick', myFunction);
}
  1. event事件对象问题 表现:
document.onclick = function(ev) {
  // 谷歌火狐的写法,IE9以上支持
  var e = ev;
}

document.onclick = function() {
  // 谷歌和IE支持
  var e = event;
}

解决方式

document.onclick = function(ev) {
  var e = ev || window.event;
}
  1. event.srcElement(事件源对象)问题 表现:IE: event对象有srcElement属性,但是没有target属性; 火狐:event对象有target属性,但是没有srcElement属性。 解决方式:
srcObj = event.srcElement ? event.srcElement : event.target;
  1. 获取元素的非行间样式值 表现: IE: dom.currentStyle['width']获取元素高度; 标准浏览器:window.getComputedStyle(obj, null)['width']; 解决方式:
function getStyle(obj, attr) {
  if (obj.currentStyle) {
    // 兼容IE
    return obj.currentStyle[attr];
  } else {
    return window.getComputedStyle(obj, null)[attr];
  }
}
  1. 阻止事件冒泡传播 表现:IE:e.cancelBubble = true; 标准浏览器:e.stopPropagation(); 解决方式:
document.onclick = function (e) {
  var e = e || window.event;
  if (e.stopPropagation) {
    // 标准浏览器
    e.stopPropagation();
  } else {
    // IE浏览器
    e.cancelBubble = true;
  }
}
  1. 阻止事件默认行为 表现:js阻止默认事件,一般是阻止a链接href,form表单submit提交. 标准浏览器: event.preventDefault(); IE浏览器:e.returnValue = 'false';
document.onclick = function (e) {
  var e = e || window.event;
  if (e.preventDefault) {
    // 标准浏览器
    e.preventDefault();
  } else {
    // IE浏览器
    e.returnValue = 'false';
  }
}
  1. ajax兼容问题 表现:IE: ActiveXObject. 其他标准浏览器:XMLHttpRequest 解决方式:
// 创建ajax对象
var oAjax = null;
if (window.XMLHttpRequest) {
  // 只支持非IE6浏览器
  oAjax = new XMLHttpRequest();
} else {
  // 只支持IE6浏览器
  oAjax = new ActiveXObject('Microsoft.XMLHTTP');
}

// 连接服务器,这里加个时间参数,每次访问地址都不一样,浏览器就不用浏览器里的缓存了,服务器那端也不会解析这个时间
oAjax.open('get', 'a.txt?=' + new Date().getTime(), true);

// 发送
oAjax.send(null);

// 接受信息
oAjax.onreadystatechange = function() {
  if (oAjax.readyState === 4) {
    if (oAjax.status == 200) {
      alert('成功' + oAjax.responseText);
    } else {
      alert('失败')
    }
  }
}

总结

以上是关于浏览器兼容性的问题,对于工作时间比较早的同学来说,可能或多或少的遇到过这些问题,对于最近一两年才工作的同学来说,可能见都没见过这些问题,没有关系,我们只需要了解一下就好,毕竟现在项目开发很少去兼容低版本的IE浏览器了,而且前端时间微软也正式公布不再维护IE浏览器了,所以以后基本上也不用处理IE浏览器兼容性问题了。

在vue项目中如何处理浏览器兼容性问题

我们在开发过程中,主要还是基于主流js框架来开发,比如:vue或者react。还会结合一些UI框架来开发,而且用到的JavaScript也基本是es6语法,或者ts这种,下面我们就来看看这里面会存在哪些兼容性问题,以及怎么处理。 我主要使用的是vue.js, 所以我主要以vue来举例

常见UI框架兼容性问题

对于css中的一些样式,比如像flex布局这种,一般使用post2css-loader来处理。对于一般的elementUI框架都会兼容一些主流浏览器版本,如果还需要额外兼容别的浏览器则需要另外处理。 针对具体的样式兼容性,我们会根据ie浏览器具体去补充。 关于样式这块,我们就先写到这,后续有什么问题我们会在这里继续补充的。

需要注意的是:vue3不再支持ie11,对应的elementUIplus也不再支持ie11.

vue.js中的兼容性问题

这里面的主要兼容性问题有以下几个方面

  1. vue 只兼容IE8以上版本。 (IE8之前的版本太低,我们可以不考虑,不做任何处理)
  2. IE不兼容axios的promise对象 主要是下载插件包es6-promise来解决。
  3. IE不兼容ES6语法 大家在开发项目的时候,基本上都会遇到浏览器页面白屏,控制台报错的问题,然后大家可能会疑惑,我在谷歌浏览器中运行的好好的呀。为什么在IE11中就不行了呢。这个主要原因就是IE浏览器不支持es6的语法啦,下面我们一起来看一下如何兼容ie11这类不兼容es6的问题。
推荐给大家两个比较好用的网站
  • 查看es6在各大浏览器不同版本的支持情况: css和兼容性检查平台:caniuse.com/?search=es6 这个网站可以很清楚地看出浏览器对es6的支持情况。
  • 查看es6语法和api的兼容性详细统计情况: github统计es6兼容性站点:kangax.github.io/compat-tabl… 从这个网站可以看出各大浏览器和插件对es6里面详细API的支持,因此我们就可以很轻松的找到解决方案了。然后我惊喜地发现IE11现在对es6的语法支持变多了。但是这并不影响我们处理我们兼容性问题,因为总可能遇到会存在兼容性问题的时候,根据这个网站也能帮我们找到解决问题的思路:那就是编译器里面Babel 7 + core-js 3基本上能解决浏览器不支持es6语法的问题。
引入babel7

需要注意的是,babel默认只转换js语法(如=>, class等),不转换新的api(如:Iterator, Generator, set, maps, proxy, reflect, symbol, Promise等全局对象,以及一些定义在对象上的方法:Object.assign等),因此还需要引入转换新api的工具库

引入core-js3

想要兼容/转换es6+的新api, 就需要core-js3来支持了。 core-js可以有效支持promises, symbols, collections, iterators, typed arrays等,需要注意的是,转换新api的方案叫做pollyfill, babel-polyfill是兼容版api的延续至今的一套方案。它内部引入了core-js2, 又集成了regenerator-runtime,但是使用这套方案会把全部api特性全部打包,会导致为使用的新的api的引入导致文件过大或全局污染。那么对应的按需加载的方案就来了:babel-runtime。 babel-time不同babel-polyfill的是它可以结合babel插件babel-plugin-transform-runtime,按需引入babel-runtime中的polyfill.但是babel-runtime也有其缺点:它只支持静态方法。includes, Object.assign等实例下es6方法支持不足,解决这个问题就需要相应插件来满足。

babel插件化

刚刚说到babel插件化,babel有很多解决特定问题的插件,比如:babel-plugin-array-includes 、@babel/plugin-transform-arrow-functions、 babel/plugin-proposal-class-properties等,但是这些插件如果一个一个引入也是很麻烦的,有没有可以一起引入所有插件的方法呢?

babel插件整合方案

最早期的插件整合方案常用的叫babel-preset-es2015,它解决了最早期es6特性的兼容性问题,但是随着es6+的各种特性的出现,后面由扩展出了babel-preset-stage-1、babel-preset-stage-2、babel-preset-stage-2、babel-preset-stage-3,具体差别和功能不做赘述,大家可以自行了解一下。随着以上方案的扩展,慢慢babel-preset-es2015需要一个最新的集成方案,于是就有了babel-preset-env,就是那个每次npm安装babel-preset-2015时都提醒我们的babel-preset-env安装提醒,它可以根据目标浏览器或者运行环境配置来自动转换es2015+的代码。

babel配置

关于babel配置,vue项目中有很多地方都可以配置处理,如packge.json、babel.config.js、.babelrc、.browserlistrc。大家根据实际情况配置和查看相应文件里面的配置。 这里面需要注意的是,从babel7开始,.babelrc的作用范围仅限于当前项目,默认不再作用于 node_modules 和工作区 (./packages/*),如果需要,可以指定作用范围。一般通过babel.config.js和.browserlistrc来做babel的转译处理了。具体的配置,这里不做详细解读了,官网有比较详细的解释:www.babeljs.cn/docs/config…

  1. babel.config.js
module.exports = {
    env: {
        test: {
            presets: [
                [
                    '@babel/preset-env',
                    {
                        targets: {node: 'current'},
                    }
                ]
            ]
        }
    },
    presets: ['@vue/cli-plugin-babel/preset', '@babel/preset-env'],
};

  1. .browserlistrc
> 0.25%
last 2 versions