# Babel 优化

提示

本节内容紧接着上节 Babel 转化

如果你有仔细观察上节编译的产物,就可以发现很多当前浏览器能识别的 语法API,Bable 都统统做了转换,导致编译出来的产物很大(dist/library.js 大约 15827 行代码)。还有一些垫片直接重写了浏览器的原型链等,这可能会导致用户代码运行后不是预期的结果。有些辅助函数还在各个文件重复出现,也会导致编译出来的产物很大。接下来的内容是介绍如何依据浏览器来使用最少的代码进行兼容,并且避免全局污染。

# 根据浏览器按需引用

查看产物

可以在产物 dist/library.js 搜索 .prototype.,就可以发现有 Array.prototype.forEachArray.prototype.mapArray.prototype.filterArray.prototype.someArray.prototype.everyArray.prototype.findArray.prototype.findIndexArray.prototype.filterReject 等很多 API 垫片。

思考: 查看上节编译出来的产物,可以发现添加了很多新 API 垫片。如果想该库只支持 Chrome,不支持 IE。那还需要添加这么多 API 垫片吗?例如现在的 Chrome 并不需要上面的 Array.prototype.forEachArray.prototype.mapArray.prototype.filterArray.prototype.someArray.prototype.everyArray.prototype.findArray.prototype.findIndex 等。

兼容性

你可以在当前浏览器的控制台输入 Array.prototype.findIndex 等,如果未打印出函数,则说明浏览器不支持这个 API。或者前往 浏览器兼容性网站 (opens new window) 查询。

针对上述情况,Babel 的 @babel/preset-env 插件提供了 useBuiltIns (opens new window) 属性,只需要设置成 entry (默认为:false),Babel 就会根据配置中的目标浏览器进行选择性 API 垫片的添加。useBuiltIns: "entry" (opens new window)

注意

请确保入口引入了 core-jsregenerator-runtime/runtime,因为 Babel 是将上述引入文件,将其拆分成所需的不同垫片。

对比: 简单使用,将 run-babel.js 中的 Babel 配置和编译文件进行修改,这样产物对比会更加清楚。

  • 默认不设置(即useBuiltIns: false),编译配置和产物如下:


 
 

 

 



// run-babel.js
// ...
const filename = path.resolve(__dirname, "./src/browser.js");
const buildFileName = path.resolve(__dirname, "./src/browser-es5-false.js");
const data = fs.readFileSync(filename, "utf8");
// 确保入口文件 browser.js 添加了 core-js、regenerator-runtime
const transformData = babel.transformSync(data, {
  presets: ["@babel/preset-env"],
});
// ...
1
2
3
4
5
6
7
8
9
10








 
 
 
















// src/browser-es5-false.js
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true,
});
exports.browser = browser;

require("core-js/stable");

require("regenerator-runtime/runtime");

// src/browser.js
var inBrowser = typeof window !== "undefined";
var UA = inBrowser && window.navigator.userAgent.toLowerCase();
var isIE = UA && /msie|trident/.test(UA);
var isEdge = UA && UA.includes("edg/");
var isChrome = UA && UA.includes("chrome") && !isEdge;

function browser() {
  return [
    "ie: ".concat(isIE),
    "edge: ".concat(isEdge),
    "chrome: ".concat(isChrome),
  ].join(", ");
}
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
  • 设置 useBuiltIns: entry,编译配置和产物如下:



 

 

 



// run-babel.js
// ...
const filename = path.resolve(__dirname, "./src/browser.js");
const buildFileName = path.resolve(__dirname, "./src/browser-es5-entry.js");
const data = fs.readFileSync(filename, "utf8");
// 确保入口文件 browser.js 添加了 core-js、regenerator-runtime
const transformData = babel.transformSync(data, {
  presets: [["@babel/preset-env", { useBuiltIns: "entry", corejs: 3 }]],
});
// ...
1
2
3
4
5
6
7
8
9
10

注意

