Babel 进阶一

参考文档

@babel/core 核心功能
@babel/cli 命令行工具
@babel/preset-env 转换语法
@babel/polyfill 补充 API、提供 regeneratorRuntime 函数
core-js 补充 API
regenerator-runtime 提供 regeneratorRuntime 函数
@babel/plugin-transform-runtime 替换内联帮助函数、替换全局 API、提供 regeneratorRuntime 函数

本章介绍除了 @babel/plugin-transform-runtime 包之外的其他包的使用。

是否受到 browserslist 影响

  • @babel/preset-env 负责转换语法,@babel/polyfill 负责补充 API
  • @babel/preset-env 转换语法会受到 browserslist 目标环境的影响
  • @babel/polyfill 补充 API 默认不受 browserslist 目标环境的影响
  • 配置 @babel/preset-env 的 useBuiltIns 选项为 'entry' 或 'usage' 时候,补充 API 会受到 browserslist 目标环境的影响。

@babel/core

@babel/core 是我们使用 Babel 进行转码的核心 NPM 包,我们使用的 babel-clibabel-node 都依赖这个包。无论我们是通过命令行转码,还是通过 Webpack 进行转码,底层都是通过 Node 来调用 @babel/core 相关功能 API 来进行的。因此我们在前端开发的时候,只要用到 Babel 转码,都需要安装这个包。

在我们的工程目录里,执行下面的命令,安装 @babel/core

npm i -D @babel/core

以下例子通过 Node 调用 @babel/core 的 API 来进行转码

var babelCore = require('@babel/core');
var es6Code = 'var fn = (num) => num + 2';
var options = {
  presets: ['@babel/preset-env']
};
var result = babelCore.transform(es6Code, options);
console.log(result.code);

@babel/cli

@babel/cli 是一个 NPM 包,安装了它之后就可以在命令行使用 Babel 进行转码。安装 @babel/cli 之前必须先安装 @babel/core

全局安装

npm i @babel/core @babel/cli -g

本地安装(推荐)

npm i -D @babel/core @babel/cli

使用

# 转码并输出到标准输出
npx babel a.js
# 转码并生成文件
npx babel a.js -o b.js
# 对整个目录进行转码
npx babel input -d output

@babel/polyfill

@babel/polyfill 是一个 NPM 包,通过在前端工程的入口文件引入 @babel/polyfill 包,可以在全局范围补齐新的 API。@babel/polyfill 其实就是两个 npm 包的集合:core-jsregenerator-runtime,从 babel 7.4 开始,官方推荐单独安装 core-jsregenerator-runtime 来代替 @babel/polyfill

  • 安装 @babel/polyfill 包的时候,会自动安装 core-jsregenerator-runtime,其中 core-js 的版本为 2
  • @babel/polyfill 会自动调用 core-js 来添加 API。
  • @babel/polyfill 会自动调用regenerator-runtime 来定义 regeneratorRuntime 函数。
  • 在前端工程的入口文件的开头引入 @babel/polyfill
  • 只能引入一次 @babel/polyfill,多次引用会报错。
  • 通过 webpack 构建之后,import '@babel/polyfill' 默认会将所有 ES6 API 添加到代码中,不会考虑 browserslist 当中的目标环境。
  • @babel/polyfill 会在构建后的代码中添加全局对象 API、全局对象的属性 API、实例的原型属性 API。
  • @babel/polyfill 会在构建后的代码中添加 regeneratorRuntime 函数。
  • 通过修改 @babel/preset-env 的 useBuiltIns 选项,可以影响 @babel/polyfill 的使用方式。
npm i @babel/polyfill
/* 入口文件 */
import '@babel/polyfill'

let todos = new Set(['vue', 'react']);
let promise = Promise.resolve('ok');
function* gen () {
  yield* todos;
}
for (let todo of gen()) {
  console.log(todo);
}

core-js

  • 安装 core-jsregenerator-runtime 的时候,不需要安装 @babel/polyfill
  • 安装 core-js 可以指定版本,默认最新的版本,2022 年 4 月最新的版本为 3。
  • 安装 @babel/polyfill 时候自动安装的 core-js 版本为 2。
  • 新版本的 core-js@3core-js@2 多了许多新的 API,例如 Array.prototype.includes
  • 在前端工程的入口文件的开头 import 'core-js/stable' 来导入 API,只能导入一次,导入多次会报错。
  • 如果使用 import 'core-js/stable' 导入 API,需要安装 core-js@3 版本,因为 core-js@2 版本没有 core-js/stable
  • 通过 webpack 构建之后,import 'core-js/stable' 默认会将所有的 ES6 API 添加到代码中,不会考虑 browserslist 当中的目标环境。
  • core-js 会在构建后的代码中添加全局对象 API、全局对象的属性 API、实例的原型属性 API。
npm i core-js

配置文件:

const presets = ['@babel/env']
const plugins = []

module.exports = {
  presets,
  plugins
}

转换之前:

import "core-js/stable";

var promise = Promise.resolve();

经过 Babel 转换之后:

"use strict";

// 下面的一行代码经过 Webpack 打包之后,会将 core-js/stable 包中的所有 API 引入到最终的文件中。
require("core-js/stable");

var promise = Promise.resolve();

经过 Webpack 打包之后,core-js/stable 包中的所有 API 都会被引入到最终的文件中。

regenerator-runtime

  • 如果代码中有 Generator/async 函数语法,则在语法转换的时候,@babel/preset-env 会将 Generator/async 函数转换为调用 regeneratorRuntime 函数。
  • regenerator-runtime 包提供了 regeneratorRuntime 函数的定义,regenerator-runtime 包可以单独安装,也可以在安装 @babel/polyfill 的时候自动安装。
  • 通过 import regenerator-runtime 可以获得 regeneratorRuntime 函数的定义。
  • 通过 import @babel/polyfill 也可以获得 regeneratorRuntime 函数的定义。
  • 一般在前端工程的入口文件 import regenerator-runtime 或者 import regenerator-runtime/runtime 来引入 regeneratorRuntime 函数的定义,只能引入一次,引入多次会报错。
npm i regenerator-runtime

配置文件:

const presets = ['@babel/env']
const plugins = []

module.exports = {
  presets,
  plugins
}

转换之前:

import "regenerator-runtime/runtime";

function* gen () {
  yield 100;
  yield 200;
}

经过 Babel 转换之后:

"use strict";

// 下面的一行代码,经过 Webpack 打包之后,会引入 regenerator-runtime 包中的 regeneratorRuntime 函数定义。
require("regenerator-runtime");

var _marked = /*#__PURE__*/regeneratorRuntime.mark(gen);

function gen() {
  return regeneratorRuntime.wrap(function gen$(_context) {
    while (1) {
      switch (_context.prev = _context.next) {
        case 0:
          _context.next = 2;
          return 100;

        case 2:
          _context.next = 4;
          return 200;

        case 4:
        case "end":
          return _context.stop();
      }
    }
  }, _marked);
}

经过 Webpack 打包之后,会将 regenerator-runtime 包中的 regeneratorRuntime 函数引入到最终的文件中。

@babel/preset-env

@babel/preset-env 预设集合了自 ES2015 以来所有最新的语法插件,用来对标准的 ES6 语法转换。它只转换 ES6 语法,不会添加 ES6 新增的 API。@babel/env@babel/preset-env 的简写。

通过配置文件,可以配置 @babel/preset-env 预设需要的参数,常用的参数:

  • targets
  • useBuiltIns
  • corejs
  • modules

targets

该参数可以取值为字符串、字符串数组或对象。如果不设置,默认值为空对象 {}

与 browserslist 功能一样,指定转码的目标环境。@babel/preset-env 转换语法的时候,将目标环境不支持的 ES6 语法进行转码,目标环境支持的 ES6 语法不转码。可以通过 browserslist 指定目标环境,也可以通过 targets 属性指定目标环境。如果没有指定目标环境,@babel/preset-env 默认会将所有的 ES6 语法进行编码。

  • 如果指定了 targets 选项,就不使用 browserslist 的配置。
  • 如果没有指定 targets 选项,则使用 browserslist 的配置。
  • 推荐使用 browserslist 的配置,不推荐配置 targets 属性。
const presets = [
  [
    '@babel/preset-env',
    {
      targets: [
        '> 1%',
        'last 2 versions',
        'not ie <= 8'
      ]
    }
  ]
]
const plugins = []

module.exports = {
  presets,
  plugins
}

useBuiltIns

@babel/preset-env 的 useBuiltIns 参数主要和 @babel/polyfill 的行为有关,该参数值可以是 "usage" 、 "entry" 或 false。如果不设置,默认值为 false。

useBuiltIns: false

useBuiltIns 没有配置或取值为 false 时候,在代码中 import '@babel/polyfill',打包之后会将所有的 ES6 API 都导入到代码中。不管是否通过 browserslist 或者 targets 属性设置了目标环境,import '@babel/polyfill' 都会将所有的 ES6 API 都导入到代码中,哪怕目标环境支持的 API 也会导入进来。

useBuiltIns: false 代表 babel 不会对源码中的 import '@babel/polyfill' 做任何转换,经过 babel 转码之后,import '@babel/polyfill' 仍然存在与源码中。webpack 打包的时候,会引入 @babel/polyfill 包的所有模块,也就是引入全部的 ES6 API。

/* babel.config.js */
const presets = [
  [
    '@babel/env',
    {
      targets: ['chrome 60'],
      useBuiltIns: false
    }
  ]
]
const plugins = []

