使用 JSX 书写标签语言 | Writing Markup with JSX

JSX is a syntax extension for JavaScript that lets you write HTML-like markup inside a JavaScript file. Although there are other ways to write components, most React developers prefer the conciseness of JSX, and most codebases use it. JSX 是 JavaScript 语法扩展,可以让你在 JavaScript 文件中书写类似 HTML 的标签。虽然还有其它方式可以编写组件,但大部分 React 开发者更喜欢 JSX 的简洁性,并且在大部分代码库中使用它。

你将会学习到

  • Why React mixes markup with rendering logic
  • 为什么 React 将标签和渲染逻辑耦合在一起
  • How JSX is different from HTML
  • JSX 与 HTML 有什么区别
  • How to display information with JSX
  • 如何通过 JSX 展示信息

JSX: 将标签引入 JavaScript | JSX: Putting markup into JavaScript

The Web has been built on HTML, CSS, and JavaScript. For many years, web developers kept content in HTML, design in CSS, and logic in JavaScript—often in separate files! Content was marked up inside HTML while the page’s logic lived separately in JavaScript: 网页是构建在 HTML、CSS 和 JavaScript 之上的。多年以来,web 开发者都是将网页内容存放在 HTML 中,样式放在 CSS 中,而逻辑则放在 JavaScript 中 —— 通常是在不同的文件中!页面的内容通过标签语言描述并存放在 HTML 文件中,而逻辑则单独存放在 JavaScript 文件中。

HTML markup with purple background and a div with two child tags: p and form.
HTML markup with purple background and a div with two child tags: p and form.

HTML

Three JavaScript handlers with yellow background: onSubmit, onLogin, and onClick.
Three JavaScript handlers with yellow background: onSubmit, onLogin, and onClick.

JavaScript

But as the Web became more interactive, logic increasingly determined content. JavaScript was in charge of the HTML! This is why in React, rendering logic and markup live together in the same place—components. 但随着 Web 的交互性越来越强,逻辑越来越决定页面中的内容。JavaScript 控制着 HTML 的内容!这也是为什么 在 React 中,渲染逻辑和标签共同存在于同一个地方——组件。

React component with HTML and JavaScript from previous examples mixed. Function name is Sidebar which calls the function isLoggedIn, highlighted in yellow. Nested inside the function highlighted in purple is the p tag from before, and a Form tag referencing the component shown in the next diagram.
React component with HTML and JavaScript from previous examples mixed. Function name is Sidebar which calls the function isLoggedIn, highlighted in yellow. Nested inside the function highlighted in purple is the p tag from before, and a Form tag referencing the component shown in the next diagram.

Sidebar.js React component Sidebar.js React 组件

React component with HTML and JavaScript from previous examples mixed. Function name is Form containing two handlers onClick and onSubmit highlighted in yellow. Following the handlers is HTML highlighted in purple. The HTML contains a form element with a nested input element, each with an onClick prop.
React component with HTML and JavaScript from previous examples mixed. Function name is Form containing two handlers onClick and onSubmit highlighted in yellow. Following the handlers is HTML highlighted in purple. The HTML contains a form element with a nested input element, each with an onClick prop.

Form.js React component Form.js React 组件

Keeping a button’s rendering logic and markup together ensures that they stay in sync with each other on every edit. Conversely, details that are unrelated, such as the button’s markup and a sidebar’s markup, are isolated from each other, making it safer to change either of them on their own. 将一个按钮的渲染逻辑和标签放在一起可以确保它们在每次编辑时都能保持互相同步。反之,彼此无关的细节是互相隔离的,例如按钮的标签和侧边栏的标签。这样我们在修改其中任意一个组件时会更安全。

Each React component is a JavaScript function that may contain some markup that React renders into the browser. React components use a syntax extension called JSX to represent that markup. JSX looks a lot like HTML, but it is a bit stricter and can display dynamic information. The best way to understand this is to convert some HTML markup to JSX markup. 每个 React 组件都是一个 JavaScript 函数,它会返回一些标签,React 会将这些标签渲染到浏览器上。React 组件使用一种被称为 JSX 的语法扩展来描述这些标签。JSX 看起来和 HTML 很像,但它的语法更加严格并且可以动态展示信息。了解这些区别最好的方式就是将一些 HTML 标签转化为 JSX 标签。

注意

