最近基于React开发的组件需要兼容IE8+。因此,总结了开发过程中的一些坑。
ie8,ie9首先考虑跨域问题
主要是采用服务端代理的方法,在项目代码里面放入java代码
(1)服务端支持在请求的接口加入前缀/v0.1/dispatcher,
(2)在请求的接口头部加入Dispatcher头
例如:
这样就可以通过访问同域的服务端转发接口,实现跨域请求
解决es3保留字的问题
保留字如:
报错信息:
解决方法:
在webpack1的配置文件中加入配置:
1 2 3 4
| postLoaders: [{ test: /\.js$/, loaders: ['es3ify-loader'] }]
|
解决压缩后UglifyJsPlugin的问题
报错信息:
解决方法:
在配置里面加入screw_ie8,压缩的时候不要去掉ie8的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| new webpack.optimize.UglifyJsPlugin({ compress: { properties: false, warnings: false, screw_ie8: false }, output: { beautify: true, comments: false, quote_keys: true, screw_ie8: false }, mangle: { screw_ie8: false } })
|
上面改完后,继续报错
报错信息:
解决方法:
原先的webpack版本装的是1.15.2版本的,后面降为1.13.2版本就ok了
解决发生异常未捕获的错误
报错信息:
1
| 从core-js的_object-dp.js发出了错误:发生异常,未捕获
|
解决方法:
原先是使用babel-polyfill用来解决es6的api的兼容es5的问题。
现在因为core-js抛出错误了,所以直接废弃babel-polyfill
分别引入 (注: babel-polyfill是包含core-js和regnerator-runtime/runtime的)
1 2
| import 'core-js-ie8' import 'regenerator-runtime/runtime'
|
虽然core-js-ie8能完美解决这个问题,但是意外发现又引发了另外一个问题,core-js-ie8这个包实现的Object.assign在360和ie8+下是有问题的,于是乎就自己发了个npm包
core-js的issue也有相关的core-js
最终解决问题的代码变成:
1 2
| import 'core-js-for-ie8' import 'regenerator-runtime/runtime'
|
解决es5的API兼容的问题
解决方法:
在index.html的模板文件里面引入shim和sham:
1 2 3 4
| <!--[if lt IE 9]> <script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.7/es5-shim.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.7/es5-sham.min.js"></script> <![endif]-->
|
继续报错,苦逼了
报错信息:
问题原因:
登录页面上有个“是否显示密码”;可以切换修改, 代码如下:
1
| { isShowPassword ? <input type="text" /> : <input type="password" /> }
|
因为对React来说,有虚拟Dom,对比下dom结构都没有改变,只是type改变,不会重新渲染新的input元素,只会做个diff,把type替换掉,于是报这个错了。
解决方法:
React组件只要带了key属性,都会重新render的,不会做diff比较。
于是最终更正后的代码:
1
| { isShowPassword ? <input type="text" key="text" /> : <input type="password" key="password" />
|
解决ie8下的事件不生效的问题:
问题:
1 2 3 4 5 6 7 8 9
| <input type="text" onChange={this.onChange} onClick={this.onClick} onInput={this.onInput} onPropertyChange={this.onPropertyChange} />
<input type="checkbox" onChange={this.onChange} />
以上代码: (1)<input type="checkbox"/>元素上的onChange事件是生效的
(2)<input type="text"/>元素上只有OnClick事件是生效的,其他都不生效
|
解决方法:
1 2 3 4
| (1)保留onInut事件,其他事件不需要 <input type="text" onInput={this.onInput} /> (2)index.html的模板里面添加 <!--[if IE 8]><script src="//cdnjs.cloudflare.com/ajax/libs/ie8/0.3.2/ie8.js"></script><![endif]-->
|
解决Observable库的问题:
问题:
1 2 3
| import { Observable } from 'rx-lite'
这个库是支持ie10+的,并不支持ie8,9
|
解决方法:
1 2 3
| 很开心的是,有个兼容库rx-lite-compat是支持ie8+的,心里默默开心,于是代码变成如下:
import { Observable } from 'rx-lite-compat'
|
上面解决完Observable问题的后遗症
问题根源:
1
| 开心的太早了,引入rx-lite-compat又报另外一个问题了,这库里面的事件报错了,估计不兼容了
|
报错信息:
1
| 无法获取未定义或null 引用的属性“keyCode”
|
解决方法:
1 2 3
| 引入ie8.js文件后,一切正常,好开心了。
<!--[if IE 8]><script src="//cdnjs.cloudflare.com/ajax/libs/ie8/0.3.2/ie8.js"></script><![endif]-->
|
样式兼容问题:
(1) transform:translate(-50%)这个是不支持的
(2) 透明度也是不支持的,改造后的代码:
1 2 3 4 5 6
| if (browserLessIE8()) { document.getElementById('shadow').style.backgroundColor = '#000' document.getElementById('shadow').style.filter = 'progid:DXImageTransform.Microsoft.Alpha(Opacity = 80)' } else { document.getElementById('shadow').style.background = 'rgba(0,0,0,0.8)' }
|
解决promise的兼容问题:
1 2 3
| 可以引入es6-promise库支持ie8,使用如下:
require('es6-promise').polyfill()
|
解决fetch的兼容问题:
问题根源:
1 2 3
| (1)发现请求成功了,也引入了es6-promise了,但是还是无法进入then方法,原来是fetch不支持ie8
(2)isomorphic-fetch是不支持ie8的
|
解决方法:
1
| 替换成universal-fetch支持ie8的
|
console的polyfill:
问题原因:
1
| ie8没有打开控制台的时候,是没有console这个对象了,所以需要对console进行polyfil
|
解决方法:
1
| import 'console-polyfill'
|
解决ie8下React项目在某些时候a标签无法跳转路由的情况
问题原因:
1 2 3 4 5
| <a href="#/home"><button>我来试一试</button></a>
(1)以上代码在ie8下跳转失效
(2)因为a标签里面包了button标签,从而导致跳转失败了
|
解决方法:
1 2 3 4 5
| <a href="#/home"><div>我来试一试</div></a>
(1)把button标签改成div就可以了 (2)所以注意不要a标签里面包含button标签哦
|
lodash版本:
lodash 从4.0.0开始支持的环境有: Chrome 46-47, Firefox 42-43, IE 9-11, Edge 13, Safari 8-9, Node.js 0.10.x, 0.12.x, 4.x, & 5.x, & PhantomJS 1.9.8。已不再支持IE6~IE8。
如果想兼容IE6~IE8,可以使用3.x版本。3.x版本支持的环境有:
Chrome 43-44, Firefox 38-39, IE 6-11, MS Edge, Safari 5-8, ChakraNode 0.12.2, io.js 2.5.0, Node.js 0.8.28, 0.10.40, & 0.12.7, PhantomJS 1.9.8, RingoJS 0.11, & Rhino1.7.6
lodash 3.x 版本没有直接提供可用的js, 需要手动构建。
1 2 3 4 5
| 安装 npm i -g lodash-cli@3.10.1 安装完成后执行 lodash compat 会输出兼容IE6~IE8的版本lodash.custom.js及lodash.custom.min.js
|
lodash 3.10.1版本文档地址: https://lodash.com/docs/3.10.1#template
es6对象中使用取值器和赋值器报错
报错信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| SCRIPT1003: 缺少 ':'
问题定位到:
var Auth = {
__access_token: null,
get accessToken() { return this.__access_token; },
set accessToken(val) { this.__access_token = val; },
__mac_key: null,
get macKey() { return this.__mac_key; },
set macKey(val) { this.__mac_key = val; } } get set后面是空格,缺少':'
|
问题原因:
1
| babel转化的时候没有加入插件babel-plugin-transform-es5-property-mutators
|
解决方法:
1 2 3 4 5
| .babelrc里面引入这个插件:
{ "plugins": ["transform-es5-property-mutators"] }
|
上面那个问题引发了另外一个问题:
上面的es6的get和set的代码,babel编译后的代码也会有get和set属性,编译后如下:
1 2 3 4 5 6 7 8
| { key: 'accessToken', get: function get() { return this.__access_token; }, set: function set(val) { this.__access_token = val; }
|
1 2 3
| export { default as request } from './request' export { default as RBAC } from './rbac' export { default as RBACAdapt } from './rbac/instance'
|
上面这个代码也是类似的,bable编译后的代码也会有get和set属性如下:
1 2 3 4 5 6
| Object.defineProperty(exports, 'RBACAdapt', { enumerable: true, get: function get() { return _interopRequireDefault(_instance)['default']; } });
|
问题原因:
1 2
| 1. ie8不支持设置访问器属性,即便是引了es5-shim; 2. Babel 会把export xxx from ‘xx’ 语法转码为访问器属性设置的exports对象。
|
解决方法:
- 代码里面不要使用get,set
- export { default as request } from ‘./request’类似这样的写法要分两句写:
1 2 3
| import request from './request'
export request
|
babel插件和webpack的兼容ie8的loader可以等价:
1 2 3 4 5 6 7 8 9 10
| es3ify-loader相当于babelrc里面的两个插件 transform-es3-property-literals transform-es3-member-expression-literal
webpack里面要是有用es3ify-loader,在.babelrc里面就不用配置 transform-es3-property-literals transform-es3-member-expression-literal 这两个插件了
反之亦然
|
本地调试
我们开发的组件,都是通过iframe的方式被业务系统所引用。由于在IE下,iframe模拟接入,存在同源限制(即iframe的src地址不能和浏览器里的url地址相同)。
因此,在IE下调试,需要解决同源问题。我们使用的方式是,通过修改etc\hosts文件,将同一本地IP地址映射到不同的域名下,保证本地地址在IE下可调试。
要支持ie8,注意的几个点:
1.webpack要用1.x,2.x不支持ie8了
2.源码中不要使用es6的get和set访问器
3.不要写成export x from ‘x’,要分两步走
4.react要用0.14.x版本
参考资料:
Webpack-IE低版本兼容指南
IE8下使用React开发总结
defineProperty
ie8.js
Exception thrown and not caught
Make your React app work in IE8
让Webpack+Babel支持IE8
alibaba的兼容ie8的项目