React 兼容 IE 之路

·  阅读 5081

写在前面

近期一个开发好的 React 项目要重新兼容 IE 浏览器,要求 IE 最低兼容版本是 10。作为一个兼容小白,无比战战兢兢。现将兼容取经途中遇到的问题总结一下,写给大家共享一下,希望对看到的小伙伴们有帮助,也欢迎大家提建议呢。

本项目的基础配置版本如下:

"react": "^16.5.2",
"react-dom": "^16.5.2",
复制代码

IE10 兼容非样式篇

✅页面报错:Set/Map 未定义问题

这个问题在React官网JS环境要求里面有提到,

React 16 依赖集合类型 Map 和 Set 。如果你要支持无法原生提供这些能力(例如 IE < 11)或实现不规范(例如 IE 11)的旧浏览器与设备,考虑在你的应用库中包含一个全局的 polyfill ,例如 core-js 或 babel-polyfill 。
复制代码

给出的解决方案就是入口文件中提前引入指定的 polyfill:

import 'core-js/es/map';
import 'core-js/es/set';

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.getElementById('root')
);
复制代码

✅页面报错:Object.setPrototypeOf 未定义问题

是 Object.setPrototypeOf 的问题,Object.setPrototypeOf 说是支持了 IE9-11, 实际在源码里只实现了 11+。具体可参考 github.com/paulmillr/e…

  1. 可以自己在Object上面注册一个 setPrototypeOf api方法来解决。
(function() {
  Object.setPrototypeOf = Object.setPrototypeOf || ({__proto__: []} instanceof Array ? setProtoOf : mixinProperties);

  function setProtoOf(obj, proto) {
    obj.__proto__ = proto;
    return obj;
  }

  function mixinProperties(obj, proto) {
    for (const prop in proto) {
      if (!obj.hasOwnProperty(prop)) {
        obj[prop] = proto[prop];
      }
    }
    return obj;
  }
})();
复制代码
  1. 可以引入 setPrototypeOf 第三方包,然后在入口文件中编写:
Object.setPrototypeOf = require('setprototypeof');
复制代码

✅IE10中页面报错:Objects are not valid as a React child (found: object with keys {?typeof, type, key, ref, props, _owner}). app不渲染。

原因:babel-polyfill、react、reactdom引入顺序问题。

  1. 解决:可以在webpack entry配置中强制控制他们的打包引入顺序,引入顺序一定要 babel-polyfill 在最前面。
...
entry: {
    babelPolyfill: 'babel-polyfill',
    app: 'src/index.js'
}
...
plugins: [
    new HtmlWebpackPlugin({
        inject: true,
        filename: 'static/index.html',
        template: path.resolve(process.cwd(), 'public', 'index.html'),
        chunks: ['polyfill', 'app'], // 在此控制他们的引入加载顺序
        chunksSortMode: 'manual',
    }),
]
复制代码

🤔🤔🤔🤔小白的疑问️:直接在入口文件中控制他们三个的引入顺序,不知道为什么不行,暂时留一个疑问,有知道的欢迎留言哈。

  1. 解决:可以在模版文件index.html 使用 CDN 的方法优先加载 babel-polyfill。
<script src="https://cdn.bootcss.com/babel-polyfill/6.26.0/polyfill.min.js"></script>
复制代码

🍒小提示:这里6.26.0是我项目中使用的babel-polyfill版本号,你们使用的可以按你们的版本号来哈

✅页面报错:无法获取未定义或null引用的属性“xxx”

原因:使用了mou对象上面的xxx属性,而这个对象没有拿到,对象是null。

解决:找到报错的地方,进行对象的判断处理,对象拿到值才会使用对象上面的xxx属性。

✅页面报错: only one instance of babel-polyfill is allowed

原因:webpack入口引入了多个babel-polyfill。

此处注意:如果 webpackp entry 和入口文件中都引入了 babel-polyfill,这个webpack打包的时候会自行忽略页面引入的那个,所以不会造成上面这个报错。

解决:检查webpack entry 的配置,去掉多余的 babel-polyfill引入。

✅react-intl 在 IE10 的不兼容问题,页面报错:[react Intl] The Intl APIs must be available in the runtime.

原因:react-intl是基于Intl进行封装的,而这些 api 在 IE10 中存在兼容问题。

解决:加 Intl 的 polyfill 包解决

<!--在入口文件中引入包,会导致webpack打包的文件比较大-->
import 'intl';
import 'intl/locale-data/jsonp/en';
import 'date-time-format-timezone';

<!--第二种:在webpack entry 中加入-->
entry = {
  polyfill: [
    path.join(process.cwd(), './node_modules/intl'),
    path.join(process.cwd(), './node_modules/intl/locale-data/jsonp/en'),
    path.join(process.cwd(), './node_modules/date-time-format-timezone'),
  ],
}
复制代码

参考:github.com/formatjs/re…

IE10 兼容样式篇

✅IE10 文字超长换行问题

/*word-break: break-word;*/ IE10中不支持
word-break: break-all;
复制代码

✅IE10 部分支持flex布局 ,不要写行内flex样式,最好独写出来

小记

最后记两个兼容过程中遇到的小问题:

✅下载问题

下载功能,前端经常会通过创建一个临时a标签并点击实现下载功能:

const a = document.createElement('a');
a.href = getUrl;
a.click();
// 火狐浏览器
// document.body.appendChild(a);
// a.click();
// document.body.removeChild(a);
复制代码

这时候如果是火狐浏览器,如果这个a标签没有添加到 dom 中,就会出现点击不上的问题,需要把 a 标签 append 到 body 上面。

✅IE 接口请求使用缓存问题

这个问题的发现,是因为我们当时系统登陆进去之后,总是马上弹出来。但是开着F12,又每次都能登陆进去。

这个问题很神奇,从最初的以为登陆接口cookei信息没带上,到后来怀疑 fetch请求的兼容包whatwg-fetch没引入,一直到最后前后端一起定位发现是,登陆使用了上一次登陆的缓存返回,导致返回的还是未登陆状态。

结论:IE 里面接口请求如果没有控制缓存使用方式,浏览器可能会自动使用缓存,返回上一次请求的结果,但是如果打开F12调试,开启调试者模式,IE就不会使用缓存,注意此问题,如果不想接口请求使用缓存,可以用加时间戳的方法解决。

分类:
前端
标签: