一文带你零基础认识Babel,Babel到底可以用来干嘛

目录

🌲 前言

🌲 Babel是什么?

🌲 Babel 的工作原理

◼️ 如何设置

◼️ Babel 有哪些值得注意的选项

◼️ @babel/core | @babel/cli | @babel/preset-env 到底做了哪些事情

🌲 Babel 的使用

◼️ 配置文件

◼️ Babel 配置文件的优先级

🌲 Plugins

如何使用插件

转换插件​

语法插件

插件顺序​

插件参数​

插件开发​

🌲 Presets

如何使用预设 

如何创建预设

预设的排列顺序​

预设的参数​

🌲 Polyfill

1、@babel/polyfill

2、babel-runtime

3、babel-plugin-transform-runtime

🎋 babel-runtime 和 babel-plugin-transform-runtime

🎋 使用技巧分享

🔆 参考文献

🔆 参考资料


  

🌲 前言

在此之前或许你已经用过babel,也许听说过什么 babel-loader babel-core、babel-cli、babel-plugin-…、babel-preset-env。

反正各种乱七八糟的做项目随便用一下就可以了,对他只有个一知半解,甚至不知道他到底是干什么的,反正项目要用,照着用就行了,至少博主之前的状态是这样,如果只对他有个一知半解甚至都不了解,那么项目出bug了你都不知道怎么去调试,只能复制 –> 粘贴 –> 百度。基于此,写下自己对Babel的理解。Babel是什么?

🌲 Babel是什么?

我们在babel的官方网站找到这样一句话:

Babel is a JavaScript compiler | Babel 是一个 JavaScript 编译器

Babel 是一个工具链,主要用于将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。下面列出的是 Babel 能为你做的事情: 

  • 语法转换
  • 通过 Polyfill 方式在目标环境中添加缺失的功能(通过引入第三方 polyfill 模块,例如 core-js
  • 源码转换(codemods)
  • 更多参考资料!(请查看这些 视频 以获得启发)

Babel是一个编译器,针对JavaScript,为什么会有Babel这样一个工具的存在?

本文默认你对es6、es7等有所涉足,我们在写es6+语法的时候是不是很方便,什么promise、async await,可是es6+语法写的虽然很酸爽,但是浏览器他不兼容啊,你想想你写的代码在浏览器上跑不起来,再好的语法、再好的特性又有什么用?

这个时候Babel这样一个工具出来了,它可以将我们写的es6+语法转换为浏览器兼容的语法,比如将箭头函数转换为普通函数,有了这样一个工具我们就即可以写酸爽的语法,又可以让使浏览器兼容。相信到这你已经知道了Babel的概念,并且可以脑补出Babel可以干什么。

🌲 Babel 的工作原理

Babel 使用 AST 把不兼容的代码编译成 es5 版本,因为大多数浏览器都支持这个版本的 JavaScript 代码。

◼️ 如何设置

整个配置过程包括:

1. 在控制台运行如下命令安装所需的包(package):

npm方式

npm install --save-dev @babel/core @babel/cli @babel/preset-env

Yarn方式

yarn add --dev @babel/core @babel/cli @babel/preset-env

2. 应用程序的根目录会默认创建一个 babel.config.json 文件(需要 v7.8.0 或更高版本)。Babel 将遍历 src 目录下的所有 JS 文件。

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "edge": "17",
          "firefox": "60",
          "chrome": "67",
          "safari": "11.1"
        },
        "useBuiltIns": "usage",
        "corejs": "3.6.5"
      }
    ]
  ]
}

上述浏览器列表仅用于示例。请根据你所需要支持的浏览器进行调整。参见 此处 以了解 @babel/preset-env 接受哪些参数。 

如果你使用的是 Babel 的旧版本,则文件名为 babel.config.js

