ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
# 深入 JSX 根本上讲,JSX 只是提供了对 React.createElement(component, props, ...children) 函数的语法糖。JSX 代码: ~~~ <MyButton color="blue" shadowSize={2}> Click Me </MyButton> ~~~ 被编译为: ~~~ React.createElement( MyButton, {color: 'blue', shadowSize: 2}, 'Click Me' ) ~~~ 你也可以使用自封闭形式的标签,如果它没有 children。如: ~~~ <div className="sidebar" /> ~~~ 被编译为: ~~~ React.createElement( 'div', {className: 'sidebar'}, null ) ~~~ 如果你希望测试某些特别的 JSX 如何被转换成 JavaScript,可以尝试使用[在线 Babel 编译器](https://babeljs.io/repl/#?babili=false&evaluate=true&lineWrap=false&presets=es2015%2Creact%2Cstage-0&code=function%20hello\(\)%20%7B%0A%20%20return%20%3Cdiv%3EHello%20world!%3C%2Fdiv%3E%3B%0A%7D)。 ## 指定反射的元素类型 JSX 标签的第一部分确定了 反射的元素的类型。 大写的类型表示 JSX 标签是提及一个 React 组件。这些标签被编译为一个直接的对命名变量的引用,所以如果你使用 JSX `<Foo />` 表达式,Foo 必须在作用域内。 ### React 必须在作用域内 由于 JSX 编译器为 React.createElement 的调用,React 库必须总是在你的 JSX 代码的作用域中。 例如,所有的 imports 在这段代码中都是必须的,即使 React 和 CustomButton 没有直接从 JavaScript 中引用: ~~~ import React from 'react'; import CustomButton from './CustomButton'; function WarningButton() { // return React.createElement(CustomButton, {color: 'red'}, null); return <CustomButton color="red" />; } ~~~ 如果你不使用一个 JavaScript 包 而是添加 React 作为一个 script 标签,它已经作为一个全局 React 存在。 ### 对于 JSX 类型使用点语法 在 JSX 中也可以使用 点语法引用一个 React 组件。如果你有一个单独的模块 exports 了许多 React 组件,这将会很方便。例如,如果 MyComponent.DatePicker 是一个 组件,你可以直接在 JSX 中使用它: ~~~ import React from 'react'; const MyComponents = { DatePicker: function DatePicker(props) { return <div>Imagine a {props.color} datepicker here.</div>; } } function BlueDatePicker() { return <MyComponents.DatePicker color="blue" />; } ~~~ ### 用户定义组件必须是大写 当一个元素类型以小写字母开始,它引用一个内置的组件如 `<div>` 或者 `<span>`,并使一个字符串 'div' 或者 'span' 传递到 React.createElement 中。而由大写字母开头的类型如 `<Foo />` 编译为 React.createElement(Foo) 并对应定义的组件或者 JavaScript 文件中导入的组件。 我们建议使用大写字母命名组件。如果你的确有一个以小写字母开头的组件,可以在 JSX 中使用它之前,分配它到一个大写字母开头的变量。 例如,这段代码将不能正常运行: ~~~ import React from 'react'; // Wrong! This is a component and should have been capitalized: function hello(props) { // Correct! This use of <div> is legitimate because div is a valid HTML tag: return <div>Hello {props.toWhat}</div>; } function HelloWorld() { // Wrong! React thinks <hello /> is an HTML tag because it's not capitalized: return <hello toWhat="World" />; } ~~~ 要修复它,我们可以重命名 hello 为 Hello 并引用时使用 `<Hello />`: ~~~ import React from 'react'; // Correct! This is a component and should be capitalized: function Hello(props) { // Correct! This use of <div> is legitimate because div is a valid HTML tag: return <div>Hello {props.toWhat}</div>; } function HelloWorld() { // Correct! React knows <Hello /> is a component because it's capitalized. return <Hello toWhat="World" />; } ~~~ ### 在运行时选择类型 不能使用一个一般的表达式作为 React 元素类型。如果你真的想要使用一个一般表达式来表示元素类型,只要先分配它到一个大写变量。这通常在你想要基于一个 prop 来渲染一个不同的组件时使用: ~~~ import React from 'react'; import { PhotoStory, VideoStory } from './stories'; const components = { photo: PhotoStory, video: VideoStory }; function Story(props) { // Wrong! JSX type can't be an expression. return <components[props.storyType] story={props.story} />; } ~~~ 要修复它,我们将分配类型到一个大写字母的变量: ~~~ import React from 'react'; import { PhotoStory, VideoStory } from './stories'; const components = { photo: PhotoStory, video: VideoStory }; function Story(props) { // Correct! JSX type can be a capitalized variable. const SpecificStory = components[props.storyType]; return <SpecificStory story={props.story} />; } ~~~ ## JSX 中的 props 有几种不同的方式在 JSX 中指定 props 。 ### JavaScript 表达式 你可以传递任何 JavaScript 表达式作为一个 props,使用花括号包围它。例如,在这个 JSX 中: ~~~ <MyComponent foo={1 + 2 + 3 + 4} /> ~~~ 对于 MyComponent,props.foo 的值将是 10,因为 表达式 1+ 2+3+4 被计算结果。 if 语句和 for 循环不是 JavaScript 格式的表达式,那么它们不能直接用在 JSX 中。反之,你可以把它们放进 花括号中。例如: ~~~ function NumberDescriber(props) { let description; if (props.number % 2 == 0) { description = <strong>even</strong>; } else { description = <i>odd</i>; } return <div>{props.number} is an {description} number</div>; } ~~~ ### 字符串字面量 可以传递字符串字面量作为 一个 prop。这两个 JSX 表达式是等效的: ~~~ <MyComponent message="hello world" /> <MyComponent message={'hello world'} /> ~~~ 当你传递了一个字符串字面量,它的值是 HTML 转义的。所以这两个 JSX 表达式是等效的: ~~~ <MyComponent message="&lt;3" /> <MyComponent message={'<3'} /> ~~~ 这个行为通常是没有关系的。这里只有涉及到完整性。 ### Props 默认为 "True" 如果你没有传递值到 prop,它默认为 true。这两个 JSX 表达式是等效的: ~~~ <MyTextBox autocomplete /> <MyTextBox autocomplete={true} /> ~~~ 普遍来说,我们不建议使用这个,因为它可能和 ES6 的对象 {foo: foo} 而不是 {foo: true} 的短语法 {foo} 混淆。这个行为只在这里所以匹配 HTML 的行为。 ### 扩展的属性 如果你已经有一个对象的属性,你希望在 JSX 中传递它,可以使用 `...` 作为一个 扩展的操作符来传递整个 props 对象。这两个组件是等效的: ~~~ function App1() { return <Greeting firstName="Ben" lastName="Hector" />; } function App2() { const props = {firstName: 'Ben', lastName: 'Hector'}; return <Greeting {...props} />; } ~~~ 扩展属性在你构建一般容器的时候是有用的。然而,它们可能使你的代码混乱,因为容易传递大量不相干的 props 到组件而并不关心它们的内容。我们建议你保守的使用这个语法。 ## JSX 中的 Children In JSX expressions that contain both an opening tag and a closing tag, the content between those tags is passed as a special prop: props.children. There are several different ways to pass children:、 在包含一个开口标签和一个闭口标签的 JSX 表达式中,在它的标签之间的内容被传递为一个特别的 prop:props.children 。还有几种不同的方式来传递 children: ### 字符串字面量 你可以在标签之间放置字符串,props.children 则是这个字符串。对于多个内置的 HTML 元素 这是有用的。例如: ~~~ <MyComponent>Hello world!</MyComponent> ~~~ 这是有效的 JSX ,MyComponent 中的 props.children只是简单的字符串”Hello world!“。HTML 是被转义的,所以你可以一般编写 JSX 就像编写 HTML 一样的方式: ~~~ <div>This is valid HTML &amp; JSX at the same time.</div> ~~~ JSX 移除了开头和结尾处的空白。它也移除了空行。邻近标签的新行被移除;出现在字符串字面量中的新行被认为是一个单独的空格。所以它们渲染相同内容: ~~~ <div>Hello World</div> <div> Hello World </div> <div> Hello World </div> <div> Hello World </div> ~~~ ### JSX Children 你可以提供更多 JSX 元素作为 children。这用于显示嵌套的组件: ~~~ <MyContainer> <MyFirstComponent /> <MySecondComponent /> </MyContainer> ~~~ 你可以混合不同的类型的 children 在一起使用,所以你可以使用字符串字面量和 JSX children。这是JSX 像 HTML 的另外一种方式,所以都是有效的 JSX 和 有效的 HTML: ~~~ <div> Here is a list: <ul> <li>Item 1</li> <li>Item 2</li> </ul> </div> ~~~ 一个 React 组件不能返回多个 React 元素,但是一个单独的 JSX 表达式可以有多个 children,所以如果你想要一个组件渲染多个内容,可以包装它们到一个 div 中,就像这样。 ### JavaScript 表达式 You can pass any JavaScript expression as children, by enclosing it within {}. For example, these expressions are equivalent: 可以传递任何 JavaScript 表达式作为 children,通过封闭的 花括号包括。例如,这些表达式是等效的: ~~~ <MyComponent>foo</MyComponent> <MyComponent>{'foo'}</MyComponent> ~~~ 通常用于渲染一个任意长度 JSX 表达式的列表。例如,这渲染了一个 HTML 列表: ~~~ function Item(props) { return <li>{props.message}</li>; } function TodoList() { const todos = ['finish doc', 'submit pr', 'nag dan to review']; return ( <ul> {todos.map((message) => <Item key={message} message={message} />)} </ul> ); } ~~~ JavaScript 表达式可以混合其它类型的 children 使用。通常用于字符串模板得地方: ~~~ function Hello(props) { return <div>Hello {props.addressee}!</div>; } ~~~ ### 函数作为 children 通常,JavaScript 表达式插入到 JSX 中将执行为一个字符串,一个 React 元素,或者一个这些内容的列表。 然而,props.children 就像其它 prop 那样可以传人任何类型的数据,不只是 React 知道如何渲染的类型。例如,如果你有一个自定义组件,可以让它有一个回调作为 props.children: ~~~ function ListOfTenThings() { return ( <Repeat numTimes={10}> {(index) => <div key={index}>This is item {index} in the list</div>} </Repeat> ); } // Calls the children callback numTimes to produce a repeated component function Repeat(props) { let items = []; for (let i = 0; i < props.numTimes; i++) { items.push(props.children(i)); } return <div>{items}</div>; } ~~~ 传递到一个自定义组件的 children 可以是任何内容,只要这个组件能转换它们到一些 React 在渲染前能理解的东西。这个用法并不常见,但是它在如果你想要扩展 JSX 的能力时可以使用。 ### Booleans,Null,和 Undefined 被忽略 false,null,undefined和 true 是有效的 children。它们只是简单的不会渲染。这些 JSX 表达式都会渲染相同的内容: ~~~ <div /> <div></div> <div>{false}</div> <div>{null}</div> <div>{true}</div> ~~~ 这可以用于条件渲染 React 元素。这个 JSX 只在 showHeader 为 true 时渲染一个 `<Header />`: ~~~ <div> {showHeader && <Header />} <Content /> </div> ~~~ 一个警告是,一些 [”falsy“的值](https://developer.mozilla.org/en-US/docs/Glossary/Falsy),比如数字 0,仍然会被 React 渲染。例如,这段代码不会按照你预期的发生,因为在 props.messages 是一个空数组时 0 会被打印: ~~~ <div> {props.messages.length && <MessageList messages={props.messages} /> } </div> ~~~ 要修复这个问题,确保 && 之前的表达式总是布尔值: ~~~ <div> {props.messages.length > 0 && <MessageList messages={props.messages} /> } </div> ~~~ 相反,如果你想要一个值比如false,true,null 或者 undefined 显示在输出中,你需要[转换它们为字符串](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#String_conversion): ~~~ <div> My JavaScript variable is {String(myVariable)}. </div> ~~~