About babel core-js

458 阅读6分钟

segmentfault.com/a/119000002… (@babel/preset-env 与@babel/plugin-transform-runtime 使用及场景区别)

babel

www.babeljs.cn/docs/ babel7

hacpai.com/article/158…

Babel 是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法, 可以使之运行在当前和旧版本的浏览器或其他环境中。

Plugin 实现 "转换"

  1. transform-es2015-template-literals : 转译模版字符串的 plugin

preset: babel 插件集合的预设,包含某些插件 plugin。显然像上面那样一个一个配置插件会非常的麻烦,为了方便,babel 为我们提供了一个配置项叫做 persets(预设)。

preset 命名: env --> babel-preset-env

☆ ☆ lugins 与 presets 同时存在的执行顺序

先执行 plugins 的配置项,再执行 Preset 的配置项 ;

  • plugins 配置项,按照声明顺序执行;
  • Preset 配置项,按照声明逆序执行。 ** 列入以下代码的执行顺序为:**
// .babelrc 文件
{ 
 "plugins": [
    "transform-es2015-template-literals" // 转译模版字符串的 plugins
 ],
 "presets": ["env", "stage-2"]
}

transform-es2015-template-literals > stage-2 > env

balel-preset-env

根据目标环境选择不支持的新特性来转译(有的环境支持的话就不用转码了)。

"babel": {
  "presets": [
    [
      "env",
      {
        "targets": {
          "browsers": ["last 2 versions", "ie >= 7"]
        }
      }
    ]
  ]
},

配置

modules : 将ES6模块语法转换为另一种模块类型,可选值:

各种流行的模块化规范:"amd"、 "commonjs"、 "systemjs"、 "umd" 禁止转译:false

useBuiltIns (boolean, 默认值: false)

babel为标准库中的新功能提供了polyfill,为内置对象,静态方法,实例方法,生成器函数提供支持。 babel-preset-env可以实现基于特定环境引入需要的polyfill。 (polyfill是个大的包,里面有很多小包,env可以按需加载需要的部分)

useBuiltIns “usage” | “entry” | false, defaults to false.

This option adds direct references to the core-js module as bare imports. Thus core-js will be resolved relative to the file itself and needs to be accessible. You may need to specify core-js@2 as a top level dependency in your application if there isn’t a core-js dependency or there are multiple versions.

useBuiltIns: 'usage'

Adds specific imports for polyfills when they are used in each file. We take advantage of the fact that a bundler will load the same polyfill only once.

和@babel/preset-env 一起用的时候 When used alongside @babel/preset-env,

  1. If useBuiltIns: 'usage' is specified in .babelrc then do not include @babel/polyfill in either webpack.config.js entry array nor source. Note, @babel/polyfill still needs to be installed.

  2. If useBuiltIns: 'entry' is specified in .babelrc then include @babel/polyfill at the top of the entry point to your application via require or import as discussed above.

  3. If useBuiltIns key is not specified or it is explicitly set with useBuiltIns: false in your .babelrc, add @babel/polyfill directly to the entry array in your webpack.config.js.

1. 如果在.babelrc 中指定 useBuiltIns: 'usage'的话,那么就不要在webpack.config.js 的 entry array 和source 中包含 @babel/polyfill 了。注意,@babel/polyfill 依然需要安装 (按需加载)

1. 如果在.babelrc 中指定 useBuiltIns: 'entry'的话,那么就在你应用的入口文件顶部通过require 或者 import 引入@babel/polyfill (此种方式是错误的, 应该要通过env来自动加载polyfill )

  1. 如果在.babelrc 中没有指定 useBuiltIns 的值为 false. 可以直接在webpack.config.js 的 entry array 中添加 @babel/polyfill
module.exports = {
entry: ['@babel/polyfill', './app']
}

在.babelrc配置了"useBuiltIns": "usage"后,Babel 会在你使用到 ES2015+ 新特性时,自动添加 babel-polyfill 的引用,并且是 partial 级别的引用。按我的理解按需引入。。。

babel-polyfill

"version": "6.26.0",
"dependencies": {
    "babel-runtime": "^6.26.0",
    "core-js": "^2.5.7",
    "regenerator-runtime": "^0.12.0"
  }

regenerator-runtime 是什么?能做什么?

www.iteye.com/blog/schifr… [源码分析]

@babel/polyfill (自babel7.4 后就 deprecated, polyfill的历史结束了)

 "dependencies": {
    "core-js": "^2.6.5",#这里可以看出来 最新的@babel/polyfill  也不支持core-js@3
    "regenerator-runtime": "^0.13.4"
  }

☆ ☆ ☆ ☆ 一个正确的配置 不用安装@babel/polyfill