const presets = [
  [
    "@babel/preset-env",
    {
      targets: {
        edge: "17",
        firefox: "60",
        chrome: "67",
        safari: "11.1",
      },
      useBuiltIns: "usage",
      corejs: "3.6.4",
    },
  ],
];

module.exports = { presets };

package.json 文件我们也不陌生,可以直接通过 npm init 命令来创建。或者我们还可以将 .babelrc 或.babelrc.json 中的配置信息作为 babel 键(key)的值添加到 package.json 文件中,如下所示:

{
    "name": "demo",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
      "test": "echo \"Error: no test specified\" && exit 1"
    },
    "author": "",
    "babel": {
      "presets": [...],
      "plugins": [...]
    }
}

当然我们除了将把配置写在上述这几种配置文件中,我们也可以写在构建工具的配置里。对于不同的构建工具,Babel 也提供了相应的配置项,例如 webpackbabel-loader 的配置项,其本质和配置文件是一样的。

配置文件中的主要配置项就是 pluginspresets 这两个数组,plugins为插件数组,presets 为预设数组。其他的例如 ignoreminified 等我们一般用不到。 

3. 运行此命令将 src 目录下的所有代码编译到 lib 目录:

./node_modules/.bin/babel src --out-dir lib

你可以利用 npm@5.2.0 所自带的 npm 包运行器将 ./node_modules/.bin/babel 命令缩短为 npx babel 

🎋 想要更详细的分步讲解,了解其工作原理以及每个工具的功能,可以参看:使用指南 · Babel 中文文档 | Babel中文网。 

◼️ Babel 有哪些值得注意的选项

{
  "presets":
  [
    [
      "@babel/oreset-env",
      {
        "targets":
        {
          "edge": "17",
          "firefox": "60",
          "chrome": "67",
          "safari": "11.1"
        },
        "useBuiltIns": "usage",
        "corejs": "3.6.5"
      }
    ]
  ],
}
  • 如果要缩减输出代码,需要增加选项 "minified": true
  • 如果要忽略某些文件,则添加 ignore: ["file or directory path"]
  • 只编译特定的文件或文件夹,则添加如下代码:
// For Files
"only" : ["./src/some_file.js"],
// For Directory
"only" : ["./src"],

◼️ @babel/core | @babel/cli | @babel/preset-env 到底做了哪些事情

  • @babel/core:Babel 的所有核心功能都在这里
  • @babel/cli:提供了 CLI 工具,使我们能够运行 npm run build
  • @babel/preset-env:提供预置功能

🌲 Babel 的使用

  • 单体文件
  • 命令行
  • 配合Webpack使用

本文将介绍Babel配合webpack使用的情况

◼️ 配置文件

babel的配置文件常用的有几种:.babelrc,.babelrc.json、babelrc.js、babel.config.json、babel.config.js

以下是官网关于配置文件类型的介绍:Config Files

Configuration File Types | 配置文件类型


Babel has two parallel config file formats, which can be used together, or independently.

History

VersionChanges
v7.21.0Support .babelrc.cts and babel.config.cts (Experimental)
v7.8.0Support .babelrc.mjs and babel.config.mjs
v7.7.0Support .babelrc.json.babelrc.cjsbabel.config.jsonbabel.config.cjs
  • Project-wide configuration
    • babel.config.* files, with the following extensions: .json.js.cjs.mjs.cts.
  • File-relative configuration
    • .babelrc.* files, with the following extensions: .json.js.cjs.mjs.cts.
    • .babelrc file, with no extension.
    • package.json files, with a "babel" key.

大致翻译一下:

Babel 有两种并行的配置文件格式,可以一起使用,也可以分开使用。

◾ 项目范围的配置 ——全局配置

你是否采用的是单一仓库(monorepo)模式?你是否需要编译 node_modules


那么 babel.config.json 文件可以满足你的的需求!
 

👉 根目录:babel.config.json会影响整个项目中的代码,包含node_modules中的代码

babel.config.* 文件,具有不同的拓展名(.json.js.cjs, .mjs.cts.)