需要显示指定 corejs: 3 或者 corejs: 2,否则 Babel 不清楚需要对哪个版本的 corejs 以哪种方式进行展开优化。









 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 















// src/browser-es5-entry.js
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true,
});
exports.browser = browser;

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

require("core-js/modules/es.symbol.description.js");

require("core-js/modules/es.symbol.async-iterator.js");

require("core-js/modules/es.symbol.has-instance.js");

require("core-js/modules/es.symbol.is-concat-spreadable.js");

require("core-js/modules/es.symbol.iterator.js");

require("core-js/modules/es.symbol.match.js");
// ... 省略约 400 行代码,均为垫片
require("core-js/modules/web.url.to-json.js");

require("core-js/modules/web.url-search-params.js");

require("regenerator-runtime/runtime");

var inBrowser = typeof window !== "undefined";
var UA = inBrowser && window.navigator.userAgent.toLowerCase();
var isIE = UA && /msie|trident/.test(UA);
var isEdge = UA && UA.includes("edg/");
var isChrome = UA && UA.includes("chrome") && !isEdge;

function browser() {
  return [
    "ie: ".concat(isIE),
    "edge: ".concat(isEdge),
    "chrome: ".concat(isChrome),
  ].join(", ");
}
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

这样我们就清楚 useBuiltIns: entry 的效果了,由于我们没有指定目标浏览器,所以产物会将 ES2015-ES2020 转化为 ES5(语法同理),也就是引入 ES2015-ES2020 的所有垫片。no-targets 说明 (opens new window)。接下来我们将指定所需兼容的目标浏览器。targets 属性 (opens new window)

配置所需目标浏览器有以下 三种方式

  1. @babel/preset-envtargets 属性上配置。
  2. 创建配置文件 .browserslistrc
  3. package.jsonbrowserslist 属性上配置。

与之前小节同理,还是用插件中传入配置的方式,加深对 Babel 的理解。可以设置为 targets: "chrome >= 58" —— 兼容 Chrome,且只兼容到 58 版本。

  • 设置 targets:<需要兼容的浏览器>





 


 





 





// run-babel
// ...
const filename = path.resolve(__dirname, "./src/browser.js");
const buildFileName = path.resolve(
  __dirname,
  "./src/browser-es5-entry-chrome58.js"
);
const data = fs.readFileSync(filename, "utf8");
// 确保入口文件 browser.js 添加了 core-js、regenerator-runtime
const transformData = babel.transformSync(data, {
  presets: [
    [
      "@babel/preset-env",
      // 可以开启 debug,来查看兼容了哪些目标浏览器和添加的垫片
      { debug: true, useBuiltIns: "entry", corejs: 3, targets: "chrome >= 58" },
    ],
  ],
});
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19








 
 
 
 
 
 
 
 
 
 
 
 
 
 
 











// src/browser-es5-entry-chrome58.js
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true,
});
exports.browser = browser;

require("core-js/modules/es.symbol.description.js");

require("core-js/modules/es.symbol.async-iterator.js");
// ... 省略约 50 行代码,均为垫片
require("core-js/modules/web.dom-collections.iterator.js");

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

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

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

require("core-js/modules/web.url.to-json.js");

require("core-js/modules/web.url-search-params.js");

const inBrowser = typeof window !== "undefined";
const UA = inBrowser && window.navigator.userAgent.toLowerCase();
const isIE = UA && /msie|trident/.test(UA);
const isEdge = UA && UA.includes("edg/");
const isChrome = UA && UA.includes("chrome") && !isEdge;

function browser() {
  return [`ie: ${isIE}`, `edge: ${isEdge}`, `chrome: ${isChrome}`].join(", ");
}
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

从上方两个编译产物 src/browser-es5-entry.jssrc/browser-es5-entry-chrome58.js 的代码对比,其结果减少了很多不必要的垫片。也可以看到 Babel 转化-语法转化 也对目标浏览器进行识别,const 和字符串模板都未转化,因为这些语法 Chrome 58 已经支持了。

