最近公司的项目要求兼容ie,因为客户需求需要兼容到ie8,接手项目时,技术架构已经形成 :webpack + gulp+ es6 +jquery 。在兼容ie8过程,遇到过一些问题,总结如下。
webpack对js代码处理过程中,会引入Object.definePrototype方法,该方法ie8中并未完全实现。网上有很多教程,其中有通过配置.babelrc为es2005-loose
{
"presets": [
"es2015-loose",
"stage-0"
]
}
也有说通过引入shim、sham文件等,但是好像都不起作用。经过多次测试发现,项目中含有 Object.definePrototype部分的代码没有实际作用。此处采用了一种比较暴力的方法,直接删除该部分代码。考虑每次上线发布需要手动删除该部分,可能会因为某次忘记删除导致线上错误,于是通过node编写了一个简单的脚本,然后再打包执行时集成该脚本的运行文件。 脚本replace.js如下:
/*** Created by zhengchangshun on 2017/10/16.
* ie8 兼容时, Object.defineProperty 手动去除,暂时未发现有什么影响
* dev下,如需兼容ie8,请自行手动执行 node ./replace.js dev
*/
var fs = require('fs');
var path = require('path');
var name = '';
// 开发、生成 的代码路径
var config = {
"dev": "./dev/js/",
"dist": "./dist/js/"
}
//获取运行的环境
var args = process.argv[2];
fs.readdirSync(path.resolve(config[args])).forEach(function (v) {
if (v.indexOf('common') > -1) {
name = path.resolve(config[args], v);
return;
}
})
//读取内容,并将 Object.defineProperty 去除
var content = fs.readFileSync(name, 'utf-8')
var reg = /Object\.defineProperty\([\s\S]*?\}\)\;/mg
content = content.replace(reg, '');
fs.writeFileSync(name, content, 'utf-8')
"scripts": {
...........
"build": "gulp build && node ./replace.js dist",
.........
*ps: 我只对common.js中的Object.definePrototype做处理,通过正则表达式匹配
Object.definePrototype( ……..}),然后将该部分代码替换为空子字符串。当前前提是必须保证删除该部分代码不会对程序造成影响。*
由于项目需要,可能会用到动态脚本加载。一般方法如下:
var Script = document.createElement('script');
Script.id = 'id';
Script.type = 'text/javascript';
Script.src = 'yourScriptUrl'
在ie8中,动态修改script的src属性会报错,解决办法:调用jquery的getScript()方法,如下:
$.getScript(yourScriptUrl)
项目发布到线上时,通常会对css文件进行打包压缩,以节省空间。通常过程中,项目开发过程中会引入一些样式库文件。库文件一起打包,生成的合并文件就会变大。在chrome、FF、IE9以上没有问题,但是在ie8中,样式不显示。调试过程中,开始觉得很诡异,突然一个想法怀疑是ie8对css文件大小有限制,经过查资料,得到如下结论:
- Up to 31 CSS files or tags per page. 单个页面最多31个css文件
- Up to 288K per CSS file (uncompressed). 每个css文件大小最大288k
- Up to 4095 selectors per CSS file. 每个css文件最多4095个选择器
确实是ie8对css文件的字节数有限制。解决办法:css、less等库文件打包时,可以打包成2至3个文件。
ie8不支持window.location.orign方法,取值为undefined。解决方法:通过协议、主机、端口拼,重写改属性
if (!window.location.origin) {
window.location.origin = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port : '');
}
window.location.href = window.location.origin;
在ie8浏览器中,对于同一接口多次请求,会从浏览器的缓存中获取,解决方法:添加时间戳
url: 'yourAjaxUrl?time=' + new Date().getTime()
一般这个问题的现象是:当你开发控制台时不报错,关闭控制台运行代码时报错。window.console()在ie8中必须打开控制台才能实现,关闭控制台时会报错,在ie9才开始修复该问题。解决这个兼容性问题,有两种方案:
- 去除页面中的console.log()方法 (我是用这种方法处理的,页面中残留的console.log()不多)
- 重写window.console()方法;
(function () {
var funcs = ['assert', 'clear', 'count', 'debug', 'dir', 'dirxml',
'error', 'exception', 'group', 'groupCollapsed', 'groupEnd',
'info', 'log', 'markTimeline', 'profile', 'profileEnd',
'table', 'time', 'timeEnd', 'timeStamp', 'trace', 'warn'];
for(var i=0,l=funcs.length;ivar func = funcs[i];
if(!console[func])
console[func] = function(){};
}
})()
forEach在ie8中不兼容,重写该方法:
if ( !Array.prototype.forEach ) {
Array.prototype.forEach = function forEach( callback, thisArg ) {
var T, k;
if ( this == null ) {
throw new TypeError( "this is null or not defined" );
}
var O = Object(this);
var len = O.length >>> 0;
if ( typeof callback !== "function" ) {
throw new TypeError( callback + " is not a function" );
}
if ( arguments.length > 1 ) {
T = thisArg;
}
k = 0;
while( k < len ) {
var kValue;
if ( k in O ) {
kValue = O[ k ];
callback.call( T, kValue, k, O );
}
k++;
}
};
}
有时候需要兼容input输入框的值动态的变化,当然onKeyup也是一种方法,但是对于用鼠标复制等无法监听,onchange需要值有变化时才出发。所以oninput方法比较合适,但是在ie8以下未被兼容,可以通过onpropertychange处理,代码如下:
Ev.on(ele,"input propertychange",function(e){
// do something
});
ie8下不兼容String.trim()方法,判断String作用域链上是否存在trim方法,如不存在,就自定义改方法
// string trim 方法兼容
if (!String.prototype.trim) {
String.prototype.trim = function trim() {
return this.replace(/^\s+|\s+$/g, '');
}
}
有道云笔记地址:http://note.youdao.com/noteshare?id=db7257e244c466caad5a6f0cd8c131f7