babel.config.js 是按照 commonjs 导出对象,可以写js的逻辑。

相对文件的配置 ——局部配置

你的配置文件是否仅适用于项目的某个部分?那么 .babelrc.json 文件适合你!
 

👉 项目中:babelrc 只会影响本项目中的代码

.babelrc.* 文件,具有不同的拓展名(.json.js.cjs, .mjs.cts.)

.babelrc 文件,不带扩展名

babel官方建议使用 babel.config.json 格式的配置文件。 Babel 自身使用的就是这种格式

🎋 配置文件的使用场景,更多详细信息:配置 Babel · Babel 中文文档 | Babel中文网

◼️ Babel 配置文件的优先级

从低到高依次为:

  • babel.config.json
  • babelrc.json
  • @babel/cli

依照优先级,babel.config.json 会被 .babelrc 覆盖,依次类推。

注意:.babelrc 文件放置在项目的根目录中时和 babel.config.js 效果一致,我们只需要选择一种创建即可。如果两种类型的配置文件都存在,.babelrc 会覆盖 babel.config.js 的配置。

理解 babel.config.js 和 babelrc

  • babel.config.js 或是babel.config.json当前项目维度 (Project Wide)的配置文件,相当于一份全局配置,如果 babel 决定应用这个配置文件,则一定会应用到所有文件的转换。
  • .babelrc 或是.babelrc.js、.babelrc.json相对文件(File Relative)的配置文件, babel 决定一个 js 文件应用哪些配置文件时,会执行如下策略: 如果要转换的这个 js 文件在当前项目内,则会先递归向上搜索最近的一个 .babelrc 文件(直到遇到package.json目录),将其与全局配置合并。如果这个 js 文件不在当前项目内,则只应用全局配置,忽略与这个文件相关的 .babelrc 。

这两个我更愿意将其称为全局配置 (babel.config.js) 和局部配置 (.babelrc) 便于理解一些。


由此便引入了一个非常关键的对 **当前项目** 的定义问题。
 

这个定义在官方文档就有说明

New in Babel 7.x, Babel has a concept of a "root" directory, which defaults to the current working directory. For project-wide configuration, Babel will automatically search for a babel.config.json file, or an equivalent one using the supported extensions, in this root directory.

大白话就是: 当前所在目录即为*当前项目*根目录,babel 会读取当前所在目录的 babel.config.js 作为全局配置。
 

—— 为了清晰记忆上面的规则我来稍微总结一下:
 

babel 会在当前执行目录搜索 babel.config.js, 若有则读取并作为全局配置,若无则全局配置为空。然后在转换一个具体的js文件时会去判断,如果这个文件在当前执行目录外面,则只应用全局配置。如果这个文件在当前执行路径内,则会去基于这个文件向上搜索最近的一个 .babelrc ,将其与全局配置合并作为转换这个文件的配置。


babel的配置文件的配置方法都一样,本文以 .babelrc 配置文件为主 

在初次接触Babel我们只要用到一下两项配置:预设插件

预设只是插件的集合,您可以在plugins阵列中单独包含插件,也可以在阵列中包含插件集合presets,如果插件是集合(预设)的一部分,则不必单独包含它plugins当你包含它们时,npm包也是如此package.json

预设是插件的集合或正如他们所说:预设是可共享的.babelrc配置或简单的一系列babel插件。

* .babelrc

{
  "presets": [...],
  "plugins": [...]
}

下面介绍presets与plugins

🌲 Plugins

Plugins顾名思义插件。

Babel的代码转换是通过在配置文件中应用插件(或预设)来启用的。

babel 本身不具有任何转化功能,我们要的代码要转换某些功能,比如将es6转换为es5,我们就需要下载相应的插件,并且将这些插件配置到.babelrc文件的plguins里面。

如何使用插件

