Skip to main content

One post tagged with "ie11"

View All Tags

· 13 min read
石晓波

ie兼容一直是个让人头疼的问题,很多新的API都不能运行在IE上,但是IE有时候又不得不去兼容他,接下里是解决ie兼容的踩坑过程。

本文使用的是wepback5去编译工程代码,如果是单纯的使用babel或者typescript编译器也可以参考后续文章对应的部分。

webpack5编译分为两部分,webpack5的 运行时代码和我们编写的业务代码,webpack5自身可以通过配置将自身的运行时代码编译成可兼容ie运行的代码,但是业务代码webpack5只会处理模块和依赖关系代码并将业务代码本身不会优化为兼容性较高的版本,并且不能直接给ie运行,这时候就需要我们去配置loader去解决代码的编译问题了。

webpack5运行时代码兼容

默认情况下不配置webpack,生成的runtime使用的箭头函数,如果要讲webpack5运行时代码编译为兼容低版本的代码,需要配置target,target可配置为['web', 'es5'],target默认使用的是package.json中的browserslinst可以配置browserslists属性也能达到目的

babel编译

使用babel编译源代码需要配置babel-loader,babel可以将新版本的JavaScript编译为稳定版本以提高兼容性。首先需要安装babel-loader

npm install babel-loader @babel/core @babel/preset-env -D

webpack需要使用babel-loader来调用babel,而@babel/core和@babel/preset-env是核心插件集

danger

babel配置文件需要命名为.babelrc,千万不要命名为babel.config.json,后续配置babel-runtime会报错,模块解析失败的问题,如果添加了模块解析的plugin同样不能实现根据需要引入babel-runtime

首先配置webpack,使用babel-loader解析js文件,babel的配置单独配置到.babelrc中

webpack.common.js
            {
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: [
{
loader: 'babel-loader',
}
]
}
{
"presets": [
[
"@babel/preset-env"
]
]
}

编写一些测试代码

const a = 1;
let b = 2;


class myClass{

}

const mine = new myClass();
console.log('mine',mine);

export const aFunc = () => {
return a + b;
}

export const arrInclude = () => {
const arr = [1, 2, 3, 4];
return arr.includes(a)
}

export const promiseFunc = () => {
return new Promise((resolve) => {
setTimeout(() => {
document.body.style.backgroundColor = 'green';
}, 3000)
})
}

const asyncFunc = async () => {
await promiseFunc();
console.log('success!!!!!!!!!!')
}



console.log('aFunc', aFunc())

console.log('arrInclude', arrInclude())

document.body.style.backgroundColor = 'red';

asyncFunc();

启动编译,查看bundle.js文件发现babel默认只对Syntax做转换,对于Promise它未转换,可以使用@babel/runtime@babel/polyfill

info

babel默认只针对Syntal做转换,例如箭头函数、es6、class语法糖等,而自带的API需要原生内置的方法需要透过polyfill才能在浏览器正常运行。

@babel/runtime使用

@babel/runtime是由Babel提供的polyfill套件,由core.js和regenerator组成,core.js是用于JavaScript的组合标准化库,它包含了各种版本的polyfill的实现,而regenerator是来自facebook的一个函数库,主要用户实现generator/yeild,async.await等特性。

安装@babel/runtime

yarn add @babel/runtime -D

使用@babel-runtime还需要安装依赖的@babel/plugin-transform-runtime

yarn add @babel/plugin-transform-runtime -D

修改.babelrc

{
"presets": ["@babel/preset-env"],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": false
}
]
]
}

从编译结果来看Class语法糖全局污染的问题已经结果,通过@babel/plugin-transform-runtime插件,它会帮助分析是否有polyfill的需求,并自动通过require的方式向@babel-runtime拿去polyfill,简单来说@babe/runtime提供了丰富的polyfill,开发者可自行使用require来导入自己需要的polyfill,但是谈过与繁琐和麻烦所以需要使用@babel/plugin-transform-runtime自动分析添加@babel/runtime中的polyfill,仔细观察编译结果来看Promise includes,接下来解锁@babel/runtime的所有功能配置。

corejs选项安装命令
falseyarn add @babel/runtime
2yarn add @babel/runtime-corejs2
3yarn add @babel/runtime-corejs3

修改babelrc文件

{
"presets": ["@babel/preset-env"],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": 2
}
]
]
}

查看编译结果

