【记一忘三二】babel

babel

babel是一个JavaScript编译器,用于代码转换:

  • 语法转换:将ES6ES7等新版本的JavaScript代码转换为ES5代码
  • 代码转换:将TypeScriptJSX等代码转换为JavaScript代码
  • 代码压缩:将JavaScript代码压缩为最小化的代码

核心库

@babel/core

@babel/corebabel的核心库,负责babel转换的整个流程,它具备以下关键功能:

  1. 解析源代码以生成 AST
  2. 遍历 AST 并对节点进行转换
  3. 将转换后的 AST 重新生成代码

transform@babel/core对外暴露的核心方法,用于将源代码转换为目标代码,并且可以接收一个配置对象作为参数,用于指定转换的插件和预设

@babel/clibabel-loader等工具库都是基于@babel/coretransform方法实现的

js 复制代码
const babel = require("@babel/core");
const code = `[1, 2, 3].map(x => x + 1)`;
const transformResult = babel.transform(code, {
  plugins: [require("@babel/plugin-transform-arrow-functions")],
});
console.log(transformResult.code); // [1, 2, 3].map(x => x + 1);;

@babel/core会自动读取项目根目录下创建.babelrcbabel.config.jsbabel.config.json文件等,用于指定转换的插件和预设

json 复制代码
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "ie": "11",
          "edge": "17"
        }
      }
    ]
  ],
  "plugins": ["@babel/plugin-transform-arrow-functions"]
}

@babel/cli

@babel/cli可以在命令行中直接执行babel的转换操作,它是babel的命令行工具,它可以在命令行中直接执行babel的转换操作

@babel/cli的核心转换还是借助@babel/core进行实现的,所有在使用@babel/cli之前,需要先安装@babel/core

bash 复制代码
# 安装 babel-cli 工具
pnpm install -D @babel/cli @babel/core

并且可以接受参数,指定输入和输出文件

bash 复制代码
# 将 src 目录下的代码转换为 dist 目录下的代码
npx babel src -D dist

@babel/cli还可以指定转换的插件和预设

bash 复制代码
# 指定转换的插件和预设
npx babel src -d dist --plugins=@babel/plugin-transform-arrow-functions --presets=@babel/preset-env

当然,因为@babel/cli是基于@babel/core,所以在项目根目录下创建了.babelrcbabel.config.jsbabel.config.json文件等,@babel/cli也会自动读取这些文件,用于指定转换的插件和预设

json 复制代码
{
  // 指定转换的预设
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "ie": "11",
          "edge": "17"
        }
      }
    ]
  ],
  // 指定转换的插件
  "plugins": ["@babel/plugin-transform-arrow-functions"]
}
bash 复制代码
# 会自动读取项目根目录下创建的 .babelrc、babel.config.js、babel.config.json 文件等,用于指定转换的插件和预设
npx babel src -d dist

@babel/plugin-transform-*

@babel/plugin-transform-*babel的插件,它可以对AST进行转换,从而实现对代码的转换

babel 提供了很多插件,比如@babel/plugin-transform-arrow-functions@babel/plugin-transform-block-scoping@babel/plugin-transform-class-properties等 通过一系列的插件组合,转换出我们想要的代码,每个插件都有自己的转换任务

@babel/plugin-transform-arrow-functions

@babel/plugin-transform-arrow-functionsbabel的插件,它可以将箭头函数转换为普通函数

项目根目录下创建babel.config.json文件

json 复制代码
{
  "plugins": ["@babel/plugin-transform-arrow-functions"]
}

src目录下创建index.js文件,并在index.js文件中编写需要转代码

js 复制代码
[1, 2, 3].map((x) => x + 1);

在命令行中执行转换操作,将src目录下的代码转换为dist目录下的代码,按照根目录babel.config.json文件中的配置进行转换

bash 复制代码
npx babel src -d dist

@babel/plugin-transform-runtime

@babel/plugin-transform-runtimebabel的插件,它可以将babel的运行时依赖抽离出来,避免全局污染

@babel/plugin-transform-runtime内部实现也是依赖core-js,但是使用的位置是来自于@babel/runtime-corejs3

js 复制代码
[1, 2, 3].includes(2);
Promise.resolve(1);
json 复制代码
{
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": 3, // 指定 core-js 的版本
        "helpers": true, // 启用辅助函数的重用
        "regenerator": true // 启用对生成器函数的支持
      }
    ]
  ]
}

生成的代码如下:

js 复制代码
var _context;
import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes";
import _Promise from "@babel/runtime-corejs3/core-js-stable/promise";
_includesInstanceProperty(_context = [1, 2, 3]).call(_context, 2);
_Promise.resolve(1);

@babel/polyfill

@babel/plugin-transform-*可以提供代码语法转换,但是不能提供一些新的 API 转换,比如PromiseSetMapArray.fromArray.ofObject.assign等,这些新的 API 是在ES6中引入的,但是在ES5中是没有的,所以需要使用@babel/polyfill来提供这些新的 API 补丁

babel7中,@babel/polyfill已经被弃用,推荐使用core-js来提供这些新的 API 补丁 core-js能够实现按需加载,只加载需要的 API 补丁,从而减少代码的体积

js 复制代码
require("core-js/modules/es.array.includes.js");
require("core-js/modules/es.object.to-string.js");
require("core-js/modules/es.promise.js");

@babel/preset-*

@babel/preset-*babel的预设,它可以根据浏览器环境自动选择需要转换的插件