如果插件是在npm上,你可以传入插件的名称,Babel会检查它是否安装在node_modules中。这是添加到插件配置选项,它接受一个数组。

* .babelrc

{
  "plugins": ["babel-plugin-myPlugin", "@babel/plugin-transform-runtime"]
}

你也可以为你的插件指定一个相对/绝对路径。

* .babelrc

{
  "plugins": ["./node_modules/asdf/plugin"]
}

默认情况下,Babel希望插件的名称中有一个Babel -plugin或Babel -preset前缀。为了避免重复,Babel有一个名称规范化阶段,将在加载项时自动添加这些前缀。
 

有关配置插件或预置路径的更多细节,请参阅名称规范化:参数 · Babel 中文文档 | Babel中文网

插件的使用方法:举个例子

Babel 使用插件来执行代码转换,插件其实就是用 JavaScript 所写的程序片段。比如,将箭头函数转换为浏览器能识别的普通函数,我们就需要用到 @babel/plugin-transform-arrow-functions插件,并将其添加到配置文件,它的代码实现如下:

1. 首先下载插件

npm i @babel/plugin-transform-arrow-functions -D

2. 添加至配置文件

* .babelrc

{
  "plugins":[
     "@babel/plugin-transform-arrow-functions"
   ]
}

这样babel就能够将箭头函数转换为普通函数了

//转换前
var a = () => {};
//转换后
var a = function () {};

🙋‍♂️ Q:什么?你怎么知道要用这个插件?

🤷 A:看文档啊!插件 · Babel 中文文档 | Babel中文网

转换插件

转换插件用于转换你的代码。

转换插件将启用相应的语法插件,因此你不必同时指定这两种插件。

语法插件

大多数语法都可以通过Babel进行转换。在更罕见的情况下(如果转换还没有实现,或者没有默认的方式来实现),你可以使用@babel/plugin-syntax-bigint这样的插件,只允许Babel解析特定类型的语法。或者您想保留源代码,因为您只想让Babel进行代码分析或代码删除。

注意:如果已经使用了相应的转换插件,则不需要指定语法插件,因为它会自动启用它。

* .babelrc 

或者,你也可以通过 Babel 解析器传递任何 plugins 参数 :

JSON

{
  "parserOpts": {
    "plugins": ["jsx", "flow"]
  }
}

插件顺序

插件的排列顺序很重要。

这意味着如果两个转换插件都将处理“程序(Program)”的某个代码片段,则将根据转换插件或 preset 的排列顺序依次执行。

  • 插件在 Presets 前运行。
  • 插件顺序从前往后排列。
  • Preset 顺序是颠倒的(从后往前)。

例如:

* .babelrc 

{
  "plugins": ["transform-decorators-legacy", "transform-class-properties"]
}

先执行 transform-decorators-legacy ,在执行 transform-class-properties

重要的时,preset 的顺序是 颠倒的。如下设置:

* .babelrc 

{
  "presets": ["@babel/preset-env", "@babel/preset-react"]
}

将按如下顺序执行: 首先是 @babel/preset-react,然后是 @babel/preset-env

插件参数

插件和 preset 都可以接受参数,参数由插件名和参数对象组成一个数组,可以在配置文件中设置。

如果不指定参数,下面这几种形式都是一样的:

* .babelrc 

{
  "plugins": ["pluginA", ["pluginA"], ["pluginA", {}]]
}

要指定参数,请传递一个以参数名作为键(key)的对象。

* .babelrc 

{
  "plugins": [
    [
      "transform-async-to-module-method",
      {
        "module": "bluebird",
        "method": "coroutine"
      }
    ]
  ]
}

preset 的设置参数的工作原理完全相同:

* .babelrc 

{
  "presets": [
    [
      "env",
      {
        "loose": true,
        "modules": false
      }
    ]
  ]
}

插件开发

请参考 babel-handbook 学习如何创建自己的插件。

一个简单的用于反转名称顺序的插件(来自于首页):

