【万字长文】深入浅出JavaScript编译器Babel

babel presets和plugins的区别

在 Babel 中,presetsplugins 是用于配置和应用不同的转换规则的两个重要概念,它们有以下区别:

  1. Presets(预设): Presets 是一组预定义的转换规则集合,它们被打包成一个模块并通过名称进行引用。这些预设包含了一组相关的插件,用于实现对特定环境或特性的转换。预设的目的是简化配置过程,使开发人员能够轻松地应用一组常用的转换规则。

    例如,@babel/preset-env 是 Babel 提供的一个常用预设,它根据目标环境和配置选项自动选择适合的插件,以实现对最新的 ECMAScript 语法和功能的转换。通过配置预设,你可以告诉 Babel 哪些特性需要转换,哪些特性不需要转换。

    配置示例:

    javascript 复制代码
    {
      "presets": ["@babel/preset-env"]
    }
  2. Plugins(插件): 插件是独立的转换规则,用于对代码进行具体的转换。每个插件通常负责处理一个特定的转换任务,例如转换箭头函数、转换类属性等。插件可以按需引入和配置,以满足项目的特定需求。

    与预设不同,插件的使用需要明确列出每个要应用的插件,它们可以是官方提供的插件,也可以是第三方社区提供的插件。

    配置示例:

    javascript 复制代码
    {
      "plugins": [
        "@babel/plugin-transform-arrow-functions",
        "@babel/plugin-proposal-class-properties"
      ]
    }

总结:

  • Presets 是一组预定义的转换规则集合,用于实现对特定环境或特性的转换,旨在简化配置过程。
  • Plugins 是独立的转换规则,用于对代码进行具体的转换,需要明确列出每个要应用的插件。

通常,你可以使用预设来覆盖大部分常见的转换需求,但当你需要更精细地控制转换规则时,可以使用插件来实现定制化转换。

如何写一个presets和plugins

编写 Babel 的 presets 和 plugins 可以帮助你自定义和配置 Babel 的转换过程。下面我将为你介绍如何编写 Babel 的 presets 和 plugins。

编写 Presets

Presets 是一组预定义的 Babel plugins 的集合,可以方便地将多个 plugins 组合在一起以实现特定的转换配置。以下是编写 Babel Presets 的步骤:

  1. 创建一个新的 npm 包或进入已有的 npm 包目录。

  2. 在包目录中创建一个名为 babel-preset-yourpresetname 的文件夹,作为你的 preset 的名称。

  3. babel-preset-yourpresetname 目录中创建一个 index.js 文件,作为 preset 的入口文件。

  4. index.js 文件中,导出一个函数,该函数将返回一个包含 presets 配置的对象。

    javascript 复制代码
    module.exports = function (api) {
      api.cache(true);
    
      const presets = [
        // 在这里添加你的 presets
      ];
    
      const plugins = [
        // 在这里添加你的 plugins
      ];
    
      return {
        presets,
        plugins
      };
    };
    
    
    在上述代码中,你可以根据需要配置 presets 和 plugins 数组,用于定义你的 preset 所需的转换和功能。
  5. 在 package.json 文件中添加一个 babel 字段,指向你的 preset 入口文件。

    json 复制代码
    {
      "name": "your-package-name",
      "babel": {
        "presets": [
          "babel-preset-yourpresetname"
        ]
      }
    }
    
    这样,当用户使用你的 preset 时,Babel 将根据包的配置自动加载和应用你的 preset。
  6. 在包目录中运行 npm publish,将你的 preset 发布到 npm 上。

现在你已经成功编写了一个 Babel preset。其他开发者可以通过在他们的项目中安装你的 preset 并在 Babel 配置中使用它来享受你所提供的转换功能。

编写 Plugins

Plugins 是用于 Babel 的单个转换功能模块。下面是编写 Babel Plugin 的步骤:

  1. 创建一个新的 npm 包或进入已有的 npm 包目录。

  2. 在包目录中创建一个名为 babel-plugin-yourpluginname 的文件夹,作为你的 plugin 的名称。

  3. babel-plugin-yourpluginname 目录中创建一个 index.js 文件,作为 plugin 的入口文件。

  4. index.js 文件中,导出一个函数,该函数将接收一个 babel 对象和一个 options 对象作为参数,并返回一个对象,该对象包含一个 visitor 属性。

    javascript 复制代码
    module.exports = function (babel, options) {
      const { types: t } = babel;
    
      return {
        visitor: {
          // 在这里定义你的转换逻辑
        }
      };
    };
    
    在上述代码中,你可以使用 `visitor` 对象来定义你的转换逻辑。`visitor` 对象包含了一组用于处理不同类型的 AST 节点的方法。
  5. 在 package.json 文件中添加一个 babel 字段,指向你的 plugin 入口文件。

    json 复制代码
    {
      "name": "your-package-name",
      "babel": {
        "plugins": [
          "babel-plugin-yourpluginname"
        ]
      }
    }
    
    这样,当用户使用你的 plugin 时,Babel 将根据包的配置自动加载和应用你的 plugin。
  6. 在包目录中运行 npm publish,将你的 plugin 发布到 npm 上。

