JSX 与编译过程

JSX 不是 HTML,而是 JavaScript 的语法扩展

1. JSX 是什么?

JSX 允许在 JavaScript 中使用类似 HTML 的写法描述 UI:

const element = <button className="primary">保存</button>;

它不是浏览器原生语法,浏览器不能直接执行 JSX。构建工具会先将 JSX 编译成 JavaScript。

JSX 中的 {} 只能放 JavaScript 表达式,不能直接放 iffor 等语句:

const title = <h1>{user.name}</h1>;

2. JSX 会编译成什么?

经典转换大致相当于:

const element = <button className="primary">保存</button>;
const element = React.createElement(
  "button",
  { className: "primary" },
  "保存",
);

现代 JSX Transform 通常会编译为 jsxjsxs 等运行时函数调用:

import { jsx as _jsx } from "react/jsx-runtime";

const element = _jsx("button", {
  className: "primary",
  children: "保存",
});

因此 JSX 的产物不是 DOM,而是描述 UI 的 React element 对象。React 后续会在 Render 阶段协调这些 element,并在 Commit 阶段更新 DOM。

3. 标签和属性如何转换?

小写标签与大写组件

<div />        // 宿主元素,通常转换为 "div"
<UserCard />   // 组件,转换为 UserCard 变量

组件名必须以大写开头,否则会被当作原生标签名称处理。

属性

<UserCard name={name} disabled={isDisabled} />

会转换成传给组件的 props 对象。JSX 属性使用驼峰命名,例如 classNamehtmlForonClick

子节点

<Panel>
  <h2>标题</h2>
  文本
</Panel>

子节点会作为 children 传入。多个子节点可能由 jsxs 或数组表示。

Fragment

return (
  <>
    <Header />
    <Content />
  </>
);

Fragment 允许返回多个兄弟节点而不额外创建 DOM 包裹元素。

4. JSX 与模板有什么区别?

JSX 是 JavaScript 表达式,可以直接使用变量、函数和条件表达式:

{visible ? <Panel /> : null}
{items.map((item) => <Item key={item.id} item={item} />)}

Vue 模板则通常先被编译为 render function,再由运行时执行。两者最终都可能生成 VNode/element,但表达方式和编译优化策略不同。

5. JSX 的常见面试点

为什么 JSX 必须有一个根节点?

因为一次 JSX 表达式需要对应一个返回值。可以使用 Fragment 返回多个兄弟节点。

JSX 中为什么不能写if

if 是语句,不是表达式。应使用提前计算、三元表达式或逻辑与:

{isLoggedIn ? <Dashboard /> : <Login />}
{hasError && <ErrorMessage />}

key 会传给组件吗?

不会。key 是 React 协调节点身份的特殊字段,不会出现在 props 中;组件需要 id 时应显式传递:

<Item key={item.id} id={item.id} />

JSX 是否等于 Virtual DOM?

不完全等于。JSX 是源码语法,编译后产生 React element;Fiber、协调和 DOM 提交是后续运行时过程。

6. 总结

JSX
→ JSX Transform
→ React element
→ Render / Reconciliation
→ Commit
→ DOM

面试时可以这样回答:JSX 是 JavaScript 的语法扩展,不能被浏览器直接执行。Babel 或 TypeScript 会把它转换成 React.createElement 或现代 JSX runtime 的函数调用,产物是描述 UI 的 React element,而不是直接生成 DOM。