深入 JSX
Posted 苍青浪
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入 JSX相关的知识,希望对你有一定的参考价值。
从本质上讲,JSX 只是为 React.createElement(component, props, ...children)
函数提供的语法糖。JSX代码:
1
2
3
|
<MyButton color= "blue" shadowSize={2}> Click Me </MyButton> |
编译后的结果为:
1
2
3
4
5
|
React.createElement( MyButton, {color: ‘blue‘ , shadowSize: 2}, ‘Click Me‘ ) |
如果不存在子节点,你可以使用自闭合(self-closing)格式的标签。例如:
1
|
<div className= "sidebar" /> |
编译为:
1
2
3
4
5
|
React.createElement( ‘div‘ , {className: ‘sidebar‘ }, null ) |
可以发现对于非自定义的html标签‘div’是以字符串的形式传入的。
指定 React 元素类型
一个 JSX 标签的开始部分决定了 React 元素的类型。
首字母大写的标签指示 JSX 标签是一个 React 组件。这些标签会被编译成 命名变量 的直接引用。所以如果你使用 JSX <Foo />
表达式,那么 Foo
必须在作用域中。
React 必须在作用域中
因为 JSX 被编译为 React.createElement
的调用,所以 React
库必须在你 JSX 代码的作用域中。
例如,所有的 imports 在这段代码中都是必须的,虽然 React
和 CustomButton
并有直接从 javascript 中引用:
1
2
3
4
5
6
7
|
import React from ‘react‘ ; import CustomButton from ‘./CustomButton‘ ; function WarningButton() { // return React.createElement(CustomButton, {color: ‘red‘}, null); return <CustomButton color= "red" />; } |
如果你不使用 JavaScript 打包器,而是通过在 <script>
标签加载 React ,它已经作为一个全局 React
存在。
对 JSX 类型使用点语法
在 JSX 中,你也可以使用点语法引用一个 React 组件。如果你有一个单一模块(module) ,但却 导出(exports) 多个 React 组件时,这将会很方便。例如,如果 MyComponents.DatePicker
是一个组件,你可以直接在 JSX 中使用它:
1
2
3
4
5
6
7
8
9
10
11
|
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>
的内置组件,会给 React.createElement
方法传递 ‘div‘
或者 ‘span‘
字符串。以大写字母开头的类型,类似于 <Foo />
,会被编译成 React.createElement(Foo)
,对应于自定义组件 或者在 JavaScript 文件中导入的组件。
在运行时选择类型
不能使用一个普通的表达式作为 React 元素类型。如果你想使用普通表达式来表示元素类型,首先你需要将其赋值给大写的变量。这通常会出现在根据不同的 props 渲染不同的组件时:
1
2
3
4
5
6
7
8
9
10
11
12
|
import React from ‘react‘ ; import { PhotoStory, VideoStory } from ‘./stories‘ ; const components = { photo: PhotoStory, video: VideoStory }; function Story(props) { // 错误!JSX 类型不能是表达式 return <components[props.storyType] story={props.story} />; } |
为了解决这个问题,首先需要将表达式赋值给一个以大写字母开头的变量。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import React from ‘react‘ ; import { PhotoStory, VideoStory } from ‘./stories‘ ; const components = { photo: PhotoStory, video: VideoStory }; function Story(props) { // 正确!JSX 类型可以是一个以大写字母开头的变量. const SpecificStory = components[props.storyType]; return <SpecificStory story={props.story} />; } |
JSX 中的 props(属性)
有几种不同的方式来指定 JSX 中的 props(属性) 。
JavaScript 表达式作为 props(属性)
常用方法:
1
|
<MyComponent foo={1 + 2 + 3 + 4} /> |
对于 MyComponent
来讲,props.foo
的值为 10
,因为表达式 1 + 2 + 3 + 4
会被计算估值。
在 JavaScript 中,if
语句和 for
循环不是表达式,因此不能在 JSX 中直接使用。但你可以将他们放在附近的代码块中,例如:
1
2
3
4
5
6
7
8
9
|
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>; } |
字符串字面量
props(属性) 默认为 “true”
如果你没给 prop(属性) 传值,那么他默认为 true
。下面两个 JSX 表达式是等价的:
1
2
3
|
<MyTextBox autocomplete /> <MyTextBox autocomplete={ true } /> |
通常情况下,不建议使用这种类型,因为这会与ES6中的对象shorthand混淆 。ES6 shorthand 中 {foo}
指的是 {foo: foo}
的简写,而不是 {foo: true}
。这种行为只是为了与 HTML 的行为相匹配。(愚人码头注:举个例子,在 HTML 中,<input type="radio" value="1" disabled />
与 <input type="radio" value="1" disabled="true" />
是等价的。JSX 中的这种行为就是为了匹配 HTML 的行为。)
属性扩展
如果你已经有一个 object 类型的 props
,并且希望在 JSX 中传入,你可以使用扩展操作符 ...
传入整个 props 对象。这两个组件是等效的:
1
2
3
4
5
6
7
8
|
function App1() { return <Greeting firstName= "Ben" lastName= "Hector" />; } function App2() { const props = {firstName: ‘Ben‘ , lastName: ‘Hector‘ }; return <Greeting {...props} />; } |
JSX Children
通过使用 {}
包裹,你可以将任何的 JavaScript 元素而作为 children(子元素) 传递,下面表达式是等价的:
1
2
3
|
<MyComponent>foo</MyComponent> <MyComponent>{ ‘foo‘ }</MyComponent> |
Functions(函数) 作为 Children(子元素)
通常情况下,嵌入到 JSX 中的 JavaScript 表达式会被认为是一个字符串、React元素 或者是这些内容的一个列表。然而, props.children
类似于其他的 props(属性) ,可以被传入任何数据,而不是仅仅只是 React 可以渲染的数据。例如,如果有自定义组件,其 props.children
的值可以是回调函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// 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>; } function ListOfTenThings() { return ( <Repeat numTimes={10}> {(index) => <div key={index}>This is item {index} in the list</div>} </Repeat> ); } |
这个例子中,直接在Repeat组件中传了一个钩子函数进去,并且用一对{}包裹起来,在Repeat中,就可以通过props.children拿到父组件中钩子函数的返回值,并且通过加工包装成React元素。
传递到自定义组件的 children(子元素) 可以是任何内容,只要在渲染之前组件可以将其转化为 React 能够处理的东西即可。
Booleans, Null, 和 Undefined 被忽略
false
,null
,undefined
,和 true
都是有效的的 children(子元素) 。但是并不会被渲染,下面的JSX表达式渲染效果是相同的:
1
2
3
4
5
6
7
8
9
10
11
|
<div /> <div></div> <div>{ false }</div> <div>{ null }</div> <div>{undefined}</div> <div>{ true }</div> |
在有条件性渲染 React 元素时非常有用。如果 showHeader
为 true
时,<Header />
会被渲染:
1
2
3
4
|
<div> {showHeader && <Header />} <Content /> </div> |
短路表达式:
showHeader为true则往下执行,为false则中断,<Header>组件将不会被渲染。
需要注意的是“falsy”值,例如数值 0
,仍然会被 React 渲染。例如,这段代码不会按照你预期的发生,因为当 props.messages
是一个空数组时 0
会被打印:
1
2
3
4
5
|
<div> {props.messages.length && <MessageList messages={props.messages} /> } </div> |
要修复这个问题,确保 &&
之前的表达式总是布尔值:
1
2
3
4
5
|
<div> {props.messages.length > 0 && <MessageList messages={props.messages} /> } </div> |
反过来,如果在输出中想要渲染 false
,true
,null
或者 undefined
,你必须先将其转化为字符串。
以上是关于深入 JSX的主要内容,如果未能解决你的问题,请参考以下文章