十二、深入理解JSX
从根本上讲,JSX就是提供了一个React.createElement(component,props,...children)
函数的语法糖。就像下面的JSX代码:
<MyButton color="blue" shadow={2}> Click Me </MyButton>
经过编译后为:
React.createElement( MyButton,{color: 'blue',shadow: 2},'Click Me' )
如果一个标签没有子元素的话,你可以使用/>
来自动闭合。例如:
<div className="sidebar" />
经过编译后为:
React.createElement( 'div',{className: 'sidebar'},null )
如果你想测试一些特定的JSX是如何转换成JavaScript的话,你可以试试在线Babel编译器
。
指定React元素类型
JSX标记的第一部分决定了React元素的类型。
首字母大写的类型表示JSX标记指的为React组件。 这些标签被编译为对指定变量的直接引用,因此如果使用JSX <Foo />
表达式,Foo必须在当前的作用域内。
React必须在作用域内
由于JSX编译的本质是对React.createElement
的调用,因此React库也必须始终在JSX代码的作用域中。
例如,虽然CustomButton
没有直接引用React
,但是这两个导入的模块在这段代码中也还是很有必要的:
import React from 'react'; import ReactDOM from 'react-dom'; function WarningButton(props) { // return React.createElement(CustomButton,{color: 'red'},null); return <CustomButton color="red" /> }
如果不使用JavaScript打包工具并将React通过script标签引入,那么它就会作为一个全局变量React
。
对JSX类型使用『点』表示符
您还可以使用JSX中的点表示符来引用React组件。 如果您有一个模块会导出很多React组件的话,使用这种方法就会十分方便。 例如,如果MyComponents.DatePicker
是一个组件,您可以直接从JSX使用它:
import React from 'react'; import ReactDOM from 'react-dom'; const MyComponents = { DatePicker(props) { return <div>这里有一个颜色为{props.color}的日期选择器</div> } }; function BlueDataPicker(props) { return <MyComponents.DatePicker color="blue" /> } ReactDOM.render( <BlueDataPicker />,document.getElementById('root') );
用户自定义组件必须是首字母大写
当元素类型以是小写字母开头时,它指向一个内置组件,如<div>
或<span>
,并生成一个字符串'div'
或'span'
传递给React.createElement
。 以大写字母开头的类型,如<Foo />
编译为React.createElement(Foo)
,并且在当前作用域内寻找这个名称为Foo
的已定义或已导入组件。
我们建议使用首字母大写
命名组件。 如果你有一个以小写字母开头的组件,请在JSX中使用它之前请将它赋值给一个首字母大写的变量。
下面代码不会按预期运行:
import React from 'react'; //这是错误的,这个组件应该为首字母大写 function hello(props) { // 这是正确的,因为div是一个有效的html标签 return <div>Hello {props.name}</div>; } function HelloWorld(props) { // 这是错误的,因为它是首字母小写,所以React认为<hello />是一个html标签 return <hello name="zhangyatao" /> }
想要修复上面的问题,我们必须将hello
重命名为Hello
,通过<Hello />
来使用该组件:
import React from 'react'; // 这是正确的 function Hello(props) { return <div>Hello {props.name}</div>; } function HelloWorld(props) { // 这是正确的 return <Hello name="zhangyatao" />; }
在运行的时候选择组件类型
不能将常规的javascript表达式用作React元素类型。 如果你想使用一个通用表达式来表示元素的类型,只需将它赋值给一个首字母大写的变量即可。
这通常出现在当你想基于同一个props渲染一个不同的组件的情况下:
import React from 'react'; import {Com1,Com2} from './Components'; const components = { myCom1: Com1,myCom2: Com2 } function RunCom(props) { // 这是错误的,JSX的类型不能这么写 return <components[props.comType] type={props.type} />; }
想要解决上面的问题,只需要将它们赋值给一个首字母大写的变量即可:
import React from 'react'; import {Com1,Com2} from './Components'; const components = { myCom1: Com1,myCom2: Com2 } function RunCom(props) { // 这是正确的,将它们赋值给一个首字母大写的变量 const MyCom = components[props.comType]; return <MyCom type={props.type} />; }
JSX中的Props
在JSX中指定Props有以下几种不同的方法。
JavaScript表达式
你可以传递任何JavaScript表达式作为Props,用{}
括住它们就可以使用。 例如,在这个JSX中:
<MyComponents foo={1 + 2 + 3 + 4} />
对于MyComponent
来说,props.foo
的值将为10
,因为是通过表达式1 + 2 + 3 + 4
计算得到的。
if
语句和for
循环在JavaScript中不是表达式,因此它们不能在JSX中直接使用。 相反,写完它们之后你可以把JSX放在里面。 例如:
function NumberDescriber(props) { let description; if (props.number % 2 === 0) { description = <strong>偶数</strong> } else { description = <strong>奇数</strong> } return <div>{props.number}是一个{description}.</div>; }
字符串直接量
你可以传递一个字符串内容作为props。 这两个JSX表达式是等价的:
<MyComponent message="hi zhangyatao" /> <MyComponent message={'hi zhangyatao'} />
当你传递一个字符串直接量时,它的值是经过html转义的。 所以这两个JSX表达式是等价的:
<MyComponent message='<3' /> <MyComponent message={'<3'} />
Props默认值为true
如果你没有给Props传入一个值,那么它的默认值为true
,这两个JSX表达式是等价的:
<MyTextBox autocomplete /> <MyTextBox autocomplete={true} />
一般来说,我们不建议使用它,因为它可以使用ES6对象的简写{foo}
,也就是{foo:foo}
的简称会和{foo:true}
混淆。 这种行为在这里只是方便它匹配到HTML行为。
Props传递
如果你有一个对象类似的数据作为props,并且想在JSX中传递它,你可以使用...
作为一个“spread”
运算符传递整个props对象。 这两个组件是等效的:
function App() { return <Greeting firstName="yatao" lastName="zhang" />; } function App() { const props = {firstName: 'yatao',lastName: 'zhang'}; return <Greeting {...props} />; }
当创建一个通用容器时,spread
props很有用。
然而,他们也可以让你的代码变得有点凌乱,这样很容易使大量不相关的prps传递给那些不关心它们的组件。 建议您谨慎使用此语法。
JSX中的子元素和子组件
在包含开始标记和结束标记的JSX表达式中,这些标记之间的内容通过一种特殊的prop:props.children
传递。 有几种不同的方式传递子组件:
字符串直接量
你可以在开始和结束标签之间放一个字符串,那么props.children
就是那个字符串。 这对许多内置的HTML元素很有用。 例如:
function MyComponent(props) { return <div>{props.children}<div>; //=> <div>hello zhangyatao</div> } <MyComponent>Hello zhangyatao</MyComponent>
这是有效的JSX,并且MyComponent
中的props.children
将是字符串“Hello zhangyatao”
。 HTML标签是不会经过转义的,所以你一般可以写JSX就像你写HTML一样:
<div>这是一个html标签 & 同时也是个JSX</div>
JSX会删除行的开始和结尾处的空格。 它也会删除中间的空行。 与标签相邻的空行被会被删除;
在字符串文本中间出现的空行会缩合成一个空格。 所以这些都渲染相同的事情:
<div>hello zhangyatao</div> <div> hello zhangyatao </div> <div> hello zhangyatao </div> <div> hello zhangyatao </div>
JSX子元素
你可以使用很多个JSX元素作为子元素。 这对需要嵌套的显示类型组件很有用:
<Dialog> <DialogHeader /> <DialogBody /> <DialogFooter /> </Dialog>
你可以将不同类型的子元素混合在一起,因此JSX子元素可以与字符串直接量一起使用。 这是JSX的另一种方式,就像一个HTML一样:
<div> 这是一个列表 <ul> <li>item 1</li> <li>item 2</li> </ul> </div>
一个React组件不可能返回多个React元素,但是一个JSX表达式可以包含多个子元素,因此如果你想让一个组件渲染多个东西,你可以将它们统一放置在就像上面那样的div中。
Javascript表达式
您可以将任何JavaScript表达式放在{}
中作为子组件传递。 例如,下面这些表达式是等价的:
function MyComponent(props) { return <div>{props.children}<div>; //=> <div>hi zhangyatao</div> } <MyComponent>hi zhangyatao</MyComponent> <MyComponent>{'hi zhangyatao'}</MyComponent>
这通常用于渲染任意长度的JSX表达式列表。 例如,这将渲染一个HTML列表:
function Item(props) { return <li>{props.message}</li>; } function TodoList(props) { const todos = ['完成文档','出去逛街','打一局dota']; return ( <ul> {todos.map(message => <Item key={message} message={message} />)} </ul> ); }
JavaScript表达式可以与其他类型的子元素混合使用。 这通常用于替换字符串模板:
function Hello(props) { return <div>Hello {props.name}</div>; }
使用函数作为子元素
通常,插入JSX中的JavaScript表达式都最终返回为一个字符串、React元素、一个列表。
当然,props.children
可以像任何其他props那样工作,它可以传递任何类型的数据,并不局限于那些告诉React应该如何渲染的东东。 例如,如果您有一个自定义组件,您可以将props.children
作为一个回调函数:
import React from 'react'; import ReactDOM from 'react-dom'; function Repeat(props) { let items = []; let callback = props.children; var numTimes = props.numTimes; for(var i = 0 ; i < numTimes ; i++ ){ items.push(callback(i)); } return <div>{items}</div>; } function ListOfTenThings(props) { return ( <Repeat numTimes={10}> {index => <div key={index}>这是列表中的第{index}项</div>} </Repeat> ); } ReactDOM.render( <ListOfTenThings/>,document.getElementById('root') );
传递给自定义组件的子元素可以是任何东西,只要在React在渲染之前,该组件将它们转换为可以理解的东西即可。 这种用法并不常见,如果你想扩展JSX的其他能力,可以通过这个例子了解下它的工作原理。
布尔值、null、undefined在渲染时会被自动忽略
false
,null
,undefined
和true
是有效的子元素,不过他们从根本上讲是不参与渲染的。 这些JSX表达式将渲染处相同的东西:
<div /> <div></div> <div>{false}</div> <div>{null}</div> <div>{true}</div>
这对于有条件地呈现React元素很有用。 如果showHeader
为true
,那么这个JSX只渲染一个<Header />
:
<div> {showHeader && <Header />} <Content /> </div>
如果返回一些“假的”
值就会收到一个警告,如数字0
,不过React仍然会渲染。 例如,此代码将不会像您预期的那样工作,因为当props.messages
是空数组时将打印0
:
<div> {props.messages.length && <Message messages={props.messages} />} </div>
想要修复上面的问题,你要确定这个表达式在&&之前总返回布尔值
:
<div> {props.messages.length > 0 && <Message messages={props.messages} />} </div>
相反,如果你想要一个值如false
,true
,null
或undefined
出现在输出中,你必须先将它转换为字符串
:
import React from 'react'; import ReactDOM from 'react-dom'; function MyVariable(props) { const myVariable = false; // 如果这里不把false转换为字符串,这只会输出『我的javascript变量是』 const convertedVar = String(myVariable); return ( <div> 我的javascript变量是{convertedVar} </div> ); } ReactDOM.render( <MyVariable/>,document.getElementById('root') );原文链接:https://www.f2er.com/react/305260.html