根据上方配置,在 babel-loader 进行同样配置就可使用相同的功能:



















 
 
 
 
 
 











// build.js
// ...
webpack(
  {
    // ...
    // 添加以下
    module: {
      rules: [
        {
          test: /\.m?js$/,
          exclude: /node_modules/,
          use: {
            loader: "babel-loader",
            // 传入配置方式 或者 创建配置文件
            options: {
              presets: [
                [
                  "@babel/preset-env",
                  {
                    debug: true,
                    useBuiltIns: "entry",
                    corejs: 3,
                    targets: "<替换成需要兼容的浏览器>",
                  },
                ],
              ],
            },
          },
        },
      ],
    },
  }
  // ...
);
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

注意

记得 清除 上面为了查看效果而添加的代码:browser.js 上的 core-js/stableregenerator-runtime/runtime确保这两个文件在全部文件中只引用了一次。 否则按需加载会不起效果,请确保在入口文件中添加这两文件。

# 依据代码和浏览器按需引用

注意

请在本节开始前,请清除 上节 添加的部分代码(上方理由):

# src/browser.js
-import "core-js/stable";
-import "regenerator-runtime/runtime";

const inBrowser = typeof window !== "undefined";
const UA = inBrowser && window.navigator.userAgent.toLowerCase();
const isIE = UA && /msie|trident/.test(UA);
const isEdge = UA && UA.includes("edg/");
const isChrome = UA && UA.includes("chrome") && !isEdge;

export function browser() {
  return [`ie: ${isIE}`, `edge: ${isEdge}`, `chrome: ${isChrome}`].join(", ");
}
1
2
3
4
5
6
7
8
9
10
11
12
13

观察上节编译出的 src/browser-es5-entry-chrome58.js 文件,不难发现有些用不上的 API 也会往里面添加。其实 src/browser.js 文件中 只用 到了以下 API

  1. String.prototype.toLowerCase
  2. RegExp.prototype.test
  3. String.prototype.includes (ES6)
  4. Array.prototype.join

要加垫片,最多也就这四个。如果 Babel 能对项目使用到的新 API 进行智能识别并添加,这会大大降低包的体积。

针对上述情况,只需将 Babel 的 @babel/preset-env 插件的 useBuiltIns (opens new window) 属性设置成 usage,Babel 就会根据项目的 API 使用情况来添加垫片。useBuiltIns: "usage" (opens new window)

注意

useBuiltIns: "usage" 模式下请确保全局未引入 core-jsregenerator-runtime/runtime。因为 Babel 将自动识别并在对应的文件中引入相应的垫片,而不是由用户来引入全局性的垫片。

对比: 简单使用,将 run-babel.js 中的 Babel 配置和编译文件进行修改,这样产物对比会更加清楚。

  • 设置 useBuiltIns: "usage",编译配置和产物如下:



 

 


 




// run-babel.js
// ...
const filename = path.resolve(__dirname, "./src/browser.js");
const buildFileName = path.resolve(__dirname, "./src/browser-es5-usage.js");
// ...
// 确保入口文件 browser.js !!没有!! 引入 core-js、regenerator-runtime
const transformData = babel.transformSync(data, {
  presets: [
    ["@babel/preset-env", { debug: true, useBuiltIns: "usage", corejs: 3 }],
  ],
});
// ...
1
2
3
4
5
6
7
8
9
10
11
12










 
 
 
 
 
 
 















// src/browser-es5-usage.js
"use strict";

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

Object.defineProperty(exports, "__esModule", {
  value: true,
});
exports.browser = browser;

require("core-js/modules/es.regexp.exec.js");

require("core-js/modules/es.array.includes.js");

require("core-js/modules/es.string.includes.js");

require("core-js/modules/es.array.join.js");

var inBrowser = typeof window !== "undefined";
var UA = inBrowser && window.navigator.userAgent.toLowerCase();
var isIE = UA && /msie|trident/.test(UA);
var isEdge = UA && UA.includes("edg/");
var isChrome = UA && UA.includes("chrome") && !isEdge;