JSX and React are two separate things. They’re often used together, but you can use them independently of each other. JSX is a syntax extension, while React is a JavaScript library. JSX and React 是相互独立的 东西。但它们经常一起使用,但你 可以 单独使用它们中的任意一个,JSX 是一种语法扩展,而 React 则是一个 JavaScript 的库。

将 HTML 转化为 JSX | Converting HTML to JSX

Suppose that you have some (perfectly valid) HTML: 假设你现在有一些(完全有效的)HTML 标签:

<h1>海蒂·拉玛的待办事项</h1>
<img
src="https://i.imgur.com/yXOvdOSs.jpg"
alt="Hedy Lamarr"
class="photo"
>
<ul>
<li>发明一种新式交通信号灯
<li>排练一个电影场景
<li>改进频谱技术
</ul>

And you want to put it into your component: 而现在想要把这些标签迁移到组件中:

export default function TodoList() {
return (
// ???
)
}

If you copy and paste it as is, it will not work: 如果直接复制到组件中,并不能正常工作:

export default function TodoList() {
  return (
    // 这不起作用!
    <h1>海蒂·拉玛的待办事项</h1>
    <img 
      src="https://i.imgur.com/yXOvdOSs.jpg" 
      alt="Hedy Lamarr" 
      class="photo"
    >
    <ul>
      <li>发明一种新式交通信号灯
      <li>排练一个电影场景
      <li>改进频谱技术
    </ul>

This is because JSX is stricter and has a few more rules than HTML! If you read the error messages above, they’ll guide you to fix the markup, or you can follow the guide below. 这是因为 JSX 语法更加严格并且相比 HTML 有更多的规则!上面的错误提示可以帮助你修复标签中的错误,当然也可以参考下面的指引。

注意

Most of the time, React’s on-screen error messages will help you find where the problem is. Give them a read if you get stuck! 大部分情况下,React 在屏幕上显示的错误提示就能帮你找到问题所在,如果在编写过程中遇到问题就参考一下提示吧。

JSX 规则 | The Rules of JSX

1. 只能返回一个根元素 | 1. Return a single root element

To return multiple elements from a component, wrap them with a single parent tag. 如果想要在一个组件中包含多个元素,需要用一个父标签把它们包裹起来

For example, you can use a <div>: 例如,你可以使用一个 <div> 标签:

<div>
<h1>海蒂·拉玛的待办事项</h1>
<img
src="https://i.imgur.com/yXOvdOSs.jpg"
alt="Hedy Lamarr"
class="photo"
>
<ul>
...
</ul>
</div>

If you don’t want to add an extra <div> to your markup, you can write <> and </> instead: 如果你不想在标签中增加一个额外的 <div>,可以用 <></> 元素来代替:

<>
<h1>海蒂·拉玛的待办事项</h1>
<img
src="https://i.imgur.com/yXOvdOSs.jpg"
alt="Hedy Lamarr"
class="photo"
>
<ul>
...
</ul>
</>

This empty tag is called a Fragment. Fragments let you group things without leaving any trace in the browser HTML tree. 这个空标签被称作 Fragment。React Fragment 允许你将子元素分组,而不会在 HTML 结构中添加额外节点。

深入探讨

为什么多个 JSX 标签需要被一个父元素包裹?| Why do multiple JSX tags need to be wrapped?

JSX looks like HTML, but under the hood it is transformed into plain JavaScript objects. You can’t return two objects from a function without wrapping them into an array. This explains why you also can’t return two JSX tags without wrapping them into another tag or a Fragment. JSX 虽然看起来很像 HTML,但在底层其实被转化为了 JavaScript 对象,你不能在一个函数中返回多个对象,除非用一个数组把他们包装起来。这就是为什么多个 JSX 标签必须要用一个父元素或者 Fragment 来包裹。

2. 标签必须闭合 | 2. Close all the tags

JSX requires tags to be explicitly closed: self-closing tags like <img> must become <img />, and wrapping tags like <li>oranges must be written as <li>oranges</li>. JSX 要求标签必须正确闭合。像 <img> 这样的自闭合标签必须书写成 <img />,而像 <li>oranges 这样只有开始标签的元素必须带有闭合标签,需要改为 <li>oranges</li>

This is how Hedy Lamarr’s image and list items look closed: 海蒂·拉玛的照片和待办事项的标签经修改后变为:

<>
<img
src="https://i.imgur.com/yXOvdOSs.jpg"
alt="Hedy Lamarr"
class="photo"
/>
<ul>
<li>发明一种新式交通信号灯</li>
<li>排练一个电影场景</li>
<li>改进频谱技术</li>
</ul>
</>

3. 使用驼峰式命名法给 所有 大部分属性命名!| camelCase all most of the things!

JSX turns into JavaScript and attributes written in JSX become keys of JavaScript objects. In your own components, you will often want to read those attributes into variables. But JavaScript has limitations on variable names. For example, their names can’t contain dashes or be reserved words like class. JSX 最终会被转化为 JavaScript,而 JSX 中的属性也会变成 JavaScript 对象中的键值对。在你自己的组件中,经常会遇到需要用变量的方式读取这些属性的时候。但 JavaScript 对变量的命名有限制。例如,变量名称不能包含 - 符号或者像 class 这样的保留字。

This is why, in React, many HTML and SVG attributes are written in camelCase. For example, instead of stroke-width you use strokeWidth. Since class is a reserved word, in React you write className instead, named after the corresponding DOM property: 这就是为什么在 React 中,大部分 HTML 和 SVG 属性都用驼峰式命名法表示。例如,需要用 strokeWidth 代替 stroke-width。由于 class 是一个保留字,所以在 React 中需要用 className 来代替。这也是 DOM 属性中的命名:

<img
src="https://i.imgur.com/yXOvdOSs.jpg"
alt="Hedy Lamarr"
className="photo"
/>

You can find all these attributes in the list of DOM component props. If you get one wrong, don’t worry—React will print a message with a possible correction to the browser console. 你可以 在 React DOM 元素中找到所有对应的属性。如果你在编写属性时发生了错误,不用担心 —— React 会在 浏览器控制台 中打印一条可能的更正信息。

陷阱

For historical reasons, aria-* and data-* attributes are written as in HTML with dashes. 由于历史原因,aria-*data-* 属性是以带 - 符号的 HTML 格式书写的。

高级提示:使用 JSX 转化器 | Pro-tip: Use a JSX Converter

Converting all these attributes in existing markup can be tedious! We recommend using a converter to translate your existing HTML and SVG to JSX. Converters are very useful in practice, but it’s still worth understanding what is going on so that you can comfortably write JSX on your own. 将现有的 HTML 中的所有属性转化 JSX 的格式是很繁琐的。我们建议使用 转化器 将 HTML 和 SVG 标签转化为 JSX。这种转化器在实践中非常有用。但我们依然有必要去了解这种转化过程中发生了什么,这样你就可以编写自己的 JSX 了。

Here is your final result: 这是最终的结果:

export default function TodoList() {
  return (
    <>
      <h1>海蒂·拉玛的待办事项</h1>
      <img 
        src="https://i.imgur.com/yXOvdOSs.jpg" 
        alt="Hedy Lamarr" 
        className="photo" 
      />
      <ul>
        <li>发明一种新式交通信号灯</li>
        <li>排练一个电影场景</li>
        <li>改进频谱技术</li>
      </ul>
    </>
  );
}

摘要

Now you know why JSX exists and how to use it in components: 现在你知道了为什么我们需要 JSX 以及如何在组件中使用它:

  • React components group rendering logic together with markup because they are related.
  • 由于渲染逻辑和标签是紧密相关的,所以 React 将它们存放在一个组件中。
  • JSX is similar to HTML, with a few differences. You can use a converter if you need to.
  • JSX 类似 HTML,不过有一些区别。如果需要的话可以使用 转化器 将 HTML 转化为 JSX。
  • Error messages will often point you in the right direction to fixing your markup.
  • 错误提示通常会指引你将标签修改为正确的格式。

1挑战 1 个挑战:
将 HTML 转化为 JSX | Convert some HTML to JSX

This HTML was pasted into a component, but it’s not valid JSX. Fix it: 下方的 HTML 是直接被复制到组件中的,所以并不是有效的 JSX,来尝试修复它吧:

export default function Bio() {
  return (
    <div class="intro">
      <h1>欢迎来到我的站点!</h1>
    </div>
    <p class="summary">
      你可以在这里了解我的想法。
      <br><br>
      <b>还有科学家们的<i>照片</b></i></p>
  );
}

Whether to do it by hand or using the converter is up to you! 你可以随意在手动转化或者使用转化器中选择!