现在你已经成功编写了一个 Babel plugin。其他开发者可以通过在他们的项目中安装你的 plugin 并在 Babel 配置中使用它来享受你所提供的转换功能。

现如今如何polyfill

在 Babel 中,有几种常见的 polyfill 方案可供选择,每种方案都有其优点和缺点。下面是几种常见的方案及其特点:

  1. 全局引入方案

    • 方案:通过在代码的入口文件中全局引入整个 polyfill 库(如 core-js@babel/polyfill)。
    • 优点:
      • 简单易用,只需在一个地方引入即可。
      • 可以覆盖大部分兼容性需求,适用于大多数项目。
    • 缺点:
      • 引入整个 polyfill 库可能包含大量不必要的代码,增加文件大小。
      • 如果项目中只使用了部分特性,会造成不必要的代码冗余。
  2. 按需引入方案

    • 方案:使用 @babel/preset-env 配置中的 useBuiltIns: 'usage',根据代码中使用的特性自动引入所需的 polyfill。
    • 优点:
      • 根据实际使用的特性自动引入,减少了不必要的代码冗余。
      • 可以根据目标环境自动选择正确的 polyfill。
    • 缺点:
      • 需要额外的配置和构建过程,相对于全局引入方案稍微复杂一些。
  3. 按需手动引入方案

    • 方案:手动选择并引入需要的 polyfill,而不依赖于自动检测和引入。
    • 优点:
      • 可以精确控制引入的 polyfill,避免不必要的代码冗余。
      • 适用于需要对不同的特性进行定制化处理的场景。
    • 缺点:
      • 需要手动维护 polyfill 的引入列表,相对繁琐。
      • 可能需要额外的学习和了解各个特性的 polyfill。
  4. 按需加载方案

    • 方案:根据特性的兼容性动态加载对应的 polyfill,通常配合特性检测和条件加载使用。
    • 优点:
      • 可以根据实际需要延迟加载 polyfill,减少初始加载时间和文件大小。
      • 可以根据目标环境和用户设备动态加载所需的 polyfill。
    • 缺点:
      • 需要额外的编码和逻辑处理,相对复杂一些。
      • 可能需要在代码中进行特性检测和条件加载,增加开发复杂性。

下面给出每种方案的相关配置和示例代码:

  1. 全局引入方案

    配置示例(.babelrc):

    json 复制代码
    {
      "presets": [
        [
          "@babel/preset-env",
          {
            "useBuiltIns": "entry",
            "corejs": 3
          }
        ]
      ]
    }
    
    入口文件(例如 `index.js`):
    ````javascript
    import 'core-js'; // 或者 import '@babel/polyfill';
    // 其他代码
    ```
  2. 按需引入方案

    配置示例(.babelrc):

    json 复制代码
    {
      "presets": [
        [
          "@babel/preset-env",
          {
            "useBuiltIns": "usage",
            "corejs": 3
          }
        ]
      ]
    }
    
    入口文件(例如 `index.js`):
    ````javascript
    // 无需手动引入 polyfill
    // 其他代码
    ```
  3. 按需手动引入方案

    配置示例(.babelrc):

    json 复制代码
    {
      "presets": [
        [
          "@babel/preset-env",
          {
            "corejs": 3,
            "useBuiltIns": false
          }
        ]
      ]
    }
    ```
    
    入口文件(例如 `index.js`):
javascript 复制代码
import 'core-js/features/promise'; // 手动引入需要的 polyfill
import 'core-js/features/array/includes';
// 其他代码
  1. 按需加载方案

    配置示例(.babelrc):

    json 复制代码
    {
      "presets": [
        [
          "@babel/preset-env",
          {
            "useBuiltIns": "usage",
            "corejs": 3
          }
        ]
      ]
    }
    
    入口文件(例如 `index.js`):
    ````javascript
    async function loadPolyfills() {
      const needsPolyfill = [];
      
      if (!Object.entries) {
        needsPolyfill.push(import('core-js/features/object/entries'));
      }
      
      if (!Array.prototype.includes) {
        needsPolyfill.push(import('core-js/features/array/includes'));
      }
      
      // 其他需要的特性
      
      await Promise.all(needsPolyfill);
      
      // 所有 polyfills 加载完成后执行其他代码
      // 其他代码
    }
    
    loadPolyfills();
    ```

corejs@2和corejs@3怎么用

core-js 是一个广泛使用的 JavaScript 标准库,用于提供对 ECMAScript 新特性和API的兼容性支持。core-js 分为不同的版本,其中 core-js@2core-js@3 是两个主要版本。