JavaScript

export default function() {
  return {
    visitor: {
      Identifier(path) {
        const name = path.node.name;
        // reverse the name: JavaScript -> tpircSavaJ
        path.node.name = name
          .split("")
          .reverse()
          .join("");
      },
    },
  };
}

🌲 Presets

Presets顾名思义预设。预设是可共享的.babelrc配置或简单的一系列babel插件.

我们要转换一些语法就得使用各种插件,并且添加到配置文件,如果每次项目需要的babel插件都差不多,而我们每次都要进行重复的下载,配置工作,这样效率是不是很低,很繁琐。

这个时候我们就可以利用presets这个功能,将一些常用的babel插件配置放入预设中,下载后直接将这个预设放入配置文件即可。具体操作可参照文档:预设(Presets) · Babel 中文文档 | Babel中文网

如何使用预设 

在Babel配置中,如果预设是在npm上,你可以传入预设的名称,Babel会检查它是否已经安装在node_modules中。这被添加到presets配置选项中,该选项接受一个数组。

* .babelrc

{
  "presets": ["babel-preset-myPreset", "@babel/preset-env"]
}

除此之外,您也可以指定预设的相对路径或绝对路径。 

{
  "presets": ["./myProject/myPreset"]
}

默认情况下,Babel希望插件的名称中有一个Babel -plugin或Babel -preset前缀。为了避免重复,Babel有一个名称规范化阶段,将在加载项时自动添加这些前缀。
 

有关配置插件或预置路径的更多细节,请参阅名称规范化:参数 · Babel 中文文档 | Babel中文网

预设的使用方法,举个例子

例如,你经常看到的 @babel/preset-env@babel/preset-react

1.下载preset

npm i @babel/preset-env

2. 配置文件

* .babelrc

{
  "presets":[ "preset-env" ]
}

🙋‍♂️ Q:我怎么知道有哪些预设?

🤷 A:看文档 ,在npm上搜preset

🙋‍♂️ Q:我怎么知道预设里面有哪些插件?

🤷 A:要知道预设里面有哪些插件,最好的方式就是搜npm看他的依赖项

可以看到preset-env有 76个插件

不过,preset 分为以下几种:

最常见的预设是官方预设实验预设

◾ 第一种:官方内容 env, react, flow 等

官方提供的预设

babel官方已经针对常用环境编写了一些预设(preset):

◾ 第二种:Stage-X(实验性质的预设),这里面包含的都是当年最新规范的草案,每年更新。

TC39 将提案分为以下几个阶段:

  • Stage 0 - 设想(Strawman):只是一个想法,经过 TC39 成员提出即可,可能有 Babel插件。
  • Stage 1 - 建议(Proposal):这是值得跟进的,初步尝试。
  • Stage 2 - 草案(Draft):初始规范。
  • Stage 3 - 候选(Candidate):完成规范并在浏览器上初步实现。
  • Stage 4 - 完成(Finished):将添加到下一个年度版本发布中。

这也就是你在早期的项目或文档中看到 stage-0,stage-1的字眼,原来他们都是预设,不过现在你不用纠结这个问题Babel7已经放弃stage预设了。

如何创建预设

如需创建一个自己的预设(无论是为了本地使用还是发布到 npm),需要导出(export)一个配置对象。

可以是返回一个插件数组...

JavaScript

module.exports = function() {
  return {
    plugins: ["pluginA", "pluginB", "pluginC"],
  };
};

preset 可以包含其他的 preset,以及带有参数的插件。

JavaScript

module.exports = () => ({
  presets: [require("@babel/preset-env")],
  plugins: [
    [require("@babel/plugin-proposal-class-properties"), { loose: true }],
    require("@babel/plugin-proposal-object-rest-spread"),
  ],
});

有关更多信息,请参考 babel 手册 中关于 preset 的部分。

预设的排列顺序