__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "aFunc": function() { return /* binding */ aFunc; },
/* harmony export */ "arrInclude": function() { return /* binding */ arrInclude; },
/* harmony export */ "promiseFunc": function() { return /* binding */ promiseFunc; }
/* harmony export */ });
/* harmony import */ var _babel_runtime_corejs2_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime-corejs2/helpers/asyncToGenerator */ "./node_modules/@babel/runtime-corejs2/helpers/esm/asyncToGenerator.js");
/* harmony import */ var _babel_runtime_corejs2_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @babel/runtime-corejs2/helpers/classCallCheck */ "./node_modules/@babel/runtime-corejs2/helpers/esm/classCallCheck.js");
/* harmony import */ var _babel_runtime_corejs2_regenerator__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @babel/runtime-corejs2/regenerator */ "./node_modules/@babel/runtime-corejs2/regenerator/index.js");
/* harmony import */ var _babel_runtime_corejs2_regenerator__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_corejs2_regenerator__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _babel_runtime_corejs2_core_js_promise__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @babel/runtime-corejs2/core-js/promise */ "./node_modules/@babel/runtime-corejs2/core-js/promise.js");
/* harmony import */ var _babel_runtime_corejs2_core_js_promise__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_corejs2_core_js_promise__WEBPACK_IMPORTED_MODULE_3__);




// import "core-js/stable";
// import "regenerator-runtime/runtime";
var a = 1;
var b = 2;

var myClass = function myClass() {
(0,_babel_runtime_corejs2_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_1__["default"])(this, myClass);
};

var mine = new myClass();
console.log('mine', mine);
var aFunc = function aFunc() {
return a + b;
};
var arrInclude = function arrInclude() {
var arr = [1, 2, 3, 4];
return arr.includes(a);
};
var promiseFunc = function promiseFunc() {
return new (_babel_runtime_corejs2_core_js_promise__WEBPACK_IMPORTED_MODULE_3___default())(function (resolve) {
setTimeout(function () {
document.body.style.backgroundColor = 'green';
}, 3000);
});
};

var asyncFunc = /*#__PURE__*/function () {
var _ref = (0,_babel_runtime_corejs2_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__["default"])( /*#__PURE__*/_babel_runtime_corejs2_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee() {
return _babel_runtime_corejs2_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return promiseFunc();

case 2:
console.log('success!!!!!!!!!!');

case 3:
case "end":
return _context.stop();
}
}
}, _callee);
}));

return function asyncFunc() {
return _ref.apply(this, arguments);
};
}();

console.log('aFunc', aFunc());
console.log('arrInclude', arrInclude());
document.body.style.backgroundColor = 'red';
asyncFunc();

修改babelrc文件

{
"presets": ["@babel/preset-env"],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"corejs": 3
}
]
]
}

编译结果如下

__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "aFunc": function() { return /* binding */ aFunc; },
/* harmony export */ "arrInclude": function() { return /* binding */ arrInclude; },
/* harmony export */ "promiseFunc": function() { return /* binding */ promiseFunc; }
/* harmony export */ });
/* harmony import */ var _babel_runtime_corejs3_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime-corejs3/helpers/asyncToGenerator */ "./node_modules/@babel/runtime-corejs3/helpers/esm/asyncToGenerator.js");
/* harmony import */ var _babel_runtime_corejs3_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @babel/runtime-corejs3/helpers/classCallCheck */ "./node_modules/@babel/runtime-corejs3/helpers/esm/classCallCheck.js");
/* harmony import */ var _babel_runtime_corejs3_regenerator__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @babel/runtime-corejs3/regenerator */ "./node_modules/@babel/runtime-corejs3/regenerator/index.js");
/* harmony import */ var _babel_runtime_corejs3_regenerator__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_corejs3_regenerator__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _babel_runtime_corejs3_core_js_stable_instance_includes__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @babel/runtime-corejs3/core-js-stable/instance/includes */ "./node_modules/@babel/runtime-corejs3/core-js-stable/instance/includes.js");
/* harmony import */ var _babel_runtime_corejs3_core_js_stable_instance_includes__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_corejs3_core_js_stable_instance_includes__WEBPACK_IMPORTED_MODULE_3__);
/* harmony import */ var _babel_runtime_corejs3_core_js_stable_promise__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @babel/runtime-corejs3/core-js-stable/promise */ "./node_modules/@babel/runtime-corejs3/core-js-stable/promise.js");
/* harmony import */ var _babel_runtime_corejs3_core_js_stable_promise__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_corejs3_core_js_stable_promise__WEBPACK_IMPORTED_MODULE_4__);
/* harmony import */ var _babel_runtime_corejs3_core_js_stable_set_timeout__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @babel/runtime-corejs3/core-js-stable/set-timeout */ "./node_modules/@babel/runtime-corejs3/core-js-stable/set-timeout.js");
/* harmony import */ var _babel_runtime_corejs3_core_js_stable_set_timeout__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_corejs3_core_js_stable_set_timeout__WEBPACK_IMPORTED_MODULE_5__);






// import "core-js/stable";
// import "regenerator-runtime/runtime";
var a = 1;
var b = 2;

var myClass = function myClass() {
(0,_babel_runtime_corejs3_helpers_classCallCheck__WEBPACK_IMPORTED_MODULE_1__["default"])(this, myClass);
};

