JSX 跑不起来?都是 Babel 在背后偷偷帮你 “翻译” 呢

252 阅读5分钟

写 React 组件时,你肯定写过这样的代码:<div><h1>Hello JSX</h1></div>—— 这行 JSX 看起来像 HTML,却能和 JavaScript 一起运行。但你有没有想过:浏览器根本不认识 JSX,它是怎么被 “看懂” 的?为什么直接在 HTML 里写 JSX 会报错?

答案藏在一个叫 “Babel” 的工具里。它就像一个 “翻译官”,能把浏览器不认识的 “新语法”(包括 JSX、ES6 + 语法)转换成它能看懂的 “旧语法”。今天就来扒一扒:JSX 为什么不能独立运行?Babel 是怎么把它 “翻译” 成正经 JavaScript 的?

一、JSX 的 “困境”:浏览器根本不认识它

先看一个简单的 JSX 代码:

const element = <h1>Hello, Babel!</h1>;

这段代码看起来直观又舒服,但有个致命问题:浏览器完全不认识它。浏览器只能理解符合 ECMAScript 标准的 JavaScript 代码,而 JSX 是 React 发明的 “扩展语法”,相当于给 JavaScript 加了 “方言”,浏览器的 “语言系统” 里根本没有这个词。

再比如 ES6 的let和箭头函数:

let a = 1;
const add = (x, y) => x + y;

在 IE8 这种 “老古董” 浏览器里,let=>会被当成 “病句” 直接报错。

二、Babel:让 JavaScript “穿越” 到任何浏览器的 “翻译官”

Babel 的 slogan 是 “Make JavaScript great again”,它的核心功能就一个:把新的 JavaScript 语法(包括 JSX)转换成旧的、浏览器能识别的语法

简单说,它就像一个 “语法转换器”:

  • let a = 1转换成var a = 1(让 IE 也能看懂);
  • () => {}转换成function () {}
  • <h1>Hello</h1>转换成React.createElement('h1', null, 'Hello')(让浏览器看懂 JSX)。

(1)Babel 处理 ES6 + 语法:把 “新写法” 转成 “旧写法”

比如这段用了let和箭头函数的代码:

// 新语法:let和箭头函数
let greet = (name) => `Hello ${name}`;

经过 Babel 转换后,会变成旧浏览器能识别的varfunction

// 转换后的旧语法
"use strict";
var greet = function greet(name) {
  return "Hello " + name;
};

(2)Babel 处理 JSX:把 “类 HTML” 转成 React 能识别的函数调用

JSX 是 React 的 “专属语法”,Babel 会把它转换成 React 提供的createElement函数调用。比如:

// JSX语法
const App = () => <h1 className="title">Hello JSX</h1>;

转换后:

// 转换后的React.createElement调用
"use strict";
const App = () => React.createElement(
  "h1", // 标签名
  { className: "title" }, // 属性(注意className代替class)
  "Hello JSX" // 子元素
);

这就是 JSX 能在 React 中运行的核心原因:Babel 把它翻译成了 React 能处理的函数调用,而React.createElement会进一步生成描述 UI 的虚拟 DOM 对象。

三、Babel 编译流程:从安装到转换,一步一步来

想让 Babel 帮你 “翻译” 代码,需要一套完整的工具链。下面用最基础的方式(不依赖 Vite、Webpack 等工程化工具),带你走一遍编译流程。

(1)第一步:安装 Babel 核心工具

Babel 的工作需要两个核心包:

  • @babel/core:Babel 的核心引擎,负责语法转换的核心逻辑;
  • @babel/cli:Babel 的命令行工具,让我们能通过命令行调用 Babel 进行转换。

安装命令(需要 Node 环境):

# 局部安装(推荐,作为项目依赖)
pnpm i @babel/core @babel/cli -D
# 或npm
npm i @babel/core @babel/cli --save-dev

-D表示 “开发环境依赖”—— 因为编译是开发阶段的工作,上线后不需要 Babel,所以只在开发时安装。

image.png

(2)第二步:告诉 Babel “怎么翻译”—— 配置预设(Presets)

Babel 本身只提供了语法转换的 “框架”,具体怎么转换(比如怎么处理 ES6,怎么处理 JSX),需要通过 “预设(Presets)” 来指定。预设就是 “一组转换规则的集合”。

常用的两个预设:

  • @babel/preset-env:处理 ES6 + 新语法(如letconst、箭头函数、Promise等),自动转换成目标浏览器支持的旧语法;
  • @babel/preset-react:专门处理 JSX 语法,把它转换成React.createElement调用。

安装这两个预设:

pnpm i @babel/preset-env @babel/preset-react -D

(3)第三步:创建配置文件 ——.babelrc

在项目根目录创建.babelrc文件,告诉 Babel 要使用哪些预设:

{
  "presets": [
    "@babel/preset-env", // 处理ES新语法
    "@babel/preset-react" // 处理JSX
  ]
}

Babel 会自动读取这个配置文件,按照预设的规则进行转换。

(4)第四步:执行编译,看 Babel 怎么 “翻译” 代码

假设我们有一个1.jsx文件,内容是 JSX 和 ES6 语法:

const App = () => {
  let message = "Hello Babel";
  return <h1>{message}</h1>;
};

通过@babel/cli提供的babel命令编译这个文件:

npx babel 1.jsx -o 2.jsx

或者依赖Babel 核心库和预设(Preset)规则:

./node_modules/.bin/babel 1.jsx -o 2.jsx

编译后的dist/app.js内容: image.png

  • let被转换成了var(兼容旧浏览器);
  • JSX 被转换成了React.createElement调用(浏览器现在能看懂了)。

四、为什么 Vite/Create React App 里不用手动配置 Babel?

在实际开发中,我们用 Vite 或 Create React App 创建的 React 项目,从来不用手动敲babel命令 —— 因为这些工程化工具已经 “内置” 了 Babel,并帮我们做好了配置。

比如 Vite 会在开发时自动监听 JSX 文件的变化,通过内置的 Babel 插件实时把 JSX 转换成React.createElement,再交给浏览器运行。我们写的 JSX 能 “直接运行”,其实是 Vite 在背后自动完成了编译步骤。

五、总结:Babel 为什么是前端工程化的 “刚需”?

  1. JSX 能运行,全靠 Babel 翻译:浏览器不认识 JSX,Babel 把它转换成React.createElement调用,让 React 能处理。
  2. 新语法兼容旧浏览器:ES6 + 的let、箭头函数等新语法,经 Babel 转换后能在 IE 等旧浏览器中运行。
  3. 工程化工具的底层依赖:Vite、Webpack 等工具通过集成 Babel,让我们能 “写新语法,跑在任何环境”,不用关心编译细节。