# e-react-es-babel-test **Repository Path**: ymcdhr/e-react-es-babel-test ## Basic Information - **Project Name**: e-react-es-babel-test - **Description**: webpack、babel、@bable/preset-env、core-js、transform-runtime-plugin 的配置与区别 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2021-08-10 - **Last Updated**: 2024-11-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ## 本文介绍 **整理时间:** 2021年8月 本文结合 [Babel官网](https://www.babeljs.cn/docs/) 和 [ Babel7专栏 ](https://segmentfault.com/u/xeleven/articles?page=2) 进行整理;本文只针对 core-js@3 新版本进行讨论。 **参考资料:** [babel插件用法官方文档](https://www.babeljs.cn/docs/)、[Babel7专栏](https://segmentfault.com/u/xeleven/articles?page=2) **完整示例:** 参考项目源码 ### 1. babel基本使用 #### 1. babel核心插件 ``` npm i -D @babel/core => babel核心模块 npm i -D babel-loader => babel转换平台,Webpack上的babel插件 ``` #### 2. babel配置文件:.babelrc - 配置文件可以为三种格式:js、json、yaml - 示例: ``` // .babelrc { "presets": [], "plugins": [] } ``` #### 3. 浏览器支持配置:browserslist 1. 配置放到文件package.json(推荐): ```js // package.json { // ... "browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ] } ``` 2. 配置放到文件.babelrc: ```js { "presets": [ [ "@babel/preset-env", { "useBuiltIns": "usage", // 用到哪个不支持的语法就去填充 "corejs": 3, "targets": { "browsers": [ "> 1%", "last 2 versions", "not ie <= 8" ] } } ], "@babel/preset-react" ] } ``` ### 2. 核心插件的作用 #### 1. babel 转换分两部分来处理: 1. babel 转换分两部分来处理:Syntax 和 Api - Syntax:类似于 =>箭头函数、...展开对象、optional chain、let、const 等语法 - Api:类似于 promise、array.includes 等函数、方法 2. Syntax:需要借助插件 @babel/preset-env,默认只转换新的 JavaScript 句法,例如:箭头函数、扩展云计算符、optional chain、let、const 等语法; 3. Api:需要借助插件 @babel/preset-env + @babel/polyfill;有两类方案: - 方案一:@babel/preset-env + @babel/polyfill - 方案二:@babel/preset-env + @babel/plugin-transform-runtime + @babel/runtime-corejs2 #### 2. @babel/preset-env(-D) 1. preset-env 可以理解为 polyfill 的转换工具,配置好后默认只转换新的 JavaScript 句法;例如:箭头函数、扩展云计算符、optional chain、let、const 等语法; 2. preset-env 包括了所有 stage 的语法: - preset-stage-0 - preset-stage-1 - preset-stage-2 - preset-stage-3 3. 主要配置说明: - 示例配置: ``` { "presets": [ [ "@babel/preset-env", { "useBuiltIns": "usage", // 用到哪个不支持的语法就去填充 "corejs": 3, "targets": { "browsers": [ "> 1%", "last 2 versions", "not ie <= 8" ] } } ], "@babel/preset-react" ] } ``` - 详细配置: ``` 1、modules:"amd" | "umd" | "systemjs" | "commonjs" | "cjs" | "auto" | false,默认值是 auto。 - 用来转换 ES6 的模块语法。如果使用 false,将不会对文件的模块语法进行转化。 - 如果要使用 webpack 中的一些新特性,比如 tree shaking 和 sideEffects,就需要设置为 false,对 ES6 的模块文件不做转化,因为这些特性只对 ES6 的模块有效。 2、useBuiltIns:"usage" | "entry" | false,默认值是 false。 - false:需要在 js 代码第一行主动 import '@babel/polyfill',会将@babel/polyfill 整个包全部导入。 (不推荐,能覆盖到所有 API 的转译,但体积最大) - entry:需要在 js 代码第一行主动 import '@babel/polyfill',会将 browserslist 环境不支持的所有垫片都导入。 (能够覆盖到‘hello‘.includes(‘h‘)这种句法,足够安全且代码体积不是特别大) - usage:项目里不用主动 import,会自动将代码里已使用到的、且 browserslist 环境不支持的垫片导入。 (但是检测不到‘hello‘.includes(‘h‘)这种句法,对这类原型链上的句法问题不会做转译,书写代码需注意) 3、targets:用来配置需要支持的的环境,不仅支持浏览器,还支持 node。如果没有配置 targets 选项,就会读取项目中的 browserslist 配置项。 4、loose:默认值是 false,如果 preset-env 中包含的 plugin 支持 loose 的设置,那么可以通过这个字段来做统一的设置。 ``` #### 3. @babel/polyfill(-s) 1. polyfill 可以理解为 ES6+ 的语言包,Babel 默认只转换新的 JavaScript 句法(syntax),而不转换新的 API,比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign)都不会转码;这些 API 的转换需要借助 polyfill。 2. **polyfill 的优点:** 几乎包含所有的 ES6+ 方法和 API 的转译,包括 Array.prototype.includes; 3. **polyfill 存在的两个问题** ,[详情参考附录]() - polyfill 里面集成了 core-js,但是它会**污染全局的 API**;因为在转换 API 时,如果发现环境中没有,它就直接重写一个全局变量; - polyfill 在转换函数的时候会生成 helper 内联函数,多个文件如果都有该函数会造成比必要的重复。 #### 4. @babel/plugin-transform-runtime(-D) 1. plugin-transform-runtime 是一个插件,可以重用 Babel 注入的 helper 函数以节省代码大小。 2. **包含3个核心功能:** - **按需导入:regenerator-runtime** 使用 generators/async 方法时,自动加载 regenerator-runtime 插件,可以在配置中关闭; - **按需导入:core-js** 使用 ES6+ 常用 API 时(Promise, Set, Symbol, etc),自动加载 core-js,根据需要导入 helper 函数,避免了用户手动导入 polyfill,可以在配置中关闭; - **复用函数:@babel/runtime** 使用 @babel/runtime/helpers 实现复用 helper 函数,自动删除内联 helper 函数,可以在配置中关闭; 3. **各配置项:** - 如果关闭了配置项,安装了插件也无效; - 例如:regenerator 为 true,就需要用到插件 regenerator-runtime; ``` { "plugins": [ [ "@babel/plugin-transform-runtime", { "absoluteRuntime": false, // 是否允许插件放到项目其它目录,不一定是node_modules,默认:false "corejs": true, // 是否按需导入 helper 函数,默认:true "helpers": true, // 是否复用 helper 函数,默认:true "regenerator": true, // 是否使用 regenerator-runtime 将 generate 函数转换为不污染全局作用域,默认:true "version": "7.0.0" // @bable/runtime 或者 @babel/runtime-corejs2 的版本号 } ] ] } ``` 4. 使用场景:通常是开发第三方包的时候使用,为了避免自己重写的全局 API 和 ployfill 发生冲突; #### 5. @babel/runtime-corejs2(-s) 1. 一个包含:Babel模块化运行时辅助函数 + regenerator-runtime + core-js 的库,可以替换 polyfill 语言包;需要结合 @babel/plugin-transform-runtime,包涵以下功能: - **模块化运行时辅助函数:** 结合 @babel/plugin-transform-runtime 实现重用 helper 函数的功能,自动删除内联 helper 函数; - **@babel/regenerator-runtime:** 结合 @babel/plugin-transform-runtime 转译 generate/async 函数等; - **core-js@2:** 结合 @babel/plugin-transform-runtime 自动按需导入 core-js 中的 helper 函数,然后使用 core-js@2 中的 library 文件夹里面转译代码避免了全局污染;(polyfill是使用的另一个目录,详细参考附录) 2. @babel/runtime、@babel/runtime-corejs2 的缺点:不支持 Array.prototype.includes 这个方法; #### 6. @babel/runtime(-s) 一个包含:Babel模块化运行时辅助函数 + regenerator-runtime 的库;需要结合 @babel/plugin-transform-runtime。 #### 7. regenerator-runtime(-s) 一个用于转换 generator、async 函数的语言包;它在 @babel/runtime、@babel/runtime-corejs2 中都已经包含了。 ### 3. babel 实现 ES6+ API 的两类方案: #### 传统方案一:preset-env + polyfill(废弃): 1. 实现 ES 基础语法的转译 + 新 API 的转译,有如下特点(未经过测试,请参考章节:**测试分析**): - 按需导入:useBuiltIns 配置可以实现按需导入,配置为:usage; - 函数复用:helpers 存在重复引入函数的问题; - 全局污染:polyfill 存在全局 Api 污染的问题; 2. 安装插件 ``` npm i -D @babel/preset-env npm i -s @babel/polyfill ``` 3. 配置示例:.babelrc ```js { "presets": [ [ "@babel/preset-env", { "modules": false, // 推荐 "useBuiltIns": "entry", // 推荐 "corejs": 2 } ] ] } ``` #### 传统方案二:preset-env + plugin-transform-runtime + runtime-corejs2(废弃) 1. 实现 ES 基础语法的转译 + 新 API 的转译,有如下特点: - 按需导入:plugin-transform-runtime + runtime-corejs2 支持按需导入 core-js@2 中的函数来进行转译; - 函数复用:plugin-transform-runtime + runtime-corejs2 支持复用 helpers 函数; - 全局污染:plugin-transform-runtime + runtime-corejs2 支持按需导入 core-js@2,且可以避免全局 Api 污染的问题;(为什么@babel/runtime-corejs2 可以避免全局污染?请阅读附录) - 存在问题:runtime-corejs2 不支持 Array.prototype.includes 函数; 2. 安装插件 ``` npm i -D @babel/preset-env npm i -D @babel/plugin-transform-runtime npm i -s @babel/runtime-corejs2 ``` 3. 配置实例:.babelrc ```js { "presets": [ [ "@babel/preset-env", { "modules": false, } ] ], "plugins": [ [ "@babel/plugin-transform-runtime", { "corejs": 2 } ] ] } ``` #### 升级方案一:@babel/preset-env + core-js@3 + regenerator-runtime(Babel7.4.0版本以后) 1. 实现 ES 基础语法的转译 + 新 API 的转译,有如下特点(已经测试过,请参考章节:**测试分析**有详细解读): - 按需导入:需要配置,useBuiltIns: usage - 函数复用:重用了 helpers 函数; - 全局污染:会有全局污染; - 存在问题:支持 includes 函数; 2. 安装插件 ``` npm i -D @babel/preset-env npm i -s regenerator-runtime npm i -s core-js@3 => core-js@3替代polyfill,安装这个就不要安装polyfill了; => core-js@3需要指定版本号为3,因为polyfill在7.4.0版本后废弃。 ``` 3. 为什么不使用@babel/runtime-corejs3? - @babel/runtime-corejs3 包含了@babel/runtime + regenerator-runtime + core-js@3; - @babel/runtime-corejs3 需要配合 @babel/plugin-transform-runtime 使用,所以这个方案里面没有使用; 4. 示例配置: ``` { "presets": [ [ "@babel/preset-env", { "modules": false, // 对ES6的模块文件不做转化,以便使用tree shaking、sideEffects等 "useBuiltIns": "entry", // browserslist环境不支持的所有垫片都导入 "corejs": { "version": 3, // 使用core-js@3,必须要指定版本避免和老版本冲突;否则会报:warning "proposals": true, } } ] ] } ``` 5. 入口文件导入: ``` import "core-js/stable"; import "regenerator-runtime/runtime"; ``` #### 升级方案二:@babel/preset-env + @babel/plugin-transform-runtime + @babel/runtime-corejs3 1. 实现 ES 基础语法的转译 + 新 API 的转译,有如下特点: - 按需导入:plugin-transform-runtime + runtime-corejs3 支持按需导入 core-js@3 中的函数来进行转译; - 函数复用:plugin-transform-runtime + runtime-corejs3 支持复用 helpers 函数; - 全局污染:plugin-transform-runtime + runtime-corejs3 支持按需导入 core-js@3,且可以避免全局 Api 污染的问题;(为什么@babel/runtime-corejs3 可以避免全局污染?请阅读附录) - 存在问题:runtime-corejs3 里面的 core-js@3 已经支持 Array.prototype.includes 函数(core-js@2不支持); 2. 安装插件 ``` npm i -D @babel/preset-env npm i -D @babel/plugin-transform-runtime npm i -s @babel/runtime-corejs3 ``` 3. 配置文件: ``` { "presets": [ [ "@babel/preset-env" ] ], "plugins": [ [ "@babel/plugin-transform-runtime", { "corejs": { "version": 3, "proposals": true } } ] ] } ``` --- ## 附录 ### Helpers 函数重复引用问题 #### Helper 函数是什么? babel 有很多帮助函数,例如 toArray函数, jsx转化函数。这些函数是 babel transform 的时候用的,都放在 babel-helpers这个包中。如果 babe 编译的时候检测到某个文件需要这些 helpers,在编译成模块的时候,会放到模块的顶部。 ```js (function(module, exports, __webpack_require__) { function _asyncToGenerator(fn) { return function () { }; } // 模块顶部定义 helper // some code // async 语法已经被转化,再利用 helper 函数包装,消费 generator。 const func = (() => { var _ref = _asyncToGenerator(function* () { console.log('begin'); yield new Promise(function (resolve) { setTimeout(function () { resolve(); }, 1000); }); console.log('done'); }); }) ``` #### Helper 函数的重复引用 如果多个文件都有相同的 helper 函数,且它们都是内联函数,会导致每一个模块都定义一份,代码冗余。所以 babel 提供了这个命令,用于生成一个包含了所有 helpers 的 js 文件,用于直接引用。然后再通过一个 plugin,去检测全局下是否存在这个模块,存在就不需要重新定义了。 #### Helper 函数重复引用如何解决? 1. 执行命令:生成 helpers.js 文件, ``` > ./node_modules/.bin/babel-external-helpers > helpers.js ``` 2. 安装插件:babel-plugin-external-helpers ``` npm i -D babel-plugin-external-helpers ``` 3. 然后在 babel 的配置文件加入 ``` { "plugins": ["external-helpers"] } ``` 4. 入口文件引入 helpers.js ``` require('./helpers.js'); ``` 5. @babel/plugin-transform-runtime 复用 helper 函数 如果使用了 @babel/plugin-transform-runtime,就不需要生成 helpers.js 文件了;配合 @babel/runtime 或者 @babel/runtime-corejs3 自动删除内联 helper 函数,提取到公用模块,这是现在最常用的方案。 6. 最新的方案 Webpack5.4 + @babel/preset-env + core-js@3 + regenerator-runtime,经过测试已经没有重复引入函数的问题,现在都是按照模块进行引入;详细请参考章节:测试分析。 ### polyfill 全局污染的问题 #### polyfill 造成全局污染的原因 1. polyfill 在转换方法时直接修改了全局变量的原型,例如: - 例如:Array.from 等静态方法,直接在 global.Array 上添加;对于例如 includes 等实例方法,直接在 global.Array.prototype 上添加; - 这个问题在开发第三方库的时候尤其重要,例如:我们在开发第三方库的时候自定义用了Promise方法,那如果引入了polyfill,那么这个Promise就会被污染。 2. core-js@2这个库有两个核心的文件夹,分别是 library 和 modules。 - @babel/runtime-corejs2 使用 library 这个文件夹进行转换; - @babel/polyfill 使用 modules 这个文件夹。 3. library 和 modules 文件夹的差异: - library 使用 helper 的方式,局部实现某个 api,不会污染全局变量; - modules 以污染全局变量的方法来实现 api; - library 和 modules 包含的文件基本相同,最大的不同是\_export.js 这个文件: #### polyfill 全局污染有何影响? 1. 在开发第三方库的时候,如果自定义用了一些全局方法(例如:Promise),那就最好不用ployfill; #### polyfill 全局污染如何解决? 1. 不使用 polyfill,使用 @babel/plugin-transform-runtime + @babel/runtime-corejs3 的方案; 2. 不使用 polyfill,升级最新的方案 Webpack5.4 + @babel/preset-env + core-js@3 + regenerator-runtime,经过测试还是有全局污染的问题,所以还是要使用 @babel/plugin-transform-runtime + @babel/runtime-corejs3 的方案; --- ## 测试分析:core-js@3 的编译特性,如何编译 es6+ 的那些 api 的?(结论在最后) ### 测试方法 注意:babel 的按需转换?如果浏览器支持的类型,例如:Chrome里面原生支持的类型是不是就不转换了?那怎么办测试自定义实现的全局方法?配置了 browserslist 之后,useBuiltIns: "usage"时,babel 会按照 browserslist 不支持的目录方法进行转换。 #### 使用 ES6+ 特性 - ...obj - => - Promise - async、await - Map - Set - Array.isArray - Object.assign - Array.prototype.includes ### 测试环境 #### Webpack 版本 1. Webpack 版本:5.49.0 2. webpack-cli 版本:4.7.2 #### babel 配置方案:@babel/preset-env + core-js@3 + regenerator-runtime 1. babel 安装: ``` npm i -D @babel/cli => 核心工具 npm i -D @babel/core => 核心工具 npm i -D babel-loader => loader:Webpack 使用的 loader npm i -D @babel/preset-env => 语言包:babel 用于转译 es6+ 基本语法的语言包 npm i -s core-js@3 => 语言包:core-js@3 取代 polyfill,es+ Api 的转译语言包 npm i -s regenerator-runtime => 语言包:用于 generate、async 函数的转译语言包 ``` 2. babel 配置: ``` { "presets": [ [ "@babel/preset-env", { "useBuiltIns": "usage", // 用到哪个不支持的语法就去填充 "corejs": { "version": 3, "proposals": true } } ] ] } ``` 3. package.json,配置了:browserslist ```json { "name": "e-es-babel-test", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "dev": "webpack --config webpack.dev.js --watch" }, "author": "Tony Young", "dependencies": { "axios": "^0.21.1", "core-js": "^3.16.1" "regenerator-runtime": "^0.13.9" }, "devDependencies": { "@babel/cli": "^7.14.8", "@babel/core": "^7.15.0", "@babel/preset-env": "^7.15.0", "babel-loader": "^8.2.2", "webpack": "^5.49.0", "webpack-cli": "^4.7.2", "webpack-merge": "^5.8.0" }, "license": "ISC", "browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ] } ``` #### Webpack 打包文件的结构 ```js (function () { // core-js@3 核心模块定义 var __webpack_modules__ = ({ "./node_modules/core-js/internals/global.js": (function (module, __unused_webpack_exports, __webpack_require__) { module.exports = 各环境下的全局对象 }), "./node_modules/core-js/internals/array-includes.js": (function (module, __unused_webpack_exports, __webpack_require__) { var createMethod = function (){ // ... } module.exports = { includes: createMethod(true), indexOf: createMethod(false) }; }), // 下面还有很多模块定义! // ... }) // 中间定义了 Webpack 导入函数 function __webpack_require__(moduleId) { // ... } // 中间定义了一系列 Webpack 功能函数 !function () { // ... }(); !function () { // ... }(); // 最后执行用户代码 !function () { // ... // 执行导入模块 var core_js_modules_es_array_includes_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./node_modules/core-js/modules/es.array.includes.js"); var core_js_modules_es_array_includes_js__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(core_js_modules_es_array_includes_js__WEBPACK_IMPORTED_MODULE_0__); // 执行了用户的代码 // includes 方法 const arr = [0,1,2,3,4] console.log(arr.includes(2)) }(); })(); ``` ### core-js 生成的几个核心模块调用流程 #### 执行用户代码前使用 Webpack 方法导入用到的模块,例如:includes 方法 - \_\_webpack_require__() 方法判断该模块是否有缓存; - \_\_webpack_require__.n() 方法执行导入模块并运行; ``` // 使用 Webpack 方法导入模块 var core_js_modules_es_array_includes_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__("./node_modules/core-js/modules/es.array.includes.js"); var core_js_modules_es_array_includes_js__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(core_js_modules_es_array_includes_js__WEBPACK_IMPORTED_MODULE_0__); // 执行用户代码 var arr = [0, 1, 2, 3, 4]; console.log(arr.includes(2)); ``` #### 导出 includes 方法的模块 模块索引:./node_modules/core-js/modules/es.array.includes.js 1. 定义了 includes 方法,并调用导出模块 $ 将 includes 方法挂载到 Array 的原型上面; ```js (function(__unused_webpack_module, __unused_webpack_exports, __webpack_require__) { "use strict"; var $ = __webpack_require__(/*! ../internals/export */ "./node_modules/core-js/internals/export.js"); var $includes = __webpack_require__(/*! ../internals/array-includes */ "./node_modules/core-js/internals/array-includes.js").includes; var addToUnscopables = __webpack_require__(/*! ../internals/add-to-unscopables */ "./node_modules/core-js/internals/add-to-unscopables.js"); $({ target: 'Array', proto: true }, { includes: function includes(el /* , fromIndex = 0 */) { return $includes(this, el, arguments.length > 1 ? arguments[1] : undefined); } }); addToUnscopables('includes'); }), ``` #### 导出模块:$ 模块索引:./node_modules/core-js/internals/export.js 1. 该模块的作用是将指定模块挂载到 target 对象下面,它会提前去加载一些用到的模块;例如:global。 2. target 对象的类型有三种: - global(浏览器下就是:window) - static - 某个全局对象(例如:Array) #### 获取全局变量的模块:global 模块索引:./node_modules/core-js/internals/global.js ``` "./node_modules/core-js/internals/global.js": (function (module, __unused_webpack_exports, __webpack_require__) { var check = function (it) { return it && it.Math == Math && it; }; module.exports = // eslint-disable-next-line es/no-global-this -- safe check(typeof globalThis == 'object' && globalThis) || check(typeof window == 'object' && window) || // eslint-disable-next-line no-restricted-globals -- safe check(typeof self == 'object' && self) || check(typeof __webpack_require__.g == 'object' && __webpack_require__.g) || // eslint-disable-next-line no-new-func -- fallback (function () { return this; })() || Function('return this')(); }), ``` ### 根据测试,得出结论 #### 完整的测试代码: ```js // // useBuiltIns: false的时候需要在入口文件引入以下; // // useBuiltIns: "usage"的时候babel会自动按需导入,也就不需要在入口文件导入了; // import "core-js/stable"; // import "regenerator-runtime/runtime"; // // 1、测试多模块同时使用同类方法 // require("./components/testHelpers") // // 2、Array.prototype.includes 方法 // const arr = [0,1,2,3,4] // console.log(arr.includes(2)) // // 3、Array.prototype.isArray // console.log(Array.isArray([])) // 4、Promise const promise = new Promise((resolve,reject) => { setTimeout(() => { resolve("hello") }, 1000) }) promise.then(str => { console.log(str) }) // 5、async/await const s = async () => { const str = await promise console.log(str) } s() // // 6、箭头函数 // export const func = () => {} // // 7、扩展运算符 // const data = { // ...{ // name: "张三", // age: 18 // }, // title: "中学生" // } // // 8、Object.assign // const n = Object.assign({}, data) // // 9、Set // const s = new Set([0,1,0,1]) // // 10、Map // const m = new Map() // m.set("name", "李四") ``` #### 结论: core-js@3 1. 按需导入: - useBuiltIns: "usage",在测试代码中,如果使用了某种方法,Webpack 打包到代码就会自动导入该方法对应的模块。 - useBuiltIns: false,需要在主入口 js 引入,并且全量引入 core-js@3 中的所有方法; ``` // useBuiltIns: false 的时候需要在主入口 js 引入 import "core-js/stable"; import "regenerator-runtime/runtime"; ``` 2. 函数复用:在测试代码中,两个模块公用的 includes 方法都被打包到了同一个模块,而且它们引用的也是同一个模块;所以不存在重复引用的问题,具体参考源码: ``` // indes.js require("./components/testHelpers") // Array.prototype.includes 方法 const arr = [0,1,2,3,4] console.log(arr.includes(2)) ``` ``` // ./components/testHelpers.js // 在主模块中使用了includes // 在本模块中也是用了includes,检查是否重复生成了includes的帮助函数 const arr = [0, 1, 2, 3, 4] const isTrue = arr.includes(2) module.exports = isTrue // 结论:没有重复引用 // 主模块和子模块中都引用了该模块:./node_modules/core-js/modules/es.array.includes.js ``` 3. 全局污染:在测试代码中,babel 将没有的方法重新定义并挂载到了全局对象上面;所以使用core-js@3还是存在全局污染的问题。 4. 存在问题:在测试代码中,core-js@3 已经支持 Array.prototype.includes 函数; 5. generator、async 函数会使用 regenerator-runtime 库来进行转换。