IE9兼容爬坑总结—vuecli3+eleUI后台管理项目兼容IE9

3,610 阅读6分钟

Q1. 不兼容ES6语法

引入babel-polyfill和es6-promise两个插件,将ES6语法转化为ES5

1-1、安装

cnpm install --save-dev babel-polyfill es6-promise

1-2、main.js引入

// 解决低版本浏览器不支持promise问题
import 'babel-polyfill'
import Es6Promise from 'es6-promise'
Es6Promise.polyfill()

在配置文件vue.config.js中引入

// ...省略
chainWebpack: config => {
    // 新增
    config.entry.app = ['babel-polyfill', './src/main.js']
}
// ...省略

Q2. CSS Hack

不可能为了兼容一个IE9,而降低了其它主流版本浏览器下的页面体验,所以我会使用CSS Hack,在IE9时再去加载兼容文件和布局。

CSS hack是通过在CSS样式中加入一些特殊的符号,让不同的浏览器识别不同的符号,以达到应用不同的CSS样式的目的

2-1、条件注释法

因为我是针对IE9,所以我采用了IE浏览器专有的Hack方式:条件注释法。

2-1.1、在public目录创建文件夹ie9

主要用来存放为了兼容ie9而添加的css和js补丁文件等

2-1.2、在ie9文件下创建css文件ie9.css

ie9样式补丁文件

2-1.3、在index.html的head中引入
<head>
    // ...
    <!--[if lte IE 9]>
      <link href="./ie9/ie9.css" rel="stylesheet"></link>
    <![endif]-->
</head>

这样做了之后,在浏览器是ie9时,会自动加载ie9.css文件,而在其他浏览器下则会忽略这行代码

Q3. 不兼容flex布局

巧用position、float、display:inline-block属性灵活修改布局

Q4. 不兼容背景颜色的渐变写法background: linear-gradient(...)

正常:

background: linear-gradient(180deg, #1A74E4, #2599F0);

ie9兼容写法:

filter: progid:DXImageTransform.Microsoft.gradient(GradientType=1, startColorstr=#1A74E4, endColorstr=#2599F0);
-ms-filter: "progid:DXImageTransform.Microsoft.gradient (GradientType=1, startColorstr=#1A74E4, endColorstr=#2599F0)";

Q5、el-table数据量多(页面有滚动条时)在ie中会导致页面卡顿

原因:包裹表格的容器用了绝对或者相对定位,一般就是.el-table用了相对定位,但没有设置层级,在ie中z-index层级降低。

解决方法:只需要将表格所属的父级或者祖父容器的z-index调高就行,将.el-table设置为10之后,卡顿问题解决

Q6、不支持placeholder

用了一个大佬封装的js,稍微改了一下,能支持正常情况下placeholder的显示,比如登录注册页。

6-1、 在工具类文件夹下生成brower-version.js文件,用来判断浏览器版本

brower-version.js:

export default function browerVersion() {
  var ua = navigator.userAgent
  var ver = 0
  var versiondata
  var versionbool

  if (ua) {
    if (ua.match(/MSIE\s+([\d]+)\./i)) {
      ver = RegExp.$1
    } else if (ua.match(/Trident.*rv\s*:\s*([\d]+)\./i)) {
      ver = RegExp.$1
    }
  }

  versiondata = parseInt(ver)

  if (versiondata <= 9 && versiondata !== 0) {
    versionbool = true
  } else {
    versionbool = false
  }

  // versionbool    true: 低于ie9   false: ie10+
  return versionbool
}

6-2、定义全局的浏览器是否是ie9版本的判断字段

main.js

import browerVersion from '@/assets/utils/brower-version.js'
const isIE9 = browerVersion()
Vue.prototype.$browerVersion = isIE9

6-3、工具类文件夹下创建ie-placeholder.js文件

/assets/utils/ie-placeholder.js

6-4、项目加载时就load一遍placeholder定义文件,为ie9下的input输入框加上placeholder

在App.js调用ie-placeholder.js定义的方法 初始化各input的placeholder

<script>
import iePlaceholders from '@/assets/utils/ie-placeholder'
export default {
  name: 'App',
  mounted() {
    if (this.$browerVersion) {
      iePlaceholders()
    }
  }
}
</script>

但是涉及到elementUI的其他组件,比如日期选择,比如级联选择,就会有点问题。这里的建议还是在ie9下不要纠结显示placeholder,体验太差了。

Q7、输入框自带有文本删除按钮和密码查看按钮

在ie10+的版本我们可以通过

::-ms-clear,
::-ms-reveal{
  display:none !important;
}

这段代码来隐藏,但是我发现在ie9下面是没用的。只能通过在输入框末尾增加一个和背景同色的块来遮掉,但是这样会影响输入的内容的全显示,我的做法是就让它留着,影响不大。

Q8、input不支持type=number

当input的type属性为number时,还是可以任意输入其他符号。。我选择在ie9放弃number限制的挣扎

Q9、el-upload无法使用

在ie9下,el-upload是无法使用的。我引入了能够兼容ie9的其他上传插件,当浏览器为ie9时就用自定义的上传组件,当非ie9时就保持原来的el-upload组件。

用vue-upload-component替代el-upload

9-1、安装vue-upload-component

cnpm install vue-upload-component --save

9-2、引入

可以全局引入也可以局部引入,因为我只有两个地方用到了上传组件,所以我选择在用到的页面引入。

<template>
<!-- 省略n行代码 -->
<file-upload
    v-if="$browerVersion"
    ref="compatibleUpload"
    v-model="compatibleFiles"
    :post-action="`${API.UploadImg}`"
    @input-file="inputFile"
>
    <el-button
       :loading="uploadLoading"
       :icon="imgName ? '' : 'el-icon-upload2'"
       :title="imgName ? '重新选择' : '选择图片'"
        plain
    >
          {{ !imgName ? '上传图片' : imgName }}
    </el-button>
</file-upload>
<!-- 省略n行代码 -->
</template>
<script>
import VueUploadComponent from 'vue-upload-component'
export default {
  components: {
    FileUpload: VueUploadComponent
  },
  data() {
      imgSizeLimit: 2,
      imgName: '',
      imgUrl: '',
      uploadLoading: false,
      compatibleFiles: []
  },
  methods: {
      inputFile(newFile, oldFile, prevent) {
      // 添加文件
      if (newFile && !oldFile) {
        // 过滤不是图片后缀的文件
        if (!/\.(jpg|png)$/i.test(newFile.name)) {
          this.$message.closeAll()
          this.$message.warning('只能上传jpg/png文件,请重新选择')
          return prevent
        }
        if (newFile.size > this.imgSizeLimit * 1024 * 1024) {
          this.$message.closeAll()
          this.$message.warning(`上传的图片的大于${this.imgSizeLimit}M,请重新选择`)
          return prevent
        }
        // 自动上传
        if (Boolean(newFile) !== Boolean(oldFile) || oldFile.error !== newFile.error) {
          if (!this.$refs.compatibleUpload.active) {
            this.uploadLoading = true
            this.$refs.compatibleUpload.active = true
          }
        }
      }

      // 上传完成
      if (newFile && oldFile && !newFile.active && oldFile.active) {
        // 获得相应数据
        this.uploadLoading = false
        this.$refs.compatibleUpload.remove(newFile) // 删除当前文件对象
        let response = newFile.response
        if (Object.prototype.toString.call(response) !== '[object Object]') {
          response = (new Function('return ' + response))()
        } else {
          this.$message.closeAll()
          this.$message.error('图片上传失败, 请重新上传')
        }
        if ((response.resultCode === '1' || response.resultCode === 1) && response.data) {
          this.imgUrl = response.data
          this.imgName = newFile.name
          this.$message.closeAll()
          this.$message.success('图片上传成功')
        } else {
          this.imgUrl = ''
          this.imgName = ''
          this.$message.closeAll()
          let errorMsg = response.resultMessage ? response.resultMessage : '图片上传失败, 请重新上传'
          if (['10021'].includes(response.resultCode)) {
            errorMsg = `上传的图片的大于${this.imgSizeLimit}M,请重新选择`
            this.$message.warning(errorMsg)
            return
          }
          this.$message.error(errorMsg)
        }
      }
    }
  }
}
</script>

具体用法请参考官方文档,这要注意如果让组件自己发起请求,就是使用post-action参数,则是用iframe模拟form表单提交数据的,用这种方式传给后台的数据就是formData格式,但是不能添加header请求头。如果非要有请求头,那就要使用custom-action自定义上传方法,但是自定义上传方法的话,接口参数就不能用formData格式来传给后台了,为啥?因为ie9不支持new FormData()。。

Q10、不支持JSON.parse

没错,在IE9以下是不支持JSON.parse方法来解析json字符串的,有两种方法来替代JSON.parse

10-1、eval方式

function strToJson(str){ 
    var json = eval('(' + str + ')'); 
    return json; 
} 

但是出于安全性的考虑,建议尽量不要使用eval,如果从第三方获取数据进行解析,会存在恶意脚本代码的风险。

10-2、new Function方式

function strToJson(str){ 
    var json = (new Function("return " + str))(); 
    return json; 
} 

Q11、vue项目在IE中自动读取缓存中的数据,不重新发请求

这也不算只是ie9的问题了,ie内核都存在这个问题。当你请求接口时,请求地址和请求参数都没有变化的时候,ie是会默认从缓存中获取数据而不会重新发送请求的。

只要保证我们每次的请求都是一个新的请求,就可以避免这种情况了,最简单的方式就是每次请求都带多一个时间戳参数,只需要在axios拦截器添加几行设置时间戳参数的代码即可

// request拦截器
service.interceptors.request.use(
  config => {
    const time = Date.parse(new Date()) / 1000
    // 添加时间戳参数
    if (config.method === 'post') {
      config.data = {
        ...config.data,
        t: time
      }
    }
    if (config.method === 'get') {
      config.params = {
        ...config.params,
        t: time
      }
    }
    return config
  },
  error => {
    // Do something with request error
    return Promise.reject(error)
  }
)

Q11、在文本框中进行删除操作时,文本框的value不更新。

在我们之前Q2创建的ie9文件夹下,新建ie9-oninput-polyfill.js文件

11-1、ie9-oninput-polyfill.js:

/* eslint-disable */
(function (d) {
  if (navigator.userAgent.indexOf('MSIE 9') === -1) return;

  d.addEventListener('selectionchange', function() {
    var el = d.activeElement;

    if (el.tagName === 'TEXTAREA' || (el.tagName === 'INPUT' && el.type === 'text')) {
      var ev = d.createEvent('CustomEvent');
      ev.initCustomEvent('input', true, true, {});
      el.dispatchEvent(ev);
    }
  });
})(document);

11-2、在css Hack中引入

<head>
    // ...
    <!--[if lte IE 9]>
      <link href="./ie9/ie9.css" rel="stylesheet"></link>
      <script src="./ie9/ie9-oninput-polyfill.js" type="text/javascript"></script>
    <![endif]-->
</head>

Q12、不能导出二进制文件流

因为ie9一下不支持new Blob,所以不能将二进制文件流转为文件下载。解决方法是让后台改接口,不要传二进制文件流过来,直接给前端传文件下载链接

Q13、ie9下el-table的排序三角形错位

解决方法:

.el-table .caret-wrapper {
  display: inline-block;
}
.el-table .sort-caret{
  display: block;
}

好了,暂时只想到这些,后续有遗漏的会继续补充~