@babel/preset-env

@babel/preset-envbabel的预设,它可以根据浏览器环境自动选择需要转换的插件和预设

json 复制代码
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "ie": "11",
          "edge": "17"
        }
      }
    ]
  ]
}

@babel/preset-env 还可以按需加载polyfill

json 复制代码
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "corejs": "3"
      }
    ]
  ]
}

useBuiltIns提供三个可选值:

  • false:不自动使用任何polyfill
  • entry:根据浏览器环境自动引入全部polyfill,但需要在入口文件中引入core-js
  • usage:根据代码中已经使用的API按需引入

转换流程

解析源代码以生成 AST

@babel/core集成了@babel/parser功能,用于解析代码生成AST

js 复制代码
const parser = require("@babel/parser");
const code = `[1, 2, 3].map(x => x + 1)`;

// 解析代码生成 AST
const ast = parser.parse(code);

// 打印 AST 抽象语法树
console.log(ast);

遍历 AST 并对节点进行转换

@babel/core集成了@babel/traverse功能,用于遍历AST,再通过@babel/plugin-*@babel/preset-*等插件对节点进行转换

@babel/traversetraverse方法会遍历AST的所有节点,每个节点会调用的需要转换规则,从而实现对代码的转换

js 复制代码
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const code = `[1, 2, 3].map(x => x + 1)`;

// 解析代码生成 AST
const ast = parser.parse(code);

// 遍历 AST 并对节点进行转换
traverse(ast, {
  ArrowFunctionExpression(path) {
    // 对箭头函数进行转换
    path.node.type = "FunctionExpression";
  },
});

// 打印转换后的 AST 抽象语法树
console.log(ast);

转换后的 AST 重新生成代码

@babel/core集成了@babel/generator功能,用于将转换后的AST重新生成代码

js 复制代码
const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const generator = require("@babel/generator").default;
const code = `[1, 2, 3].map(x => x + 1)`;

// 解析代码生成 AST
const ast = parser.parse(code);

// 遍历 AST 并对节点进行转换
traverse(ast, {
  ArrowFunctionExpression(path) {
    // 对箭头函数进行转换
    path.node.type = "FunctionExpression";
  },
});

// 转换后的 AST 重新生成代码
const { code: generatedCode } = generator(ast);

// 打印生成的代码
console.log(generatedCode);

其他问题

@babel/polyfillcore-js的区别

  • @babel/polyfill是将所有的polyfill都引入到全局作用域中,从而实现全局的polyfill功能,但是这在babel7中已经被弃用,推荐使用core-js来实现
  • core-js能够实现按需加载,只加载需要的polyfill,从而减少代码的体积

@babel/plugin-transform-runtime@babel/preset-env的区别

都是解决如何按需加载polyfill的问题,但是倾向的方向不一样

  • @babel/plugin-transform-runtime用于在转换过程中重用辅助函数,避免全局污染,并按需引入 polyfills
  • @babel/preset-env用于根据目标环境自动选择需要的 polyfills,并且可以配置按需加载的 polyfills,但都是全局引入

当同时使用@babel/plugin-transform-runtime@babel/preset-env时,@babel/plugin-transform-runtime会替代@babel/preset-env中的polyfills

js 复制代码
[1,2,3].includes(2)
Promise.resolve(1)
json 复制代码
{
    "presets": [
        [
            "@babel/preset-env",
            {
                "targets": {
                    "ie": "11"
                },
                "modules": false,
                "useBuiltIns": "usage", // 或 "usage",根据需要选择
                "corejs": 3 // 指定 core-js 的版本
            }
        ]
    ],
    "plugins": [
        [
            "@babel/plugin-transform-runtime",
            {
                "corejs": 3, // 指定 core-js 的版本
                "helpers": true, // 启用辅助函数的重用
                "regenerator": true // 启用对生成器函数的支持
            }
        ]
    ]
}

如果使用@babel/plugin-transform-runtime配合@babel/preset-env,打包之后代码:

js 复制代码
var _context;
import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes";
import _Promise from "@babel/runtime-corejs3/core-js-stable/promise";
import "core-js";
_includesInstanceProperty(_context = [1, 2, 3]).call(_context, 2);
_Promise.resolve(1);
相关推荐
幼儿园技术家4 分钟前
微信小程序/H5 调起确认收款界面
前端
微笑边缘的金元宝9 分钟前
Echarts柱状图斜线环纹(图形的贴花图案)
前端·javascript·echarts
wuxiguala14 分钟前
【web考试系统的设计】
前端
独立开阀者_FwtCoder1 小时前
CSS view():JavaScript 滚动动画的终结
前端·javascript·vue.js
咖啡教室1 小时前
用markdown语法制作一个好看的网址导航页面(markdown-web-nav)
前端·javascript·markdown
独立开阀者_FwtCoder1 小时前
Vue 团队“王炸”新作!又一打包工具发布!
前端·javascript·vue.js
天天扭码1 小时前
一分钟解决“3.无重复字符的最长字串问题”(最优解)
前端·javascript·算法
独立开阀者_FwtCoder1 小时前
Promise 引入全新 API!效率提升 300%!
前端·javascript·后端
陈明勇1 小时前
三句话搞定周末出行攻略!我用 AI 生成一日游可视化页面,还能秒上线!
前端·人工智能·mcp
_一条咸鱼_2 小时前
Vue 样式深入剖析:从基础到源码级理解(十)
前端·javascript·面试