Preset 是逆序排列的(从后往前)。

 * .babelrc

{
  "presets": ["a", "b", "c"]
}

将按如下顺序执行: cb 然后是 a

这主要是为了确保向后兼容,由于大多数用户将 "es2015" 放在 "stage-0" 之前。

预设的参数

插件和 preset 都可以接受参数,参数由插件名和参数对象组成一个数组,可以在配置文件中设置。

如果不指定参数,下面这几种形式都是一样的:

 * .babelrc

{
  "presets": [
    "presetA", // bare string
    ["presetA"], // wrapped in array
    ["presetA", {}] // 2nd argument is an empty options object
  ]
}

要指定参数,请传递一个以参数名作为键(key)的对象。

 * .babelrc

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "loose": true,
        "modules": false
      }
    ]
  ]
}

🌲 Polyfill

Plolyfill 垫片。| 使用指南 · Babel 中文文档 | Babel中文网

babel默认只转换新的 JavaScript 语法,比如箭头函数、扩展运算(spread)不转换新的 API,例如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise 等全局对象,以及一些定义在全局对象上的方法(比如 Object.assign)都不会转译。如果想使用这些新的对象和方法,则需要为当前环境提供一个垫片(polyfill)。

Polyfill主要有三种

1、@babel/polyfill

Babel 包含一个polyfill 库即@babel/polyfill。@babel/polyfill模块包含 core-js 和一个自定义的 regenerator runtime 来模拟完整的 ES2015+ 环境。

◼️ 什么是regenerator?

npm地址:regenerator-runtime - npm

github地址:https://github.com/facebook/regenerator

官方对他的描述直译过来是;

Standalone runtime for Regenerator-compiled generator and async functions.
 

重新生成编译的生成器和异步函数的独立运行时 | Regenerator编译的生成器和异步函数的独立运行时。

在网上找到这样一句话,解释道:

生成器函数、async、await函数经babel编译后,regenerator-runtime模块用于提供功能实现。

博主对于生成器(regenerator)也是一知半解,这里我们就简单的理解成,项目中要使用async、await函数就必须使用这个库吧!🤔 (对于regenerator有知道更详细的,欢迎下方留言已作解释)。

◼️ 什么是core-js?

npm地址:core-js - npm

下面是babel官方对core-js的解释:

Modular standard library for JavaScript. Includes polyfills for ECMAScript up to 2023promisessymbolscollections, iterators, typed arrays, many other features, ECMAScript proposalssome cross-platform WHATWG / W3C features and proposals like URL. You can load only required features or use it without global namespace pollution.

直译:JavaScript的模块化标准库。包括到2023年的ECMAScript的Polyfill: promises、symbols、 collections 、iterators、 typed 、arrays、许多其他特性、ECMAScript提案、跨平台WHATWG / W3C特性和提案,比如URL。您可以只加载所需的特性,或者在不污染全局命名空间的情况下使用它。

简单粗暴的理解就是,你要使用一些js高级特性,如promise就得使用这个库。

经过上面潦草的描述,我们在总结一下:

由于babel只能将es6+语法转换为低级语法,而当我们使用一些高级特性时比如 async、await类似的Api,babel就显得无能为力了,因为babel无法实现这些高级Api的功能,这个时候就需要一个垫片(polyfill),而babel又包含了一个polyfill叫@babel/polyfill,这个polyfill本身也无法实现像async等高级API的功能,但是市面上有现成的封装好的类库实现了,于是@babel/polyfill将他们包含进来。这样当我们引入@babel/polyfill时,就可以丝滑的写高级语法了!

◼️ 使用方式

1. 下载

npm方式

npm install --save @babel/polyfill

注意,使用 --save 参数而不是 --save-dev,因为这是一个需要在你的源码之前运行的 polyfill。也就是说polyfill要在源代码之前运行!

Yarn方式  

yarn add @babel/polyfill

 2. 在入口文件导入

