Skip to content

为什么说 JSX 不仅仅是模板语法?

目录


在初次接触 React 时,我们常常会对 JSX 产生一个直观的印象:它看起来就像是直接在 JavaScript 代码里写 HTML,似乎是一种方便的"模板语法"。这种看法很自然,但它并未完全揭示 JSX 的本质。

实际上,将 JSX 简单地归类为模板语法,会忽略其背后更深层、更强大的机制。更准确地说,JSX 是 JavaScript 的一种语法扩展(Syntax Extension)。理解这一点,是深入掌握 React 工作方式的关键。

接下来,我们将从几个方面探讨,为什么 JSX 的身份远不止模板那么简单。


1. JSX 并非直接运行,它需要被编译

最核心的一点是:浏览器本身并不认识 JSX。如果我们把包含 JSX 的代码直接扔到浏览器控制台,只会得到一个语法错误。

JSX 能够生效,得益于一个"编译"或更准确地说是"转译"(Transpilation)的过程。这个过程通常由 Babel 这样的工具完成。在转译过程中,JSX 语法会被转换成标准的、浏览器可以理解的 JavaScript 代码。

让我们来看一个具体的例子。下面这段我们习以为常的 JSX 代码:

jsx
const element = <h1 className='greeting'>Hello, world!</h1>

经过 Babel 转译后,会变成下面这个样子:

javascript
const element = React.createElement(
  'h1',
  { className: 'greeting' },
  'Hello, world!'
)

这个转换过程清晰地揭示了 JSX 的本质:它本质上是 React.createElement() 函数调用的一种"语法糖"。它让我们能用一种更接近最终 UI 结构的声明式语法,来替代繁琐的函数调用。


2. JSX 的产物是 JavaScript 对象

既然 JSX 会被转换为 React.createElement(),那么这个函数执行后会返回什么呢?

答案是:一个普通的 JavaScript 对象

这个对象通常被称为"React 元素(React Element)",它像一张蓝图,描述了我们希望在屏幕上看到的 UI 结构。以上面的 element 为例,React.createElement() 执行后得到的对象,大致会是这样:

javascript
{
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello, world!'
  },
  // ... 其他内部属性
}

这个对象并不直接操作 DOM。它只是一个轻量的数据结构,用来告诉 React:"嘿,我想要渲染一个 h1 标签,它的 class 是 greeting,里面的内容是 Hello, world!。" React 会收集这些对象,构建出我们常说的虚拟 DOM(Virtual DOM),再高效地计算出如何更新真实的浏览器 DOM。

所以,当我们编写 JSX 时,我们实际上是在用一种便捷的方式创建和组合 JavaScript 对象。


3. JSX 内部可以无缝嵌入 JavaScript 的全部能力

这或许是 JSX 作为"JavaScript 扩展"最有力的证明。在传统的模板引擎(如 Handlebars 或 EJS)中,我们通常只能使用其提供的特定语法来进行逻辑判断(如 if)或循环(如 each)。这些语法是受限的,它们自成体系,与 JavaScript 本身是割裂的。

而 JSX 完全不同。通过 {} 括号,我们可以在 JSX 中嵌入任何有效的 JavaScript 表达式。这意味着我们可以直接利用 JavaScript 语言的全部能力来构建动态 UI。

变量与表达式

我们可以直接引用变量,或进行简单的运算。

jsx
const user = { firstName: 'Harper', lastName: 'Perez' }
const element = (
  <h1>
    Hello, {user.firstName} {user.lastName}!
  </h1>
)

函数调用

在渲染过程中执行函数,并将返回值作为内容。

jsx
function formatName(user) {
  return user.firstName + ' ' + user.lastName
}

const element = <h1>Hello, {formatName(user)}!</h1>

条件渲染

利用三元运算符或逻辑与(&&)操作符,可以轻松实现条件渲染。

jsx
// 使用三元运算符
<div>
  {isLoggedIn ? <LogoutButton /> : <LoginButton />}
</div>

// 使用 && 操作符
<div>
  <h1>Hello!</h1>
  {unreadMessages.length > 0 &&
    <h2>
      You have {unreadMessages.length} unread messages.
    </h2>
  }
</div>

列表渲染

Array.prototype.map() 是 JavaScript 的原生方法,在 JSX 中用于列表渲染是极其自然和常见的模式。map 方法返回一个由 JSX 元素构成的新数组,React 会将其渲染为列表。

jsx
const numbers = [1, 2, 3, 4, 5]
const listItems = numbers.map(number => (
  <li key={number.toString()}>{number}</li>
))

// 最终渲染
;<ul>{listItems}</ul>

这些例子都表明,我们不是在学习一种新的、独立的模板语言。我们只是在用一种新的语法形式,来组织和运用我们已经掌握的 JavaScript 知识。


结论

综上所述,JSX 的精妙之处在于它并非一种与 JavaScript 分离的模板技术。

  • 它是一种语法扩展,为 React.createElement() 提供了更具可读性和表现力的声明式写法。
  • 它的最终产物是纯粹的 JavaScript 对象,这构成了 React 虚拟 DOM 的基础。
  • 它允许我们无缝地利用 JavaScript 的全部功能,包括变量、函数、条件逻辑和循环,从而构建出复杂而动态的用户界面。

当我们说 JSX 是 JavaScript 的扩展时,我们强调的是它与 JavaScript 语言的深度融合。这种融合打破了传统"逻辑层"与"视图层"之间的壁垒,为我们带来了一种更内聚、更强大的组件化开发体验。

基于 VitePress 的本地知识库