使用 core-js@2core-js@3 的方法略有不同:

使用 core-js@2:

  1. 安装 core-js@2 作为项目的依赖项:

    perl 复制代码
    npm install core-js@2
    ```
  2. 在代码中使用 requireimport 导入需要的 polyfill 或模块。例如,导入 core-js 的全局命名空间对象,以及需要使用的特性或 API:

    javascript 复制代码
    // 导入 core-js 全局命名空间
    require('core-js');
    
    // 导入具体的 polyfill 或模块
    require('core-js/es6/array');
    require('core-js/es6/promise');
    // ...
    ```
  3. 在构建工具(例如 Babel)的配置文件中,指定使用 core-js@2 来转换代码。例如,在 Babel 配置的 plugins 中添加以下配置:

    javascript 复制代码
    {
      "plugins": [
        ["transform-runtime", {
          "polyfill": false,
          "regenerator": true
        }]
      ]
    }

使用 core-js@3:

  1. 安装 core-js@3 作为项目的依赖项:

    perl 复制代码
    npm install core-js@3
    ```
  2. 在代码中使用 import 导入需要的 polyfill 或模块。例如,导入 core-js 的全局命名空间对象,以及需要使用的特性或 API:

    javascript 复制代码
    // 导入 core-js 全局命名空间
    import 'core-js';
    
    // 导入具体的 polyfill 或模块
    import 'core-js/es/array';
    import 'core-js/es/promise';
    // ...
  3. 在构建工具(例如 Babel)的配置文件中,指定使用 core-js@3 来转换代码。例如,在 Babel 配置的 plugins 中添加以下配置:

    javascript 复制代码
    {
      "plugins": [
        ["@babel/plugin-transform-runtime", {
          "corejs": 3
        }]
      ]
    }
    ```

@babel/plugin-tranform-runtime和@babel/runtime区别

@babel/plugin-transform-runtime@babel/runtime 是 Babel 工具链中的两个相关模块,它们的作用和使用方式有所不同。

  1. @babel/plugin-transform-runtime

    ``@babel/plugin-transform-runtime` 是 Babel 的一个插件,用于在编译过程中转换代码,以实现对一些新语法和 API 的兼容。它主要的作用是将代码中使用的新语法或 API 转换为等效的旧语法或 API,以确保在目标环境中能够正常运行。

    这个插件在转换过程中会使用到一个运行时帮助函数库,它提供了一些低级的工具函数,用于替代编译后的代码中所使用的新语法或 API。为了使用该插件,你需要在 Babel 配置文件中添加相应的配置,并且安装运行时帮助函数库 @babel/runtime-corejs3

    使用 @babel/plugin-transform-runtime 有以下优点:

    • 避免在每个编译后的文件中重复引入运行时帮助函数,减少代码冗余。
    • 可以减小编译后的文件的体积。
  2. @babel/runtime

    ``@babel/runtime` 是一个运行时帮助函数库,它包含了一些辅助函数,用于支持通过 Babel 编译后的代码在目标环境中正常运行。这些辅助函数提供了对一些新语法和 API 的实现,以保证代码的兼容性。

    ``@babel/runtime` 库的安装和使用不需要配置 Babel 插件,它主要用于在编译后的代码中引入所需的辅助函数。通常,你需要将其作为开发依赖项安装,并通过模块导入来使用所需的辅助函数。

    使用 @babel/runtime 有以下优点:

    • 提供了一些新语法和 API 的实现,以确保在目标环境中的兼容性。
    • 可以避免在每个编译后的文件中重复引入辅助函数,减少代码冗余。

参考

babel polyfill

TC39

各个阶段的处理

怎么使用pnpm

相关推荐
fishmemory7sec7 分钟前
Electron 主进程与渲染进程、预加载preload.js
前端·javascript·electron
fishmemory7sec10 分钟前
Electron 使⽤ electron-builder 打包应用
前端·javascript·electron
豆豆1 小时前
为什么用PageAdmin CMS建设网站?
服务器·开发语言·前端·php·软件构建
JUNAI_Strive_ving1 小时前
番茄小说逆向爬取
javascript·python
看到请催我学习2 小时前
如何实现两个标签页之间的通信
javascript·css·typescript·node.js·html5
twins35202 小时前
解决Vue应用中遇到路由刷新后出现 404 错误
前端·javascript·vue.js
qiyi.sky2 小时前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js
煸橙干儿~~2 小时前
分析JS Crash(进程崩溃)
java·前端·javascript
哪 吒2 小时前
华为OD机试 - 几何平均值最大子数(Python/JS/C/C++ 2024 E卷 200分)
javascript·python·华为od
安冬的码畜日常2 小时前
【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法
前端·javascript·信息可视化·数据可视化·d3.js·d3比例尺·分段比例尺