![React工程师修炼指南](https://wfqqreader-1252317822.image.myqcloud.com/cover/475/37323475/b_37323475.jpg)
2.3 React视图渲染
构建视图一直是React的重点,从createElement到JSX,React构建视图的方法一直深受开发者喜爱。
2.3.1 ReactElement
当需要用React创建虚拟DOM时,React专门提供了一个方法createElement()。注意该方法并非是原生DOM中的createElement。具体使用方法如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/54_02.jpg?sign=1739018527-GtIwiBuUADXyFG4J4pBQkiJjnef8rgaY-0-a2c005f9a8de7c0dcb93a685474ec0de)
该方法区别于上文中讲的ReactDOM,它属于React对象,不要混淆。利用createElement方法,就可以来创建ReactElement,也就是React中的虚拟DOM。具体参数如下。
1)type要创建的标签类型。如要创建的是个div标签,则写React.createElement("div"),一定注意type的类型是一个字符串。
2)congfig参数是设置生成的节点的相关属性,这里要注意congfig的类型是一个纯对象,具体代码如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/54_03.jpg?sign=1739018527-37D63pkyWsJdQekygrTRR0Fk06Q5RzaH-0-86fbca1d3f720d172226e13837f4c9c4)
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/55_01.jpg?sign=1739018527-vvIW58sc7dLAvA1oDJfsV30xlnZ0j7X5-0-2f98a164734c7a0b37a115dea56088e9)
在使用congfig的时候,有两个问题需要注意。
①没有属性需要定义,但又需要传递children参数时,congfig可以给null,React.createElement("h1",null,"hello React")。
②congfig中有两个固定的参数key和ref,最好不要乱用,后续章节会详细讲到。
3)children代表该元素的内容或者子元素。具体有三种不同的写法。
①children是字符串时,则代表在元素里添加文本内容,如:
React.createElement("h1",null,"hello React"),最终渲染到DOM里的内容为
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/55_02.jpg?sign=1739018527-dLR7qlZ8pK2157JxlL2JzBzp40ZRkdBS-0-edec5e3e914d40adcf88d70ef148daa7)
②children是数组时,则会把数组中的内容展开放入元素中,如:
React.createElement("h1",null,["hello React"]),最终渲染到DOM里的内容为
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/55_03.jpg?sign=1739018527-NdGdk72KpwnXcGrHiijJ9PJzS5TQhqNo-0-bccdc55427e317b5d6221249baaaa21f)
当然这里也可以在数组中放入新的ReactElement,具体代码如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/55_04.jpg?sign=1739018527-sk2MF4jf26CwbgxGWm6PGzZodPihu1is-0-d707358b074d8a43fd02f9ecdf17773e)
最终生成结果如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/55_05.jpg?sign=1739018527-WwBR7clxXl5jpAW4oP0XTEhTggDUYheP-0-d94964b715046e1afa3592a42eb887cb)
③children是ReactElement时,会直接当作元素的子节点进行添加。需要添加多个子元素时,可以一直跟在后边写。具体代码如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/56_01.jpg?sign=1739018527-rM9zJXmueQglBhAzmKIl8R7LjnI6WxWd-0-efaa82b814400162284f8d8f83c430a4)
上述代码的展示效果跟数组的案例并没有什么不同,就不再过多复述。通过createElement已经可以正常来构建视图了,但是利用createElement构建视图时,如果视图结构特别复杂,写起来就特别麻烦,而且结果极其不清晰。具体代码如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/56_02.jpg?sign=1739018527-1gs4FsCxuLTBTpmdbJcWsRvw2XGEo2tI-0-a92d72848a95546cfaba366c93971430)
生成结果如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/56_03.jpg?sign=1739018527-h8GB0sSnk91p0fwUCANzt9SDFJvMAH5i-0-4c369d7b37fc2827203ad8ffc4f6d111)
通过上述demo可以看到一个比较复杂视图的编写,但是这种代码从层级结构上来看极其不清晰,所以在真正开发时不推荐使用ReactElement的这种方式来编写视图。React中提供了一个编写视图的神器JSX。
2.3.2 JSX
JSX是什么呢?展开来说就是JavaScript+XML,是一个看起来很像XML的JavaScript语法扩展。具体代码如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/57_01.jpg?sign=1739018527-Jp6dgHfC16tsn60SBQyMTrQ6kr2PglDG-0-8f6370ef75c45fd45dde4cde9a02f3d8)
从上述示例中可以看到,可以直接在JS中利用前端开发者熟悉的html标签来构建视图,这样的代码结构层级非常清晰,也便于维护,当然上手也更便捷。但是在使用JSX的时候,还有些问题需要开发人员注意。
JSX是JS的语法扩展,但是浏览器并不识别这些扩展,所以需要借助babel.js来对JSX进行编译,使其成为浏览器识别的语法,也就是React.createElement,具体用法如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/57_02.jpg?sign=1739018527-7XoEGWg4yUsxGByrvFE3eNOl6uU3pno1-0-5278c4267f6fd9095660bbd5f1e5b1bf)
这里有两点需要注意:使用JSX时必须引用babel对代码进行编译;该script标签内的代码需要使用babel编译时,必须设置type="text/babel"。上述代码经过babel编译之后的代码如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/58_01.jpg?sign=1739018527-96FgtEFDNEM1x1so5cQTWAZ8cEO6g5yN-0-1f2963bb4ac6631283200567178c20d2)
JSX本身是一个值,这个值是一个ReactElement,而非字符串。在编写的时候一定要注意,如果给一个字符串类型的值时,代码如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/58_02.jpg?sign=1739018527-tUlxXp5UOFH9tkojZpNKYIHcTLzkFDYG-0-98a5a8ceb9d04e6d63d7c56d93525745)
最终在视图上h1并不会被解析成一个标签,而是解析成文本内容。这里主要是因为JSX在解析的时候会被编译,字符串内容会进行转义,这样在设置innerHTML的时候,就不会被解析成标签了,最终呈现结果如图2-3所示。
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/58_03.jpg?sign=1739018527-sF66fPENraSSHBo4ERVIDwdL39YuQdAp-0-bd3d3df43e484552fbbe90032ae39986)
●图2-3 最终呈现结果
1.插值表达式
使用JSX时,如果需要视图和数据进行绑定,就需要使用插值表达式,也就是在视图中去插入数据。写法跟ES6中的模板字符串类似,不过用的是{数据},而非${数据}。示例如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/59_01.jpg?sign=1739018527-11k1RuuTHk03TcuCw3Iyftb0U7eB8ftg-0-e68adef38f9250f5083f30dfeb49ffa0)
在使用插值表达式时,要注意以下几个问题。
1){}中,接收一个JS表达式,可以是运算式,变量或函数调用等。表达式的意思就是这个语句一定会有一个值返回,而插值的意思就是把表达式计算得到的值插入到视图中去。
2){}中,接收的是函数调用时,该函数需要有返回值。明确了{}中可以放什么样的代码之后,再来看看各种不同类型的数据,在插值之后去渲染视图的表现。
3)字符串、数字:原样输出。如:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/59_02.jpg?sign=1739018527-CAxktE5uajmWlP1igN7ezyWHqTd2wIEl-0-8440ba145651169648910c5526c4c830)
最后可以得到:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/59_03.jpg?sign=1739018527-7JxcC6SNQBlrWVJCHUjVTEwX45xJnVkC-0-89b4487763fb5fe74ccdcb1a42acff21)
4)布尔值、空、未定义:输出空值,也不会有错误。如:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/59_04.jpg?sign=1739018527-rGHAm1JHnRQhjSf80dxwzLKRhelw3y9L-0-efa2415480dbe5be6c52065fe08e9286)
最后可以得到:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/59_05.jpg?sign=1739018527-W90O6fDmlvabePcvDuBl61OTI5iaINkc-0-a1d1b597065ffee4b0eddb6c77de6312)
5)数组:支持直接输出,默认情况下把数组的连接符“,”替换成空,然后直接输出。如下例:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/59_06.jpg?sign=1739018527-fvWyPehTldgcK1Gni0fdL6tl1FDjUpuM-0-d5d5e708712054b9029f277092cba43d)
输出结果为
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/59_07.jpg?sign=1739018527-6x664jCfG9HDsSUiIsjAWrMhdFTgP5Br-0-48b00146df08a21c3ddc5ebc1336317a)
6)对象:不能直接输出,但是可以通过其他方式,如Object.values、Object.keys等方法去解析对象,转换成数组之后进行输出。示例如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/60_01.jpg?sign=1739018527-NXMFoVnbFTPXNCn0a3Gq6uhmtK4Ibap0-0-81b0cb189f5cd2b767c83c175e30ebf2)
输出结果为
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/60_02.jpg?sign=1739018527-LDRQ7QU0uJRjM2RmcayS1XUMli0lpgIV-0-d62e369dcd822f198fa4d00aa437b7a7)
了解了不同类型的数据在插值中的输出之后,来学习一些比较特殊的渲染情况。
1)列表渲染。所谓的列表渲染,就是需要把数据批量渲染到JSX中,示例如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/60_03.jpg?sign=1739018527-K6OlZVWYauZe1XxlzFlQ47SzWbsLgyoe-0-3a5785733cd1975850ef57110069f65c)
输出结果为
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/60_04.jpg?sign=1739018527-gmkir55C1KVyWfk6l4UfSyAGNw2nslKk-0-8dc8ac445136628ff74cbb5110fa73be)
这里利用JSX可以插入数组的特性,利用数组在这里进行批量渲染。在开发环境下有些用户可能会看到这里有一个关于key的错误,这个问题在后面的章节会详细地进行讲解。
2)条件渲染。有些时候,React需要根据不同的情况来渲染不同的内容,但是在插值中不能直接使用if语句,该怎么处理这个问题?有以下几种选择。
①&&与运算符。&&运算有一个特征,左侧的运算结果为true时返回右侧内容。示例如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/60_05.jpg?sign=1739018527-530fEqAvuXcOOBO16GZ9kxxZweGUsghI-0-658341f6d8f5c7bc87ca1d6cd0a1c23b)
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/61_01.jpg?sign=1739018527-z84l76d8ZCNGB6yIG912swnVELl0eTRC-0-c7ba9c9593c604863fa7d447e4fbbb96)
在该示例中,如果age的数值>=18才会输出:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/61_02.jpg?sign=1739018527-fCrDaOjffPQ7aTjRm3cWLASNYXLvahVE-0-f4774a1547afe8edb9ec71e76f9eda49)
否则不做任何输出。
②‖或运算符。‖运算的特征和&&相反,左侧的运算结果为false时,返回右侧内容。如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/61_03.jpg?sign=1739018527-HE2IQ5toNiLsDHzTaQyn7Cge3qqQU13m-0-ede382b3418c9cf1a31830b26bca9cfa)
在该示例中,如果age的数值>=18不做任何输出,否则输出:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/61_04.jpg?sign=1739018527-wjeBdxkc2yuZN7wCgcCNzXO8FxcqW6pC-0-49766f229380a3709b8e9dabeba37ad2)
③三目运算。示例如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/61_05.jpg?sign=1739018527-Pw8c3fVoF2nB5slfG8rxbSZAeowKAza4-0-5eb17706ba2e41e2f3911606d55c712d)
在示例中,如果age>=18则输出:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/61_06.jpg?sign=1739018527-Tz9M55BlrUrJtcL7iNiZXLJMQOGX5L1I-0-eb784b75f636fc2b7d36f34725d233ad)
否则输出:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/61_07.jpg?sign=1739018527-2MbDK1Ubq6HXaF9GZo8Wv0moGO5q71UY-0-a65d417b58c0441b8c94764e2bd57be4)
④在逻辑特别复杂的情况下,也可以借用函数。在函数里进行相关的处理,最后把处理结果返回即可。示例如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/61_08.jpg?sign=1739018527-lNHuCYygvPHBbRLzTFak6232mHucpMcQ-0-6d0877ad5481225dea5d4149e5bd794f)
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/62_01.jpg?sign=1739018527-cjk6tR5BPdzbLMFgapkabznKcvyy2f46-0-de04598d21a648995825e79bffd028d7)
2.JSX属性书写
通过JSX已经可以正常去编写视图了,但编写视图时肯定还需要去添加一些相应的属性,如class、id、type等。但要注意JSX并不是真正的HTML,所以书写时还是有一些注意事项。
1)所有的属性名都使用驼峰命名法。
2)如果属性值是字符串并且是固定不变的,则可以直接写,如:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/62_02.jpg?sign=1739018527-r0BT7WG8KDfJPZdm1KOvoEF5eQ6SvShw-0-d94311bc71631c308b11b8369112a48c)
3)如果属性值是非字符串类型,或者是动态的,则必须用插值表单式。如:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/62_03.jpg?sign=1739018527-qOYsxZh4zofaFOERYOeLnYMIkes8Ytxt-0-09d49c2db9ff11a7ae2d9a5946016bf0)
4)有一些特殊的属性名并不能直接用,具体如下:
①class属性改为className。如:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/62_04.jpg?sign=1739018527-e2wWJxf3iKkUXpcSZP8uNfgObaleWq1e-0-3fe1e3b16a3804f3ed7f4a95bf2d037f)
②for属性改为htmlFor。
③colspan属性改为colSpan。
5)style在书写的时候要注意它接收的值是个对象,示例如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/62_05.jpg?sign=1739018527-2QUXLKF75B46U4lmbLNqeBjeDV6HnzMJ-0-c6e9e95a77f5613280e56251c96d2789)
这里可以看到style的值是个插值,接收的是一个对象。除了单独声明变量外,也可以直接简写成下例所示:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/62_06.jpg?sign=1739018527-wq94ETqjoWqqgwdkOHb6Ss41PEoytdvO-0-40ec9550e4e6535e109ebf46e722f4fd)
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/63_01.jpg?sign=1739018527-eSIdnUsqLI7yVI5rYPVdN5blvNbU9m3d-0-308362e0057f5bf2e58cf1495d52c0af)
这里是直接把对象写进了插值里。
3.JSX注意事项
前文中详细讲解了JSX的使用,不过真正使用JSX的时候,还需要注意它的一些问题,下面对JSX的使用问题进行一个汇总。
1)浏览器并不支持JSX,在使用时要使用babel编译。
2)JSX不要写成字符串,否则标签会被当作文本直接输出。
3)JSX是一个值,在输出时只能有一个顶层标签,示例如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/63_02.jpg?sign=1739018527-YMqdwFQ5blneXH4XqPoo1J8bV1SKyUhu-0-a99f30da6a0442c5bf0052bec8c853c5)
该例子中,JSX输出了三个顶层标签header、div、footer,这样运行时就会报错。如果JSX的顶层标签不希望在DOM中被解析出来,则可以使用<React.Fragment></React.Fragment>作为顶层标签。Fragment是React提供的一个容器组件,它本身并不会在真实的DOM中渲染出来。利用Fragment对上述案例进行修改,具体代码如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/63_03.jpg?sign=1739018527-EuMSpjtDDdAU2JRychRBMmZnkkV231cn-0-36acb52c91f24dbd351e0f2765e6ad30)
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/64_01.jpg?sign=1739018527-OP3BXDnNSTeSHwCntc5AOM5vDMhXX9I6-0-0b4c7a82bc6093ca15a1ad9ebb97e08b)
最终渲染出来的真实DOM如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/64_02.jpg?sign=1739018527-0Jd3XkPDHSQ1XBuf2Ks3nJ3cn3pNYH85-0-57376113838d6f03ef79a1c26877e089)
4)所有的标签名字都必须小写。
5)无论单标签还是双标签都必须闭合。
6)JSX并不是HTML,在书写时很多属性的写法不一样。
①属性名都必须遵循驼峰命名法,从第二个单词开始首字母大写。
②个别属性的属性名写法有变化,具体参考2.3.2节。
③style的值接收的是一个对象。
7)在JSX中,插入数据需要用插值表达式{数据}