import "@babel/polyfill";

🚨 从 Babel 7.4.0 版本开始,这个软件包已经不建议使用了,建议直接包含 core-js/stable (用于模拟 ECMAScript 的功能): 

import "core-js/stable";

当然在webpack中你也可以这样干,在@babel/polyfill的描述有这样一段

The polyfill is provided as a convenience but you should use it with @babel/preset-env and the useBuiltIns option so that it doesn’t include the whole polyfill which isn’t always needed. Otherwise, we would recommend you import the individual polyfills manually

提供polyfill是为了方便,但您应该将其与@babel/preset-env和use Built-Ins选项一起使用,这样它就不会包括并非总是需要的整个polyfill。否则,我们建议您手动导入单个polyfill

大概意思就是建议我们将@babel/polyfill和@babel/preset-env配合使用并根据需求设置 useBuiltlns选项,这样就不至于将这个polyfill加载进来,显得很大。不然就使用手动导入各个polyfill的方式。

—— 在webpack中我们可以将@babel/polyfill和@babel/preset-env配合使用!

主要是设置 useBuiltlns选项。更多preset-env的配置请参照:@babel/preset-env · Babel

{
  "presets" : [
    ["@babel/preset-env",{
      "targets": {
        "browsers": [
          "last 2 versions",
          "not ie <= 9"
        ]
      },
      "useBuiltIns": "usage"
    }]
  ]
}

useBuiltlns:false:此时不对 polyfill 做操作。如果引入 @babel/polyfill,则无视配置的浏览器兼容,引入所有的 polyfill。

useBuiltlns:usage:按需加载polyfill,根据配置的浏览器兼容以及代码所用到的polyfill,不至于将所有polyfill加载进来,使用这种方式我们不用手动导入polyfill,但是需要安装。

useBuiltlns:entry:根据配置的浏览器兼容,引入浏览器不兼容的 polyfill。需要在入口文件手动添加 import @babel/polyfill,会自动根据 browserslist 替换成浏览器不兼容的所有 polyfill。

—— @babel/polyfill带来的问题

babel-polyfill,通过改写全局prototype的方式实现,它会加载整个polyfill,针对编译的代码中新的API进行处理,并且在代码中插入一些帮助函数,比较适合单独运行的项目。

babel-polyfill解决了Babel不转换新API的问题,但是直接在代码中插入帮助函数,会导致污染了全局环境,并且不同的代码文件中包含重复的代码,导致编译后的代码体积变大。虽然这对于应用程序或命令行工具来说可能是好事,但如果你的代码打算发布为供其他人使用的库,或你无法完全控制代码运行的环境,则会成为问题。

后来@babel/polyfill凉了,通过官方我们知道,从 Babel 7.4.0起 @babel/polyfill已经被弃用了。
之前使用的方式现在改成了

//import "@babel/polyfill"; //之前的写法
import "core-js/stable";
import "regenerator-runtime/runtime";

2、babel-runtime

npm install --save babel-runtime

为了解决 @babel/polyfill带来的问题,Babel提供了单独的包babel-runtime用于提供编译模块的工具函数,启用插件babel-plugin-transform-runtime后,Babel就会使用babel-runtime下的工具函数。

babel-runtime插件能够将这些工具函数的代码转换成require语句,指向为对babel-runtime的引用。每当要转译一个api时都要手动加上require(‘babel-runtime’)。简单说 babel-runtime 更像是一种按需加载的实现,比如,你哪里需要使用 Promise,只要在这个文件头部 require Promise from ‘babel-runtime/core-js/promise’就行了。

不过如果你许多文件都要使用 Promise,难道每个文件都要 import 一遍不成?

3、babel-plugin-transform-runtime

为了方便使用 babel-runtime,解决手动 require 的苦恼。它会分析我们的 ast 中,是否有引用 babel-rumtime 中的垫片(通过映射关系),如果有,就会在当前模块顶部插入我们需要的垫片。

