0%

一次学习 Babel 和 Webpack 兼容 IE8 的过程

公司的项目都是 JSP 项目,而我自己喜欢使用 ES6 的一些语法,但是 ES6 的很多方法在 IE8 中不支持,查了资料,可以用 Babel 进行转码。

然而兼容 IE 会碰到很多问题:

  1. IE8 对 ES5 支持不好,一些代码得编译成 ES3。比如转换模块调用时候会出现 exports.default , 而 default 是 IE8 中的关键字会异常。
  2. 缺少很多 API 如: promiseObject-assign 等。
  3. IE8 不完全支持 Object.defineProperty

Babel

Bable 初次使用

新建一个文件夹,初始化,装依赖

1
2
3
yarn init
npm install --save-dev @babel/core @babel/cli @babel/preset-env
npm install --save @babel/polyfill

新建文件 babel.config.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"presets": [
[
"@babel/env",
{
"targets": {
"ie": "8"
},
"useBuiltIns": "usage",
"corejs": "3.6.4"
}
]
]
}

useBuiltIns: 引入垫片的方式,一个是全局引入垫片,另一个是按需引入垫片,值可以是 entryusage ,假如是entry会在入口处把所有 ie8 以上浏览器不支持 api 的 polyfill 引入进来

useBuiltIns

运行命令行将 src 文件夹的代码转换到 lib文件夹

1
./node_modules/.bin/babel src --out-dir lib

也可以将 ./node_modules/.bin/babel 换成 npx babel 直接运行

测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Hello {
static world() {
console.log("Hello, World!");
}
}
Hello.world();
let a = (data) => {
setTimeout(() => {
const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };

const returnedTarget = Object.assign(target, source);

console.log(target);

console.log(returnedTarget);
}, 500);
};
a("哈哈");

输出结果:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
"use strict";

require("core-js/modules/es.object.assign");

require("core-js/modules/es.object.define-property");

require("core-js/modules/web.timers");

function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}

function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}

function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}

var Hello = /*#__PURE__*/ (function () {
function Hello() {
_classCallCheck(this, Hello);
}

_createClass(Hello, null, [
{
key: "world",
value: function world() {
console.log("Hello, World!");
},
},
]);

return Hello;
})();

Hello.world();

var a = function a(data) {
setTimeout(function () {
var target = {
a: 1,
b: 2,
};
var source = {
b: 4,
c: 5,
};
var returnedTarget = Object.assign(target, source);
console.log(target);
console.log(returnedTarget);
}, 500);
};

这时候我发现了个问题,代码里面有 require("core-js/modules/es.object.assign"); 在浏览器中无法运行。

查资料得知可能要用 webpack 或者 gulp 打包一下。

继续了解 Bable 插件

依稀记着 babel runtime, webpack 的 bable-loader,还有 plugin-transform-runtime 可能可以给我带来一些帮助

@babel/plugin-transform-runtime

@babel/plugin-transform-runtime

一个插件,可重新使用 Babel 注入的帮助程序代码以节省代码大小。

1
2
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime

配置文件加

1
2
3
{
"plugins": ["@babel/plugin-transform-runtime"]
}

这次,代码发生了一些的变化,具体细节就不说了,因为我也不是很懂说不清

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

require("core-js/modules/es.object.assign");

require("core-js/modules/web.timers");

var _classCallCheck2 = _interopRequireDefault(
require("@babel/runtime/helpers/classCallCheck")
);

var _createClass2 = _interopRequireDefault(
require("@babel/runtime/helpers/createClass")
);

var Hello = /*#__PURE__*/ (function () {
function Hello() {
(0, _classCallCheck2["default"])(this, Hello);
}

(0, _createClass2["default"])(Hello, null, [
{
key: "world",
value: function world() {
console.log("Hello, World!");
},
},
]);
return Hello;
})();

Hello.world();

var a = function a(data) {
setTimeout(function () {
var target = {
a: 1,
b: 2,
};
var source = {
b: 4,
c: 5,
};
var returnedTarget = Object.assign(target, source);
console.log(target);
console.log(returnedTarget);
}, 500);
};

a("haha");

@babel/plugin-transform-object-assign

使用 @babel/plugin-transform-object-assign 测试

@babel/plugin-transform-object-assign

发现 Object.assign 没有了

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

require("core-js/modules/web.timers");

var _extends2 = _interopRequireDefault(
require("@babel/runtime/helpers/extends")
);

var _classCallCheck2 = _interopRequireDefault(
require("@babel/runtime/helpers/classCallCheck")
);

var _createClass2 = _interopRequireDefault(
require("@babel/runtime/helpers/createClass")
);

var Hello = /*#__PURE__*/ (function () {
function Hello() {
(0, _classCallCheck2["default"])(this, Hello);
}

(0, _createClass2["default"])(Hello, null, [
{
key: "world",
value: function world() {
console.log("Hello, World!");
},
},
]);
return Hello;
})();

Hello.world();

var a = function a(data) {
setTimeout(function () {
var target = {
a: 1,
b: 2,
};
var source = {
b: 4,
c: 5,
};
var returnedTarget = (0, _extends2["default"])(target, source);
console.log(target);
console.log(returnedTarget);
}, 500);
};

Webpack

看了 webpack 入门 这篇文章