module.exports = {
  presets,
  plugins
}
/* entry.js */
// 导入所有 API
import '@babel/polyfill';

var promise = Promise.resolve('ok');
console.log(promise);

useBuiltIns: 'entry'

useBuiltIns 取值为 'entry' 的时候,在代码中 import '@babel/polyfill',会根据 browserslist 或者 target 属性配置的目标环境,找出目标环境需要的 ES6 API 进行部分导入。

useBuiltIns 值为 'entry' 的时候,最好同时设置 corejs 属性。

userBuiltIns: 'entry' 在 babel 转码的时候,会将 import '@babel/polyfill' 语句转换为一堆 import 语句,来引入全部的 ES6 API。如果指定了 .browserslistrc 文件,则只 import 浏览器不支持的 ES6 API。

/* babel.config.js */
const presets = [
  [
    '@babel/env',
    {
      targets: ['chrome 60'],
      useBuiltIns: 'entry',
      corejs: 2
    }
  ]
]
const plugins = []

module.exports = {
  presets,
  plugins
}
/* entry.js */
// 导入 chrome 60 浏览器不支持的所有 API
import '@babel/polyfill';

var promise = Promise.resolve('ok');
console.log(promise);

useBuiltIns: 'usage'

useBuiltIns 取值为 'usage' 时候,不需要在文件中 import '@babel/polyfill' 或者 import 'core-js/stable' import 'regenerator-runtime/runtime',转码时候会根据 browserslist 或者 targets 属性设置的目标环境,自动导入当前代码中使用到的且目标环境不支持的 API。

useBuiltIns 值为 'usage' 的时候,最好同时设置 corejs 属性。

/* babel.config.js */
const presets = [
  [
    '@babel/env',
    {
      targets: ['chrome 60'],
      useBuiltIns: 'usage',
      corejs: 2
    }
  ]
]
const plugins = []

module.exports = {
  presets,
  plugins
}
/* entry.js */
// 自动导入当前代码中使用到的且 chrome 60 浏览器不支持的 API
var promise = Promise.resolve('ok');
console.log(promise);

useBuiltIns: 'entry' 与 useBuiltIns: 'usage' 对比

相同点:

  • 都需要参考 browserslist 或者 targets 属性设置的目标环境。
  • 都需要同时设置 corejs 属性。

不同点:

  • 'entry' 需要我们在项目入口处手动引入 polyfill,而 'usage' 不需要。
  • 'entry' 引入目标环境不支持的所有 API,而 'usage' 按需引入目标环境不支持,且当前代码中使用到的 API 。

corejs

该参数项的取值可以是 2 或 3,没有设置的时候取默认值为 2,指定转码的时候使用的 core-js 的版本。

这个参数只有 useBuiltIns 设置为 'usage' 或 'entry' 时,才会生效。

安装 @babel/polyfill 包的时候,会自动安装 core-js@2 版本,因此如果代码中通过 import '@babel/polyfill' 导入 API,则需要将 corejs 属性值设置为 2。

如果代码中通过 import 'core-js/stable'import 'regenerator-runtime/runtime' 导入 API,那么需要单独安装 core-js@3 版本,因为 core-js@2 版本没有 core-js/stable。这时候需要将 corejs 属性值设置为 3。

只要安装了 core-js@3 版本,corejs 属性就必须设置为 3,否则转码时候报错。由于 @babel/polyfill 内部是通过调用 core-js@2 来引入 API 的,所以这时候就不能使用 @babel/polyfill 了,只能使用 core-js/stable。如果此时还想要使用 @babel/polyfill,那么你需要再单独安装一个 core-js@2,并且将 corejs 属性设置为 2。

推荐要么使用 @babel/polyfill 并且将 corejs 设置为 2,要么使用 core-js/stable 并且将 corejs 设置为 3。@babel/polyfillcore-js@3 这两个包最好不要同时安装。

modules

这个参数的取值可以是"amd"、"umd" 、 "systemjs" 、 "commonjs" 、"cjs" 、"auto" 、false。在不设置的时候,取默认值"auto"。

  • 设置该参数用来将代码中的 ES6 的模块转换成其他的模块,常用 'auto' 或 false。
  • 在该参数值是 'auto' 'commonjs' 或不设置的时候,会将 ES6 的模块转化为 CommonJS 的模块。
  • 在该参数值是 false 的时候,保留 ES6 模块,不会转换 ES6 模块为其他模块。
  • 该参数只影响通过 Babel 转码后的代码,不影响通过 Webpack 打包最终生成的代码,因为不管使用 ES6 模块还是使用 CommonJS 模块,Webpack 的依赖图都一样,最终生成的代码都一样,都是通过 __webpack_require__() 这个内部的方法来引入模块。
  • 推荐将 modules 值设置为 false,使用 ES6 模块,这样在用 Webpack 打包的时候可以做静态分析。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容