Skip to content

Babel 和 Webpack 兼容 IE8 的实践

Published: at 04:48 PMSuggest Changes

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

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

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

Babel

Bable 初次使用

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

yarn init
yarn add @babel/core @babel/cli @babel/preset-env @babel/polyfill
yarn add core-js regenerator-runtime -D

# or
npm install --save-dev @babel/core @babel/cli @babel/preset-env
npm install --save @babel/polyfill core-js regenerator-runtime

新建文件 babel.config.json

{
  "presets": [
    [
      "@babel/env",
      {
        "targets": {
          "ie": "8"
        },
        "useBuiltIns": "usage",
        "corejs": "3.6.4"
      }
    ]
  ]
}

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

useBuiltIns

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

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

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

测试代码如下:

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("哈哈");

输出结果:

"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 注入的帮助程序代码以节省代码大小。

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

配置文件加

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

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

"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 没有了

"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

const path = require("path");
module.exports = {
  entry: "./src/index.js",
  output: {
    filename: "main.js",
    path: path.resolve(__dirname, "dist"),
  },
};

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

"scripts": {
  "build": "webpack",
  "dev": "webpack"
},

使用下面的命令转换

npm run build
#or
yarn build

踩坑

缺少标识符

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

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

安装依赖 uglifyjs-webpack-plugin

npm i -D uglifyjs-webpack-plugin

配置文件

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 可以实现下面的效果

function(t) { return t.default; } // 编译前
function(t) { return t["default"]; } // 编译后

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

配置文件如下

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 好像就是这个原因。

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

运行命令行装依赖

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

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

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

最终代码

package.json

{
  "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

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

{
  "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

学习的时候搜索的关键词

Github 地址

webpack-ie8-configuration

实战

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

selection

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

后面有时间再弄,先空着

总结

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


Previous Post
Webpack 取消对 require 处理的几种方式
Next Post
JavaScript 数组拍平方法