function browser() {
  return [
    "ie: ".concat(isIE),
    "edge: ".concat(isEdge),
    "chrome: ".concat(isChrome),
  ].join(", ");
}
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

提示

由于 Babel 不清楚是 UA 是数组还是字符串,两个类型都有 includes 方法,所以都会添加。

不难发现现在的产物已经非常简洁,只有了 4 个垫片。

  • 设置 targets:<需要兼容的浏览器>

接下来再添加目标浏览器 targets: "chrome >= 58",移除浏览器已经支持的 API。编译配置和产物如下:






 


 




 





// run-babel.js
// ...
const filename = path.resolve(__dirname, "./src/browser.js");
const buildFileName = path.resolve(
  __dirname,
  "./src/browser-es5-usage-chrome58.js"
);
// ...
// 确保入口文件 browser.js !!没有!! 引入 core-js、regenerator-runtime
const transformData = babel.transformSync(data, {
  presets: [
    [
      "@babel/preset-env",
      { debug: true, useBuiltIns: "usage", corejs: 3, targets: "chrome >= 58" },
    ],
  ],
});
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18








 











// src/browser-es5-usage-chrome58.js
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true,
});
exports.browser = browser;

require("core-js/modules/es.regexp.exec.js");

const inBrowser = typeof window !== "undefined";
const UA = inBrowser && window.navigator.userAgent.toLowerCase();
const isIE = UA && /msie|trident/.test(UA);
const isEdge = UA && UA.includes("edg/");
const isChrome = UA && UA.includes("chrome") && !isEdge;

function browser() {
  return [`ie: ${isIE}`, `edge: ${isEdge}`, `chrome: ${isChrome}`].join(", ");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

上面的 src/browser-es5-usage.jssrc/browser-es5-usage-chrome58.js 文件对比,可以发现现在的代码只需一个垫片就可在 Chrome 58 上运行的,其中使用的 String.prototype.includes 和 模板字符串功能均在 Chrome 58 支持。

# 打包配置

根据上方相同的配置,在 babel-loader 同样配置即可使用相同的功能:

注意

请确保所有文件没有 core-jsregenerator-runtime/runtime


















 












// build.js
webpack({
  // ...
  module: {
    rules: [
      {
        test: /\.m?js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          // 传入配置方式 或者 创建配置文件
          options: {
            presets: [
              [
                "@babel/preset-env",
                {
                  debug: true,
                  useBuiltIns: "usage",
                  corejs: 3,
                  targets: "<替换成需要兼容的浏览器>",
                },
              ],
            ],
          },
        },
      },
    ],
  },
});
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

# 避免全局污染和重复辅助函数

build.js 的 babel 配置改为 ie >= 11。然后运行 node build.js,再到 IE 浏览器运行 index.html 文件,正常运行。但如果打开控制台,输入 PromiseString.prototype.includes 等,可以发现原本 不支持 的 API,现在却可以在控制台 正常运行。这是 babel 将新 API 的实现函数注入到了浏览器运行环境里,这是非常严重的代码入侵行为。这会让用户运行的代码出现非预期的结果,作为一个合格的库是不允许修改原型链等变量的。

其实还有个问题,先假设我们每个文件都有使用 classregeneratorasync、await,在原内容下使用这些语法。

// src/browser.js
// ...
// 类
class Browser {
  constructor(value) {
    this.value = value;
  }

  getVal() {
    return `value: ${this.value}`;
  }
}

