面试时被问到 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
:标签的属性(如className
、id
)和子元素(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 表达式(变量、函数调用、三元运算等),但不能嵌入语句(如if
、for
)。
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 的核心考点与底层原理
- 定义:JSX 是 JavaScript 扩展语法,允许在 JS 中嵌入 HTML 结构,提升 UI 代码的可读性。
- 本质 :语法糖,编译后会变成
React.createElement
调用,生成虚拟 DOM 对象。 - 关键语法 :
className
、htmlFor
、驼峰事件名、{}
嵌入表达式、唯一根元素。 - 渲染流程 :JSX → 编译为
createElement
→ 生成虚拟 DOM → ReactDOM.render → 真实 DOM。