JSX:看似 HTML 的 “卧底”?

面试时被问到 JSX,很多人会说:"就是在 JavaScript 里写 HTML 嘛!" 但如果面试官追问:"JSX 能直接在浏览器里运行吗?""它和 HTML 到底有啥区别?" 不少人就卡壳了。其实 JSX 这东西,表面上像 HTML,骨子里却是个 "披着 HTML 外衣的 JavaScript 卧底"。

一、JSX 是啥?------ 不是 HTML,是 "语法糖"

先看个例子,这是一段典型的 JSX 代码:

jsx 复制代码
const element = <h1 className="title">Hello, JSX!</h1>;

看起来是不是和 HTML 一模一样?但它有个隐藏身份:JavaScript 的扩展语法(可以理解为 "语法糖")。它的全称是 "JavaScript XML",意思是 "在 JavaScript 里写 XML(HTML 是 XML 的一种)"。

React 之所以搞出 JSX,是为了解决一个痛点:让 UI 结构和 JavaScript 逻辑能更自然地结合。在没有 JSX 的年代,我们得用纯 JavaScript 写 UI,比如这样:

javascript 复制代码
// 没有JSX的日子,用纯JS创建UI(像写配置文件)
const element = React.createElement(
  'h1',
  { className: 'title' },
  'Hello, 纯JS!'
);

对比一下,JSX 的写法是不是直观多了?就像直接在代码里画 UI 一样,这也是 React 的一大特色。

二、JSX 能直接运行吗?------ 不能!得靠 "翻译官"

浏览器只能理解纯 JavaScript 代码,根本不认识 JSX。那我们写的 JSX 是怎么跑起来的?

答案是:需要被编译成纯 JavaScript 代码 。这个 "翻译官" 通常是 Babel(React 项目默认集成),它会把 JSX 转换成 React 提供的一个函数调用 ------React.createElement

比如前面的 JSX 代码:

jsx 复制代码
const element = <h1 className="title">Hello, JSX!</h1>;

会被 Babel 翻译成:

javascript 复制代码
const element = React.createElement(
  'h1', // 标签名(或组件)
  { className: 'title' }, // 标签属性(注意是className,不是class)
  'Hello, JSX!' // 子元素(文本或其他节点)
);

这就是 JSX 的底层原理:它只是React.createElement函数的语法糖,最终都会变成纯 JavaScript 代码。

三、React.createElement是啥?------ 虚拟 DOM 的 "设计师"

React.createElement函数的作用,是创建一个描述 DOM 结构的 JavaScript 对象 (也就是 "虚拟 DOM")。我们可以打印一下上面的element

javascript 复制代码
console.log(element);
// 输出结果(简化版):
{
  type: 'h1',
  props: {
    className: 'title',
    children: 'Hello, JSX!'
  },
  // ...其他属性(如key、ref等)
}

这个对象记录了:

  • type:标签类型(如'h1''div',或 React 组件);
  • props:标签的属性(如classNameid)和子元素(children);

React 会根据这个 "虚拟 DOM 对象",去创建或更新真实的 DOM 节点。这也是 React 性能高效的原因之一 ------ 通过对比新旧虚拟 DOM 的差异(Diff 算法),只更新变化的部分,避免全量重绘。

四、实战对比:JSX vs React.createElement

看一个更复杂的例子,用 JSX 写一个列表:

jsx 复制代码
// JSX写法
<ul>
  {todos.map(todo => (
    <li key={todo.id}>{todo.title}</li>
  ))}
</ul>

如果用React.createElement写,是这样的:

javascript 复制代码
// 等价的createElement写法
React.createElement(
  'ul',
  null,
  todos.map(todo => 
    React.createElement(
      'li',
      { key: todo.id },
      todo.title
    )
  )
);

是不是瞬间觉得 JSX 香多了?尤其是复杂 UI,JSX 能让代码可读性提升一个档次。这也是为什么 React 官方推荐用 JSX------ 它让开发者把精力放在 "写什么",而不是 "怎么实现"。

