为了解决这个问题,Babel 社区又提出了 @babel/runtime 的方案,@babel/runtime 不再修改原型,而是采用替换的方式,比如我们用 Promise,使用 @babel/polyfill 会产生一个 window.Promise 对象,而 @babel/runtime 则会生成一个 _Promise (示例而已)来替换掉我们代码中用到的 Promise。另外 @babel/runtime 还支持按需引入。下面以转换 Object.assign 为例,来看下 @babel/runtime 怎么使用。
安装依赖 @babel/runtime:npm i @babel/runtime;
安装 npm i -D @babel/plugin-transform-runtime 作为 Babel 插件;
安装需要转换 Object.assign 的插件:
npm i -D @babel/plugin-transform-object-assign
编写一个 runtime.js 文件,内容如下:
Object.assign({}, {a:1});
执行 npx babel runtime.js --plugins @babel/plugin-transform-runtime,@babel/plugin-transform-object-assign,最终的输出结果是:
import _extends from ‘@babel/runtime/helpers/extends'; _extends( {}, { a:1 } );
代码中自动引入了 @babel/runtime/helpers/extends 这个模块(所以要添加 @babel/runtime 依赖啊)。
@babel/runtime也不是完美的解决方案,由于 @babel/runtime 不修改原型,所以类似[].includes() 这类使用直接使用原型方法的语法是不能被转换的。
Tips:'@babel/polyfill'实际是 core-js和
regenerator-runtime的合集,所以如果要按需引入'@babel/polyfill'的某个模块,可以直接引入对应的
core-js 模块,但是手动引入的方式还是太费劲。
@babel/preset-env
铺垫了这么多,我们继续来讲 @babel/preset-env,前面介绍了@babel/preset-env 可以零配置的转化 ES6 代码,我们如果要精细化的使用 @babel/preset-env ,就需要配置对应的选项了,在 @babel/preset-env 的选项中,useBuiltIns 和 target 是最重要的两个, useBuiltIns 用来设置浏览器 polyfill,target 是为了目标浏览器或者对应的环境(browser/node)。
preset-env 的 useBuiltIns
前面介绍了 @babel/polyfill 和 @babel/runtime 两种方式来实现浏览器 polyfill,两种方式都比较繁琐,而且不够智能,我们可以使用 @babel/preset-env 的 useBuildIn 选项做 polyfill,这种方式简单而且智能。
useBuiltIns 默认为 false,可以使用的值有 usage 和 entry:
{ ‘presets': [ ‘@babel/preset-env', { ‘useBuiltnls': ‘usage|entry|false' } ] }
usage 表示明确使用到的 Polyfill 引用。在一些 ES2015+ 语法不支持的环境下,每个需要用到 Polyfill 的引用时,会自动加上,例如:
const p = new Promise(); [1,2].includes(1); ‘foobar'.includes(‘foo');
使用 useBuiltIns='usage' 编译之后,上面代码变成,真正的做到了按需加载,而且类似 [].includes() 这类直接使用原型方法的语法是能被转换的:
‘use strict' require(‘core-js/modules/es.array.includes'); require(‘core-js/modules/es.object.to-string'); require(‘core-js/modules/es.promise'); require(‘core-js/modules/es.string.includes'); var p = new Promise(); [1,2].includes(1); ‘foobar'.includes(‘foo');
entry 表示替换 import "@babel/polyfill";(新版本的 Babel,会提示直接引入 core-js或者regenerator-runtime/runtime来代替 @babel/polyfill)的全局声明,然后根据 targets 中浏览器版本的支持,将 polyfill 拆分引入,仅引入有浏览器不支持的 polyfill,所以 entry 相对 usage 使用起来相对麻烦一些,首先需要手动显性的引入 @babel/polyfill ,而且根据配置 targets 来确定输出,这样会导致代码实际用不到的 polyfill 也会被打包到输出文件,导致文件比较大。
一般情况下,个人建议直接使用 usage 就满足日常开发了。
需要提一下的是,polyfill 用到的 core-js 是可以指定版本的,比如使用 core-js@3,则首先安装依赖 npm i -S core-js@3,然后在 Babel 配置文件 .babelrc 中写上版本。
// .babelrc { ‘presets': [ [ ‘@babel/preset-env', { ‘useBuiltlns': ‘useage', ‘corejs': 3 } ] ] }
preset-env 的 target
假设希望代码中使用 ES6 的模板字面量`语法,但是实际执行代码的宿主浏览器是 IE 10 却不支持,那么我们可以使用</code>target指定目标浏览器了。
{ ‘presets': [ [ ‘@babel/preset-env', { ‘targets': { ‘browsers': ‘IE 10' } } ] ] }
如果我们代码是在 Node.js 环境执行的,则可以指定 Node.js 的版本号:
{ ‘presets': [ [ ‘env', { ‘@babel/preset-env': { ‘node: ‘8.9.3' } } ] ] }