var mine = new myClass();
console.log('mine', mine);
var aFunc = function aFunc() {
return a + b;
};
var arrInclude = function arrInclude() {
var arr = [1, 2, 3, 4];
return _babel_runtime_corejs3_core_js_stable_instance_includes__WEBPACK_IMPORTED_MODULE_3___default()(arr).call(arr, a);
};
var promiseFunc = function promiseFunc() {
return new (_babel_runtime_corejs3_core_js_stable_promise__WEBPACK_IMPORTED_MODULE_4___default())(function (resolve) {
_babel_runtime_corejs3_core_js_stable_set_timeout__WEBPACK_IMPORTED_MODULE_5___default()(function () {
document.body.style.backgroundColor = 'green';
}, 3000);
});
};

var asyncFunc = /*#__PURE__*/function () {
var _ref = (0,_babel_runtime_corejs3_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__["default"])( /*#__PURE__*/_babel_runtime_corejs3_regenerator__WEBPACK_IMPORTED_MODULE_2___default().mark(function _callee() {
return _babel_runtime_corejs3_regenerator__WEBPACK_IMPORTED_MODULE_2___default().wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return promiseFunc();

case 2:
console.log('success!!!!!!!!!!');

case 3:
case "end":
return _context.stop();
}
}
}, _callee);
}));

return function asyncFunc() {
return _ref.apply(this, arguments);
};
}();

console.log('aFunc', aFunc());
console.log('arrInclude', arrInclude());
document.body.style.backgroundColor = 'red';
asyncFunc();

从上面的结果可知corejs2版本主要针对底层API做编译如:Promise、Fetch等等;corejs3版本主要针对底层API和一些方法如Array.prototype.filter、Array.prototype.includes,简单来说如果要彻底解决兼容性的问题就需要使用corejs3的版本。

note

使用@babel/runtime能够在不污染全局环境的情况下提供相对的的polyfil,拥有自动识别的功能,一般情况下编译出来的文件要比使用@babel/polyfill要小,适合开发组件库或者对环境较为严格的方案。

@babel/polyfill

当babel版本小于7.0.4可以使用@babel/polyfill,最新版本已经废弃,@babel/polyfill是由stable版本的core-js和regenerator-runtime组成,可以直接下在这两个组件库当做@babel/polyfill来使用官方也推荐这样使用。还需要注意的是regenerator-runtime为@babel/runtime的相依套件,检查是否正确安装

修改.babelrc如下所示

{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": false,
"corejs": 3 // 当前 core-js 版本
}
]
]
}

使用同样的JavaScript源码进行测试结果发现和单纯使用babel一样,只针对语法(Syntax)做编译,那是因为尚未开启polyfill的功能,可通过useBuniltIns来修改模式,模式可选为false usage entry,如果使用usage模式结果和@babel/runtime-corejs3一样,自动识别需要require的语法,将兼容性问题彻底解决,不同的地方在于@babel/runtime在不污染全局变量环境的情况下提供了polyfill,而@babel/polyfill则是将需要兼容的新语法挂在到全局对象上,这样的做法造成了所谓的全局污染,如果使用entry模式。就比较简单了,没有使用任何主动识别,直接将整个关于ES环境代码挂在到全局对象,确保浏览器可以兼容所有的新特性,但这样缺陷也显而易见,会导致源代码体积变大,所以为什么要有entry模式,其实最主要的原因是babel默认不会检测第三方的依赖库,所以使用usage选项时可能会因为第三方库的源代码问题导致不能兼容,这事就有了使用entry模式的必要。

note

使用entry选项记得在代码的最前面添加import core-js/stableregenerator-runtime/runtime组件库

@babel/polyfill具有一次性导入或自动和别导入polyfill的功能,使用挂在全局对象的方法兼容新特性,适合开发应用。不太适合开发组件库或者第三方工具包,存在污染全局环境的问题。

小总结

  1. Babel版本<7.4.0>
    • 开发组件库、工具包选择@babel/runtime
    • 开发应用类项目选择@babel/polyfill
  2. Babel版本>=7.4.0
    • 配置较简单,会污染全局环境,选择@babel/polyfill
    • 配置较繁琐,不会污染全局环境选择@babel/runtime

ts-loader

首先修改webpack的配置文件

webpack.common.js
  module: {
rules: [{
test: /\.ts$/,
use: ['ts-loader'],
}],
},

修改tsconfig.json

{
"compilerOptions":{
"target": "es3",
"strict":true,
"lib": [
"dom",
"es2015",
"scripthost"
],
}
}

async await为例,首先需要安装Promise的polyfill

yarn add es6-promise -D

源代码最前面需要手动引入polyfill

import "es6-promise/auto";
...

编译结果中添加了promise的polyfill,所有的类似需要兼容的API都需要添加对应的polyfill,比较繁琐。

总结

Babel的生态和编译提供的polyfill更为丰富,配置更方便,推荐使用Babel,后续文章中也会完成在React中使用typescript并且使用babel编译源代码。