五、JSX 语法规则

JSX 虽然长得像 HTML,但有一些语法差异,这些差异是面试高频考点:

(1)className 代替 class

在 HTML 里给元素加类名用class,但 JSX 里必须用className------ 因为class是 JavaScript 的关键字(用于定义类),为了避免冲突,JSX 改用className

jsx 复制代码
// 正确:用className
<div className="container">内容</div>

// 错误:不能用class
<div class="container">内容</div> // 会被忽略或警告

(2)htmlFor 代替 for

同理,HTML 里的for属性(用于关联 label 和 input)在 JSX 里要用htmlFor,因为for也是 JavaScript 关键字。

jsx 复制代码
// 正确:用htmlFor
<label htmlFor="username">用户名:</label>
<input id="username" />

// 错误:不能用for
<label for="username">用户名:</label>

(3)必须有唯一根元素

JSX 要求 "最外层必须有一个唯一的父元素",否则会报错。如果不想多一个多余的 div,可以用 Fragment(<></>)当根元素(前面讲过)。

jsx 复制代码
// 正确:用Fragment当根元素
<>
  <h1>标题</h1>
  <p>内容</p>
</>

// 错误:没有唯一根元素
<h1>标题</h1>
<p>内容</p> // 报错:Adjacent JSX elements must be wrapped in an enclosing tag

(4)嵌入 JavaScript 表达式用{}

JSX 里可以用{}嵌入任何 JavaScript 表达式(变量、函数调用、三元运算等),但不能嵌入语句(如iffor)。

jsx 复制代码
const name = 'React';
const isLoggedIn = true;

// 正确:嵌入变量、三元表达式
<div>
  <p>Hello, {name}</p>
  {isLoggedIn ? <p>已登录</p> : <p>未登录</p>}
</div>

// 错误:不能嵌入语句
<div>
  {if (isLoggedIn) { <p>已登录</p> }} // 报错:Unexpected token
</div>

(5)事件绑定用驼峰命名

HTML 里事件名是全小写(如onclick),JSX 里要用驼峰命名(如onClick)。

jsx 复制代码
// 正确:驼峰命名onClick
<button onClick={() => console.log('点击了')}>点击我</button>

// 错误:全小写onclick
<button onclick={() => console.log('点击了')}>点击我</button> // 不生效

六、总结:JSX 的核心考点与底层原理

  1. 定义:JSX 是 JavaScript 扩展语法,允许在 JS 中嵌入 HTML 结构,提升 UI 代码的可读性。
  2. 本质 :语法糖,编译后会变成React.createElement调用,生成虚拟 DOM 对象。
  3. 关键语法classNamehtmlFor、驼峰事件名、{}嵌入表达式、唯一根元素。
  4. 渲染流程 :JSX → 编译为createElement → 生成虚拟 DOM → ReactDOM.render → 真实 DOM。
相关推荐
拉不动的猪3 分钟前
Vue 3 中 async setup () 的「坑」与避坑指南1
前端·javascript·面试
拉不动的猪5 分钟前
Vue 3 中 async setup () 的「坑」与避坑指南2
前端·vue.js·后端
lvchaoq8 分钟前
Vite的优缺点(精简版)
前端
_花卷8 分钟前
🌟ELPIS-如何基于vue3完成领域模型架构
前端·vue.js·架构
讨厌吃蛋黄酥17 分钟前
`useState`是同步还是异步?深入解析闭包陷阱与解决方案
前端·react.js
_一条咸鱼_21 分钟前
Android Runtime调试检测与反制手段(86)
android·面试·android jetpack
五号厂房21 分钟前
一道关于事件循环(Event Loop) 机制和任务队列模型的面试题
前端·面试
兵临天下api22 分钟前
【干货满满】解密 API 数据解析:从 JSON 到数据库存储的完整流程
前端
支撑前端荣耀22 分钟前
十一、用Cypress做接口测试——不止于UI的全能选手
前端
Java水解23 分钟前
Unity3D WebGL内存优化与缓存管理
前端