前端开发最佳实践之React兼容IE8+的踩坑总结
- 1. ie8,ie9首先考虑跨域问题
- 2. 解决es3保留字的问题
- 3. 解决压缩后UglifyJsPlugin的问题
- 4. 上面改完后,继续报错
- 5. 解决发生异常未捕获的错误
- 6. 解决es5的API兼容的问题
- 7. 继续报错,苦逼了
- 8. 解决ie8下的事件不生效的问题:
- 9. 解决Observable库的问题:
- 10. 上面解决完Observable问题的后遗症
- 11. 样式兼容问题:
- 12. 解决promise的兼容问题:
- 13. 解决fetch的兼容问题:
- 14. console的polyfill:
- 15. 解决ie8下React项目在某些时候a标签无法跳转路由的情况
最近基于React开发的组件需要兼容IE8+。因此,总结了开发过程中的一些坑。
ie8,ie9首先考虑跨域问题
主要是采用服务端代理的方法,在项目代码里面放入java代码
(1)服务端支持在请求的接口加入前缀/v0.1/dispatcher,
(2)在请求的接口头部加入Dispatcher头
例如:
这样就可以通过访问同域的服务端转发接口,实现跨域请求
解决es3保留字的问题
保留字如:
1 | default, class等 |
报错信息:
1 | 缺少标识符 |
解决方法:
在webpack1的配置文件中加入配置:
1 | postLoaders: [{ |
解决压缩后UglifyJsPlugin的问题
报错信息:
1 | 缺少标识符 |
解决方法:
在配置里面加入screw_ie8,压缩的时候不要去掉ie8的代码
1 | new webpack.optimize.UglifyJsPlugin({ |
上面改完后,继续报错
报错信息:
解决方法:
原先的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 | import 'core-js-ie8' |
虽然core-js-ie8能完美解决这个问题,但是意外发现又引发了另外一个问题,core-js-ie8这个包实现的Object.assign在360和ie8+下是有问题的,于是乎就自己发了个npm包
core-js的issue也有相关的core-js
最终解决问题的代码变成:
1 | import 'core-js-for-ie8' |
解决es5的API兼容的问题
解决方法:
在index.html的模板文件里面引入shim和sham:
1 | <!--[if lt IE 9]> |
继续报错,苦逼了
报错信息:
1 | 无法修改属性type,不允许此命令 |
问题原因:
登录页面上有个“是否显示密码”;可以切换修改, 代码如下:
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 |
|
解决方法:
1 | (1)保留onInut事件,其他事件不需要 |
解决Observable库的问题:
问题:
1 | import { Observable } from 'rx-lite' |
解决方法:
1 | 很开心的是,有个兼容库rx-lite-compat是支持ie8+的,心里默默开心,于是代码变成如下: |
上面解决完Observable问题的后遗症
问题根源:
1 | 开心的太早了,引入rx-lite-compat又报另外一个问题了,这库里面的事件报错了,估计不兼容了 |
报错信息:
1 | 无法获取未定义或null 引用的属性“keyCode” |
解决方法:
1 | 引入ie8.js文件后,一切正常,好开心了。 |
样式兼容问题:
(1) transform:translate(-50%)这个是不支持的
(2) 透明度也是不支持的,改造后的代码:
1 | if (browserLessIE8()) { |
解决promise的兼容问题:
1 | 可以引入es6-promise库支持ie8,使用如下: |
解决fetch的兼容问题:
问题根源:
1 | (1)发现请求成功了,也引入了es6-promise了,但是还是无法进入then方法,原来是fetch不支持ie8 |
解决方法:
1 | 替换成universal-fetch支持ie8的 |
console的polyfill:
问题原因:
1 | ie8没有打开控制台的时候,是没有console这个对象了,所以需要对console进行polyfil |
解决方法:
1 | import 'console-polyfill' |
解决ie8下React项目在某些时候a标签无法跳转路由的情况
问题原因:
1 | <a href="#/home"><button>我来试一试</button></a> |
解决方法:
1 |
|
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 | 安装 |
lodash 3.10.1版本文档地址: https://lodash.com/docs/3.10.1#template
es6对象中使用取值器和赋值器报错
报错信息:
1 | SCRIPT1003: 缺少 ':' |
问题原因:
1 | babel转化的时候没有加入插件babel-plugin-transform-es5-property-mutators |
解决方法:
1 | .babelrc里面引入这个插件: |
上面那个问题引发了另外一个问题:
上面的es6的get和set的代码,babel编译后的代码也会有get和set属性,编译后如下:
1 | { |
1 | export { default as request } from './request' |
上面这个代码也是类似的,bable编译后的代码也会有get和set属性如下:
1 | Object.defineProperty(exports, 'RBACAdapt', { |
问题原因:
1 | 1. ie8不支持设置访问器属性,即便是引了es5-shim; |
解决方法:
- 代码里面不要使用get,set
- export { default as request } from ‘./request’类似这样的写法要分两句写:
1 | import request from './request' |
babel插件和webpack的兼容ie8的loader可以等价:
1 | es3ify-loader相当于babelrc里面的两个插件 |
本地调试
我们开发的组件,都是通过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版本
参考资料:
Exception thrown and not caught