项目背景
因为开发的网页需要嵌入到别的业务方的系统中,而使用该系统的人大多使用的是IE8,故项目需要兼容IE8。本来使用vue早早就开发好了,后来听闻必须兼容IE8,故使用了支持 IE8 且有好多现成的表单组件的 layui 框架,而自己又已经习惯写 ES6 的语法,所以引入了babel + webpack4来编译打包。开发过程当中,除了边参考layui官网边写代码,更让人恶心的是 IE8 下出现的一些奇奇怪怪的问题,在此记录下。
出现的问题以及解决方式
webpack代理发送请求
出现原因
未搞清楚webpack中 devServer.proxy 的用法
解决方式
ie8下出现SCRIPT1010 缺少标识符
出现原因
低版本ie把 default 列为关键字
解决方式
其中 @babel/plugin-transform-runtime 这个plugin配置不需要配。
ie8下select动态渲染的问题
出现原因
如图中laytpl.js 的 t.pt.parse 的 replace(/(?="|')/g, "\\") 这一步,在测试demo中动态生成的时候这里运行后两个双引号之间 "" 是会变成 \"\" ,所以 <option value=""> 会变成<option value=\"\"> ,而在自己项目中同样的代码,在 IE8 下会被编译成 <option value=\""> ,这样导致编译错误,报如下错误。
怀疑是因为打包后的 replace 方法被改写了还是啥,在浏览器运行这个项目的代码后,在浏览器控制台使用 replace 方法都会有问题,而不运行这个项目的代码则没有问题。


解决方式
- 把
<option value="">请选择</option>中value=""改成value,即不写双引号的空字符串。但是这样又会引发另一个问题,当选择这个默认的”请选择“的时候,此时value值会为”请选择“而导致出错,如下图。 - 把
<option value="">请选择</option>中的双引号改为单引号即可,这个是最佳方式。


ie8下监听input导致堆栈溢出
出现原因
IE8 下监听 propertychange 时,若在处理事件中给该 input 赋值,则又会导致触发这个事件,从而造成死循环。
解决方式
$("#idname").bind("input propertychange", function() {
// 增加一个全局的同步信号,防止在ie8下陷入死循环
if($.syncProcessSign) return ;
$.syncProcessSign = true;
//函数执行...
$.syncProcessSign = false;
});
ie8不支持before等伪元素
出现原因
天性使然
解决方式
::before 改为 :before
ie8下上传文件的问题
出现原因
因为要将以上传的文件的名字显示在页面上,故每次选择文件后上传文件前需要读取到所选文件的 name,如下图1所示,file的name其实是可以获取的,但是在 ie8/9下不支持 obj.preview 方法,且ie8/9下是读取不到所选文件的具体信息的,只能上传后读取file input 的value值,如下图2所示


解决方式
分环境来上传,当为非IE8/9时,采用 obj.preview 方法,并手动上传(因为在上传前要做校验,比如校验目前已上传的数量是否超过限制,以及上传的文件大小是否超过最大值);当为IE8/9时,设置为自动上传(因为此时不能获取到选中的file,手动上传的 obj.upload(index, file) 需要具体file参数),从 file input 的value中读取文件名并保存下来,上传成功后再动态渲染出文件名,大致代码如下:
let uploadInst = upload.render({
elem: "#uploadFile", //绑定元素
url: "/xxx/xxx", //上传接口
accept: "file",
data: { sso },
auto: isIE8_9 ? true : false,
size: 1024 * MAX_FILE_SIZE_MB,
choose: function(resultObj) {
if (uploadFileList.length >= LIMIT_FILE_COUNT) {
layer.msg(`最多支持上传${LIMIT_FILE_COUNT}个文件`);
if (isIE8_9) {
// 如果是IE8/IE9,直接return掉
return;
}
}
if (!isIE8_9) {
//预读本地文件,如果是多文件,则会遍历。(不支持ie8/9)
resultObj.preview(function(index, file) {
crtFileInfo = { file: file }; // 保存当前上传的文件信息
resultObj.upload(index, file); //文件上传
});
} else {
let inputFile = $('input[type="file"]');
let fileValue = inputFile[0].value;
let pos = fileValue.lastIndexOf("\\");
let fileName = fileValue.substring(pos + 1);
crtFileInfo = { file: { name: fileName } }; // 保存当前上传的文件名
}
},
before: function(obj) {
//obj参数包含的信息,跟 choose回调完全一致
layer.load(); //上传loading
},
done: function(res) {
//上传完毕回调
console.log("file上传完毕");
if (isIE8_9 || isIE10Func()) {
resetUploadInput(); // 重置file input,不然在低版本ie下不能重复上传
}
if (res.code === "0000") {
crtFileInfo && (crtFileInfo.id = res.data);
uploadFileList.push(crtFileInfo);
} else {
layer.msg(res.message);
}
crtFileInfo = null;
layer.closeAll("loading"); //关闭loading
// 渲染已上传文档区域
renderFileList();
},
error: function() {
//请求异常回调
crtFileInfo = null;
console.log("上传文件失败");
layer.closeAll("loading"); //关闭loading
}
});
ie8/9/10下再次上传同一文件时,选择文件不会被唤起
出现原因
因为此时file input 的value值跟上一个文件名是一样的,这样input会认为没有发生变化,故不会被唤起。
解决方式
在 done 回调中,清除input的value值。IE8下无法改变input的value值,IE9/10可以,layui在IE8下的file input 外层包了一个form,此时reset form即可,大致代码如下:
// 处理因ie造成的无法上传同名文件
function resetUploadInput() {
const $fileInputEle = $(".layui-upload-file")
const $fileUploadFormEle = $('.layui-upload-form')[0];
if(isIE8Func()){
$fileUploadFormEle.reset();
}else if(isIE9Func() || isIE10Func()){
$fileInputEle.attr("type", "hidden");
$fileInputEle.attr("type", "file");
}
}
ie8/9/10删除上传的文件报错
出现原因
因为文件列表的每个文件的index写在了dataset里,而低版本ie不支持 dataset 。
解决方式
// 兼容dataset
export const getDataset = function(ele) {
if (ele.dataset) {
return ele.dataset;
} else {
var attrs = ele.attributes, //元素的属性集合
dataset = {},
name,
matchStr;
for (var i = 0; i < attrs.length; i++) {
//是否是data- 开头
matchStr = attrs[i].name.match(/^data-(.+)/);
if (matchStr) {
//data-auto-play 转成驼峰写法 autoPlay
name = matchStr[1].replace(/-([\da-z])/gi, function(all, letter) {
return letter.toUpperCase();
});
dataset[name] = attrs[i].value;
}
}
return dataset;
}
};
ie下无console对象导致的报错
出现原因
在低版本ie下,若不打开浏览器控制台,console会不存在,导致console.log()报错。
解决方式
// 憨憨的方式
if(console && console.log){
console.log('test');
}
常用的工具类方法
解析URL传参
1/**
2 * 解析URL传参
3 * @param {*} key
4 */
5
6export const getQueryString = function(key) {
7 let after = window.location.search;
8 if (location.href.indexOf("?") === -1) return null; //如果url中没有传参直接返回空
9
10 //key存在先通过search取值如果取不到就通过hash来取
11 after = after.substr(1) || window.location.hash.split("?")[1];
12 if (after) {
13 let reg = new RegExp("(^|&)" + key + "=([^&]*)(&|$)");
14 let r = after.match(reg);
15 if (r != null) {
16 return decodeURIComponent(r[2]);
17 } else {
18 return null;
19 }
20 }
21};
使用post方式实现文件下载
1function downloadFile($, url, params) {
2 // Build a form
3 var form = $("<form></form>")
4 .attr("action", url)
5 .attr("method", "post");
6 // Add the one key/value
7 $.each(params, function(k, v) {
8 form.append(
9 $("<input></input>")
10 .attr("type", "hidden")
11 .attr("name", k)
12 .attr("value", v)
13 );
14 });
15 //send request
16 form
17 .appendTo("body")
18 .submit()
19 .remove();
20}
webpack的配置
目录结构
我的目录结构:
├── assets
│ ├── css
│ │ ├── apply.css
│ │ ├── common.css
│ │ └── myskin.css
│ └── js
│ ├── html5.min.js
│ └── respond.min.js
├── html
│ ├── apply.html
│ └── list.html
├── js
│ ├── apply.js
│ └── list.js
├── layui
│ ├── css
│ │ ├── layui.css
│ │ ├── layui.mobile.css
│ │ └── modules
│ ├── font
│ │ ├── iconfont.eot
│ │ ├── iconfont.svg
│ │ ├── iconfont.ttf
│ │ ├── iconfont.woff
│ │ └── iconfont.woff2
│ ├── images
│ │ └── face
│ ├── lay
│ │ └── modules
│ ├── layui.all.js
│ └── layui.js
├── package.json
├── src
│ ├── apply.js
│ ├── js
│ │ ├── const.js
│ │ ├── httpReq.js
│ │ ├── polifill.js
│ │ └── util.js
│ └── list.js
├── webpack.dev.js
├── webpack.prod.js
└── yarn.lock
webpack配置
看到留言里说附上webpack的配置,在这里贴一下dev环境和production环境对应webpack的配置:
// webpack.dev.js
const glob = require("glob");
const path = require("path"); //node的路径模块
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const webpack = require('webpack');
const entry = {};
const entryFiles = glob.sync(path.join(__dirname, "./src/*.js"));
Object.keys(entryFiles).map(index => {
const entryFile = entryFiles[index];
const match = entryFile.match(/src\/(.*)\.js/);
const pageName = match && match[1];
entry[pageName] = entryFile;
});
module.exports = {
mode: "development",
entry,
output: {
path: path.join(__dirname, "./build"), //输出位置
filename: "js/[name].js" //输入文件
},
module: {
rules: [
{
test: /\.js$/,
include: path.resolve("src"),
use: "babel-loader"
}
]
},
plugins: [new CleanWebpackPlugin(), new webpack.HotModuleReplacementPlugin(),],
devServer: {
proxy: {
"/xxx": {
target: "http://xx.xxx.xxx.xxx:port", //开发环境
changeOrigin: true,
}
},
hot: true,
stats: 'errors-only' //打包阶段只有出现error时才输出出错内容
},
devtool: "source-map"
};
// webpack.prod.js
const glob = require("glob");
const path = require("path"); //node的路径模块
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const entry = {};
const entryFiles = glob.sync(path.join(__dirname, "./src/*.js"));
Object.keys(entryFiles).map(index => {
const entryFile = entryFiles[index];
const match = entryFile.match(/src\/(.*)\.js/);
const pageName = match && match[1];
entry[pageName] = entryFile;
});
module.exports = {
mode: "production",
entry,
output: {
path: path.join(__dirname, "./js"), //输出位置
filename: "[name].js" //输入文件
},
module: {
rules: [
{
test: /\.js$/,
include: path.resolve("src"),
use: "babel-loader"
}
]
},
optimization: {
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
ie8: true
}
})
]
}
};