// async、await
(async function() {
  await new Promise((resolve) => setTimeout(resolve, 2000));
  // 两秒后显示打印类
  console.log(Browser);
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// src/util.js
// ...
// 类
class Util {
  constructor(value) {
    this.value = value;
  }

  getVal() {
    return `value: ${this.value}`;
  }
}

// async、await
(async function() {
  await new Promise((resolve) => setTimeout(resolve, 2000));
  // 两秒后显示打印类
  console.log(Util);
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// src/element.js
// ...
// 类
class Element {
  constructor(value) {
    this.value = value;
  }

  getVal() {
    return `value: ${this.value}`;
  }
}

// async、await
(async function() {
  await new Promise((resolve) => setTimeout(resolve, 2000));
  // 两秒后显示打印类
  console.log(Element);
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

这时候在 mode: "none" 模式下运行 node build.js,打开 dist/library.js,查询生成出的 _classCallCheck_asyncToGenerator 声明辅助函数。它们的数量均为 3 个。

如果你认为 mode: "production" 模式下,Webpack 会进行合并优化等。可以尝试在该模式下运行,然后在产物中查询 Cannot call a class as a function 字符串,因为变量会被压缩,而 _classCallCheck 函数内部会包含这个字符串,也就可以当作 _classCallCheck 的计数。_asyncToGenerator 也同理。结果就是 Cannot call a class as a function 字符串能查询出 3 个。

这就说明多一个包含 classregeneratorasync、await 内容的文件,就会多个相应的辅助函数。假设是 100 个文件,那么就是 100 个相同的辅助函数。这会无意义的增加包的体积。

# 改造

Babel 的 @babel/plugin-transform-runtime 插件支持为库的代码提供一个沙盒环境,也可以将重复的 regeneratorclass 辅助函数提取出来。

# 安装开发依赖
npm install @babel/plugin-transform-runtime --save-dev
# 安装生产依赖,请选择对应 corejs 的版本安装
# corejs false
npm install @babel/runtime
# corejs 2
npm install @babel/runtime-corejs2
# corejs 3
npm install @babel/runtime-corejs3
1
2
3
4
5
6
7
8
9

注意

  1. 这需要安装两个包,因为 Babel 将辅助函数统一放到了 @babel/runtime 编译出来的代码需要用到,而 @babel/plugin-transform-runtime 的作用是编译的时候将各个地方原本用到的辅助函数(单独生成或污染全局)切换成 @babel/runtime 里的辅助函数。
  2. @babel/preset-envuseBuiltIns 不能@babel/plugin-transform-runtime 混用,因为这两个功能是 互斥 的。一个是全局加上垫片,一个是单独添加,避免全局污染。Babel 作者的回答 (opens new window)

我们可以将 run-babel.js 中的 Babel 配置和编译文件进行修改,这样产物对比会更加清楚。

使用 @babel/plugin-transform-runtime,编译配置和产物如下:






 











 



 



// run-babel.js
// ...
const filename = path.resolve(__dirname, "./src/browser.js");
const buildFileName = path.resolve(
  __dirname,
  "./src/browser-es5-ie11-runtime.js"
);
const data = fs.readFileSync(filename, "utf8");
// 配置会读取根目录下的 babel.config.json 或 .babelrc.json (也有其他的扩展名文件)
// const transformData = babel.transformSync(data);

// 指定配置
const transformData = babel.transformSync(data, {
  presets: [
    [
      "@babel/preset-env",
      // 可以开启 debug,来查看兼容了哪些目标浏览器和添加的垫片
      { debug: true, targets: "ie >= 11" },
    ],
  ],
  // @babel/preset-env 的 useBuiltIns 不能与 @babel/plugin-transform-runtime 混用
  plugins: [["@babel/plugin-transform-runtime", { corejs: 3 }]],
});
// ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

















 



 







 














 
 


















































// src/browser-es5-ie11-runtime.js
"use strict";

var _Object$defineProperty = require("@babel/runtime-corejs3/core-js-stable/object/define-property");

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

_Object$defineProperty(exports, "__esModule", {
  value: true,
});

exports.browser = browser;

var _regenerator = _interopRequireDefault(
  require("@babel/runtime-corejs3/regenerator")
);

var _asyncToGenerator2 = _interopRequireDefault(
  require("@babel/runtime-corejs3/helpers/asyncToGenerator")
);

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

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

var _includes = _interopRequireDefault(
  require("@babel/runtime-corejs3/core-js-stable/instance/includes")
);

var _promise = _interopRequireDefault(
  require("@babel/runtime-corejs3/core-js-stable/promise")
);

var _setTimeout2 = _interopRequireDefault(
  require("@babel/runtime-corejs3/core-js-stable/set-timeout")
);

var inBrowser = typeof window !== "undefined";
var UA = inBrowser && window.navigator.userAgent.toLowerCase();
var isIE = UA && /msie|trident/.test(UA);
var isEdge = UA && (0, _includes.default)(UA).call(UA, "edg/");
var isChrome = UA && (0, _includes.default)(UA).call(UA, "chrome") && !isEdge;

function browser() {
  return [
    "ie: ".concat(isIE),
    "edge: ".concat(isEdge),
    "chrome: ".concat(isChrome),
  ].join(", ");
} // 类

var Browser = /*#__PURE__*/ (function() {
  function Browser(value) {
    (0, _classCallCheck2.default)(this, Browser);
    this.value = value;
  }

  (0, _createClass2.default)(Browser, [
    {
      key: "getVal",
      value: function getVal() {
        return "value: ".concat(this.value);
      },
    },
  ]);
  return Browser;
})(); // async、await

(0, _asyncToGenerator2.default)(
  /*#__PURE__*/ _regenerator.default.mark(function _callee() {
    return _regenerator.default.wrap(function _callee$(_context) {
      while (1) {
        switch ((_context.prev = _context.next)) {
          case 0:
            _context.next = 2;
            return new _promise.default(function(resolve) {
              return (0, _setTimeout2.default)(resolve, 2000);
            });

          case 2:
            // 两秒后显示打印类
            console.log(Browser);

          case 3:
          case "end":
            return _context.stop();
        }
      }
    }, _callee);
  })
)();
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

可以发现:

  1. 转化的代码是通过 变量自执行函数 去执行的,而不是直接 require 使用副作用来修改环境变量、原型链。
  2. 辅助函数 使用的是 @babel/runtime-corejs,而不是每个文件都实现。
  3. includes,其结果与 官方示例 (opens new window) 类似。
  4. await async(异步函数),其结果与 官方示例 (opens new window) 类似。
  5. class,其结果也类似 官方示例 (opens new window)

# 打包配置

根据上方相同的配置,在 babel-loader 同样配置即可使用相同的功能:















 

 









// build.js
webpack(
  {
    // ...
    module: {
      rules: [
        {
          test: /\.m?js$/,
          exclude: /node_modules/,
          use: {
            loader: "babel-loader",
            // 传入配置方式 或者 创建配置文件
            options: {
              presets: [
                ["@babel/preset-env", { debug: true, targets: "ie >= 11" }],
              ],
              plugins: [["@babel/plugin-transform-runtime", { corejs: 3 }]],
            },
          },
        },
      ],
    },
  }
  // ...
);
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

运行 node build.js,打开 index.html,IE 上正常运行。在 IE 控制台输入 String.prototype.includesPromise API,其结果为 undefined未定义,这就说明库的代码并未入侵到运行环境中。

同时查看 mode: "none" 输出的 dist/index.js 产物的代码行数,为 5002 行。对比之前未优化的 Babel 配置所输出的 15827 行代码,减少 了将近 三分之二

提示

代码行数不一定和作者的结果一致,Webpack 和 Babel 的版本、代码的写法与注释都会影响代码行数。

整理

为了方便后续章节展开,将 src/browser.js 以各种模式输出的

  1. src/browser.js (复制)
  2. src/browser-es5-false.js(迁移)
  3. src/browser-es5-entry.js(迁移)
  4. src/browser-es5-entry-chrome58.js(迁移)
  5. src/browser-es5-usage.js(迁移)
  6. src/browser-es5-usage-chrome58.js(迁移)
  7. src/browser-es5-ie11-runtime.js(迁移)

移动至 babel-example 文件夹下。