transform-runtime 是利用 plugin 自动识别并替换代码中的新特性,你不需要再引入,只需要装好 babel-runtime 和 配好 plugin 就可以了。

好处是按需替换,检测到你需要哪个,就引入哪个 polyfill,如果只用了一部分,打包完的文件体积对比 babel-polyfill 会小很多。而且 transform-runtime 不会污染原生的对象,方法,也不会对其他 polyfill 产生影响。

所以, transform-runtime 的方式更适合开发工具包,一方面是体积够小,另一方面是用户(开发者)不会因为引用了我们的工具包而污染了全局的原生方法,产生副作用,还是应该留给用户自己去选择。

🎋 babel-runtime 和 babel-plugin-transform-runtime

在大多数情况下,你应该安装 babel-plugin-transform-runtime 作为开发依赖(使用 --save-dev),并且将 babel-runtime 作为生产依赖(使用 --save)。这个看vue-cli生成的 package.json就能发现。

因为babel编译es6到es5的过程中,babel-plugin-transform-runtime这个插件会自动polyfill es5不支持的特性,这些polyfill包就是在babel-runtime这个包里(core-js 、regenerator等)

npm install —save-dev babel-plugin-transform-runtime

npm install —save babel-runtime

🎋 使用技巧分享

以下内容摘自:babel从入门到精通_babel学习

◼️ @babel/preset-env 包含了一些基本es语法转换的插件(箭头函数、类转换等等),同时还支持polyfill,有usage跟entry模式,但是preset-env添加polyfill会像之前使用@babel/polyfill一样,会污染全局变量。

◼️ @babel/plugin-transform-runtime 主要是利用@babel/runtime提取了一些公共的babel帮助函数,同时也支持polyfill的添加,添加的polyfill都是以一个局部变量的形式引入,不会污染全局变量。

◾ 如果你做的是一个二方库,然后需要被别人依赖,那么建议使用@babel/plugin-transform-runtime来引入polyfill,因为你要尽可能的专注于做自己的事,而不是说去影响别人,语法转换可以使用preset-env预设,比如以下配置:

module.exports = {
  presets: [['@babel/preset-env']],
  plugins: [
    [
      '@babel/plugin-transform-runtime',
      {
        corejs: { version: 3, proposals: true },
        helpers: true,
        useESModules: true,
        regenerator: true,
        absoluteRuntime: './node_modules'
      }
    ]
  ]
}

◾ 如果你做的是一个普通的业务项目的话,可以用preset-env来转换语法和polyfill,然后再利用@babel/plugin-transform-runtime来引入helpers跟generator做到代码重复利用,比如以下配置:

module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        corejs: 3,
        useBuiltIns: 'usage'
      }
    ]
  ],
  plugins: [
    [
      '@babel/plugin-transform-runtime',
      {
        corejs: false,
        helpers: true,
        useESModules: false,
        regenerator: true,
        absoluteRuntime: './node_modules'
      }
    ]
  ]
}

◾ @babel/plugin-transform-runtime 常见配置参照@babel/plugin-transform-runtime · Babel

◼️ 下面再介绍几个与Babel相关的:

  • @babel/core:babel的核心库
  • babel-loader:使用webpack时作为有个loader在代码混淆之前进行代码转换
  • @babel/preset-env:babel预设的一种
  • @babel/cli:允许使用babel命令转译文件

到此相信你已经对babel有个初步了解,还有很多细节本文没有提及,欢迎指教。


🔆 参考文献

Babel 中文文档 | Babel中文网 · Babel 中文文档 | Babel中文网

预设(Presets) · Babel 中文文档 | Babel中文网

插件 · Babel 中文文档 | Babel中文网

🔆 参考资料

Babel 插件有啥用? - 知乎

Babel 配置文件 - 简书

理解babel.config.js和babelrc

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

儒雅的烤地瓜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值