//
"babel": {
    "presets": [
      [
        "@babel/preset-env",
        {
          "modules": false, // 对ES6的模块文件不做转化,以便使用tree shaking、sideEffects等
          "targets": {
            "browsers": [
              "> 1%",
              "last 2 versions",
              "not ie <= 8"
            ]
          },
          "useBuiltIns": "usage", //按需加载
          "corejs": 3 
        }
      ]
    ],
    "plugins": [
      "transform-vue-jsx"
    ]
  }

三种方案对比

方案 优点 缺点
@babel/runtime & @babel/plugin-transform-runtime 按需引入, 打包体积小 不能兼容实例方法
@babel/polyfill 完整模拟 ES2015+ 环境 打包体积过大、污染全局对象和内置的对象原型
@babel/preset-env 按需引入, 可配置性高 不支持 stage-x 插件,没有利用针对特定浏览器的功能

babel-runtime

const key = 'babel'
const obj = {
    [key]: 'polyfill',
}

==>

function _defineProperty(obj, key, value) {
    if (key in obj) {
        Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true });
    } else {
        obj[key] = value;
    }
    return obj;
}
var key = 'babel';
var obj = _defineProperty({}, key, Object.assign({}, { key: 'polyfill' }));

babel-polyfill解决了Babel不转换新API的问题,但是直接在代码中插入帮助函数,会导致污染了全局环境,并且不同的代码文件中包含重复的代码,导致编译后的代码体积变大。 (比如:上述的帮助函数_defineProperty有可能在很多的代码模块文件中都会被插入)

Babel为了解决这个问题,提供了单独的包babel-runtime用以提供编译模块的工具函数, 启用插件babel-plugin-transform-runtime后,Babel就会使用babel-runtime下的工具函数,上述的代码就会变成这样

var _defineProperty2 = __webpack_require__("./node_modules/babel-runtime/helpers/defineProperty.js");

var _defineProperty3 = _interopRequireDefault(_defineProperty2);

var _assign = __webpack_require__("./node_modules/babel-runtime/core-js/object/assign.js");

var _assign2 = _interopRequireDefault(_assign);

function _interopRequireDefault(obj) { 
    return obj && obj.__esModule ? obj : { default: obj }; 
}

var key = 'babel';
var obj = (0, _defineProperty3.default)(
            {}, key, (0, _assign2.default)({}, { key: 'polyfill' })
          );

可以看到上述转换后的代码中_defineProperty帮助函数是通过babel-runtime下的模块引用的, 同时Object.assign也变成了模块引用, 这样可以避免自行引入polyfill时导致的污染全局命名空间的问

TODO ? @babel/runtime和@babel/plugin-transform-runtime 区别?

core-js

github.com/zloirock/co…

core-js 是babel-polyfill 的底层依赖,通过各种奇技淫巧,用 ES3 实现了大部分的 ES2017 原生标准库,同时还要严格遵循规范。

提供了es5、es6的polyfills,包括promises、symbols、collections、iterators、typed arrays、ECMAScript 7+ proposals、setImmediate 等等。 如果使用了 babel-runtime、babel-plugin-transform-runtime 或者 babel-polyfill,你就可以间接的引入了 core-js 标准库

blog.meathill.com/js/some-tip…

core-js 2 封版于 1.5 年之前,所以里面只有对 1.5 年之前 feature 的 polyfill,最近 1.5 年新增的 feature 都不支持,也就存在因为新功能没有 polyfill 于是在旧浏览器里失败的风险。

Stable ECMAScript features

Stable ECMAScript features had already been almost completely supported by core-js for a long time, however, core-js@3 introduced some new features:

  1. Added support of @@isConcatSpreadable and @@species well-known symbols, introduced in ECMAScript 2015, to all the methods which use them.
  2. Added Array.prototype.flat and Array.prototype.flatMap methods from ECMAScript 2018 (core-js@2 provided a polyfill for an old version of this proposal with Array.prototype.flatten).
  3. Added Object.fromEntries method, introduced in ECMAScript 2019.
  4. Added Symbol.prototype.description accessor, introduced ECMAScript 2019.

Some features that have already been available for a long time as proposals have been accepted in ES2016-ES2019 and are now marked as stable:

  1. Array.prototype.includes and %TypedArray%.prototype.includes methods (ECMAScript 2016)

  2. Object.values and Object.entries methods (ECMAScript 2017)

  3. Object.getOwnPropertyDescriptors method (ECMAScript 2017)

  4. String.prototype.padStart and String.prototype.padEnd methods (ECMAScript 2017)

  5. Promise.prototype.finally method (ECMAScript 2018)

  6. Symbol.asyncIterator well-known symbol (ECMAScript 2018)

  7. Object.prototype._ _ define(Getter|Setter) _ _ and Object.prototype._ _ lookup(Getter|Setter) _ _ methods (ECMAScript 2018)

  8. String.prototype.trim(Start|End|Left|Right) methods (ECMAScript 2019)

  9. Added many fixes for browsers bugs/issues. For example, Safari 12.0 Array.prototype.reverse bug has been fixed.