运行 npm install webpack webpack-cli --save-dev 命令安装 webpack 依赖

创建 webpack 配置文件 webpack.config.js

1
2
3
4
5
6
7
8
const path = require("path");
module.exports = {
entry: "./src/index.js",
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist"),
},
};

package.json 加入下面代码,用来运行 webpack 脚本

1
2
3
4
"scripts": {
"build": "webpack",
"dev": "webpack"
},

使用下面的命令转换

1
2
3
npm run build
#or
yarn build

踩坑

缺少标识符

如图,一路 debug,发现 default 是 ie8 的关键字,不能使用。

网上查了一圈,加自己动手实践,方法有 2 个,最方便的是 webpack 的配置里加下面的代码,其中 UglifyJsPluginuglifyOptions 内 的 ie8 参数默认是 false,改成 true 就可以了。

安装依赖 uglifyjs-webpack-plugin

1
npm i -D uglifyjs-webpack-plugin

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const path = require("path");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist"),
},
optimization: {
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
ie8: true,
},
}),
],
},
};

另一个是用 es3ify-loader 可以实现下面的效果

1
2
3
4
5
function(t) { return t.default; } // 编译前
function(t) { return t["default"]; } // 编译后

{ catchfunction(t){} } // 编译前
{ "catch"function(t){} } // 编译后

配置文件如下

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
28
const path = require("path");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist"),
},
optimization: {
minimize: false,
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
ie8: true,
},
}),
],
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: ["es3ify-loader"],
},
],
},
};

还有个 es5-shim 的垫片,不过我没太看,没试验

对象不支持该操作

查了一下,是 object.defineProperty 在 ie8 中存在,但是只用用于操作 dom 对象

IE8 这波操作秀的我脑瓜子嗡嗡的,又想起来 vue 不支持 IE8 好像就是这个原因。

解决方法很多,比如加垫片。

运行命令行装依赖

1
2
npm i object-defineproperty-ie8
npm i core-js
1
2
3
require("object-defineproperty-ie8");
// 或者按需引入 core-js 的 define-property
require("core-js/features/object/define-property");

object-defineproperty-ie8 是司徒正美大佬写的,你们可以看看他的 github: object-defineproperty-ie8

另外就是装这两个插件也可以在一定程度上解决问题

最终代码

package.json

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
28
29
30
31
{
"name": "1",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"build": "webpack",
"dev": "webpack"
},
"devDependencies": {
"@babel/cli": "^7.11.6",
"@babel/core": "^7.11.6",
"@babel/plugin-transform-modules-commonjs": "^7.10.4",
"@babel/plugin-transform-object-assign": "^7.10.4",
"@babel/plugin-transform-runtime": "^7.11.5",
"@babel/preset-env": "^7.11.5",
"@babel/register": "^7.11.5",
"babel-loader": "^8.1.0",
"babel-plugin-transform-es2015-modules-simple-commonjs": "^0.3.0",
"uglifyjs-webpack-plugin": "^2.2.0",
"webpack": "^4.44.2",
"webpack-cli": "^3.3.12"
},
"dependencies": {
"@babel/polyfill": "^7.11.5",
"@babel/runtime": "^7.11.2",
"core-js": "^3.6.5",
"es3ify-loader": "^0.2.0",
"object-defineproperty-ie8": "^1.0.1"
}
}

webpack.config.js

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
const path = require("path");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
filename: "main.js",
path: path.resolve(__dirname, "dist"),
},
optimization: {
minimize: false,
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
ie8: true,
},
}),
],
},
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: "babel-loader",
options: {
presets: [
[
"@babel/env",
{
targets: {
ie: "8",
},
useBuiltIns: "entry",
corejs: "3.6.4",
debug: true,
},
],
],
plugins: [
"@babel/plugin-transform-runtime",
"@babel/plugin-transform-object-assign",
"@babel/plugin-transform-modules-commonjs",
"transform-es2015-modules-simple-commonjs",
],
},
},
},
{
test: /\.js$/,
exclude: /node_modules/,
use: ["es3ify-loader"],
},
],
},
};

babel.config.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"presets": [
[
"@babel/env",
{
"targets": {
"edge": "17",
"ie": "8",
"firefox": "60",
"chrome": "67",
"safari": "11.1"
},
"useBuiltIns": "usage",
"corejs": "3.6.4"
}
]
],
"plugins": [
"@babel/plugin-transform-runtime",
"@babel/plugin-transform-object-assign"
]
}

相关资料

参考文章

感谢这些前辈帮忙踩坑,让我有资料可查

资源

司徒正美大佬的垫片:object-defineproperty-ie8

学习的时候搜索的关键词

  • babel 基础
  • babel example 找相关例子
  • polyfill 垫片
  • browserslist 浏览器支持
  • stage-0 stage-1 stage-2 stage-3 都是做什么的
  • babel-cli 怎么用
  • babel-core 怎么用
  • webpack babel-loader 怎么配置

Github 地址

webpack-ie8-configuration

实战

有个项目,以前我做过 babel 转 es6 加垫片,然后我忘了,我再实验一下

selection

就是这个,这个是个非常好使的拖拽多选组件,越用越香,越用越好使,然而不支持 IE8。

后面有时间再弄,先空着

总结

学习了 webpack 和 babel ,收获蛮多的。