![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=1738888174-8FiG4IqE25pLVqXohgsZEZ5IRHtAg1N5-0-489d7bf6032e00b162cfdd2959ca2220)
该方法区别于上文中讲的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=1738888174-n5J67QrCxbRNaT6MmEwKANhNcG4fmwjH-0-9b72faf6dc93f4331332c0ca44e5095d)
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/55_01.jpg?sign=1738888174-Dg81x7ztTSdNBp5C2bJe1aMWG49Y7bHZ-0-2b49c87d6c073b3865c9694022ecb6a9)
在使用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=1738888174-DkWMcywfAafGFR0jjDoKoRPPqpN7eoYm-0-77b7cf85dfd2cc2b475b883f3a74c403)
②children是数组时,则会把数组中的内容展开放入元素中,如:
React.createElement("h1",null,["hello React"]),最终渲染到DOM里的内容为
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/55_03.jpg?sign=1738888174-2DQDtYe4XXHTXznMzUtEygfTWfWACoJb-0-56d850d3c1d3a08a27cb296116fec7f5)
当然这里也可以在数组中放入新的ReactElement,具体代码如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/55_04.jpg?sign=1738888174-LpNFFkr4LpPhftvoIbrypfPyCxFd7pYo-0-eba13dc6a6e8e8a160b01b5b9ed13f23)
最终生成结果如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/55_05.jpg?sign=1738888174-96ou5ZS4DiFno4YdbwGRWSRnaXRF1fGP-0-adc5ec1c975ab43ecc039d11753236ad)
③children是ReactElement时,会直接当作元素的子节点进行添加。需要添加多个子元素时,可以一直跟在后边写。具体代码如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/56_01.jpg?sign=1738888174-2ZcizOs8xRDHzi6D0fnllVHj85sxzpbg-0-36a03a30fd18a590f6613b61a4da9460)
上述代码的展示效果跟数组的案例并没有什么不同,就不再过多复述。通过createElement已经可以正常来构建视图了,但是利用createElement构建视图时,如果视图结构特别复杂,写起来就特别麻烦,而且结果极其不清晰。具体代码如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/56_02.jpg?sign=1738888174-CpNj5gUevgLpVkimVIwYPLKTLaOzRKW7-0-1d602ac87e3bc683910010fe303aa884)
生成结果如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/56_03.jpg?sign=1738888174-06zBdeI0FRDxooJ1cbhtirUvYyjnog5l-0-bda61671df88de60183c76ff04e4af64)
通过上述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=1738888174-ThrLFZWwyr9ksU2jXoXSueHR7GqNFEBb-0-8f98e4d3a82d1ac41204213e9b4a9e40)
从上述示例中可以看到,可以直接在JS中利用前端开发者熟悉的html标签来构建视图,这样的代码结构层级非常清晰,也便于维护,当然上手也更便捷。但是在使用JSX的时候,还有些问题需要开发人员注意。
JSX是JS的语法扩展,但是浏览器并不识别这些扩展,所以需要借助babel.js来对JSX进行编译,使其成为浏览器识别的语法,也就是React.createElement,具体用法如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/57_02.jpg?sign=1738888174-EvJEN6cfXi0EWA5cbLspD2ZE3hfcLm5t-0-45f6a08ee6dd7d2d711a1bcb1c336e9f)
这里有两点需要注意:使用JSX时必须引用babel对代码进行编译;该script标签内的代码需要使用babel编译时,必须设置type="text/babel"。上述代码经过babel编译之后的代码如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/58_01.jpg?sign=1738888174-VIkordrIstaCtvU6V84MiUq9yOg59nCG-0-e3e91acaee40387f64cf4f0e20413c0e)
JSX本身是一个值,这个值是一个ReactElement,而非字符串。在编写的时候一定要注意,如果给一个字符串类型的值时,代码如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/58_02.jpg?sign=1738888174-UjYOMmgVCQcTigSdehoyJAUcCHu8t6xw-0-129c84c1cfceae8316ac5d94d55c2674)
最终在视图上h1并不会被解析成一个标签,而是解析成文本内容。这里主要是因为JSX在解析的时候会被编译,字符串内容会进行转义,这样在设置innerHTML的时候,就不会被解析成标签了,最终呈现结果如图2-3所示。
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/58_03.jpg?sign=1738888174-BfAtLYTOJpR35nOpTcB3vhzXaf1mJf8c-0-5668fb0c2e93b8fe6b07831826e85c24)
●图2-3 最终呈现结果
1.插值表达式
使用JSX时,如果需要视图和数据进行绑定,就需要使用插值表达式,也就是在视图中去插入数据。写法跟ES6中的模板字符串类似,不过用的是{数据},而非${数据}。示例如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/59_01.jpg?sign=1738888174-HM8sXAgLlq0oUJXFT0H1bE1JOdNi6jAp-0-07b11607c255352f434018b88d3be8b4)
在使用插值表达式时,要注意以下几个问题。
1){}中,接收一个JS表达式,可以是运算式,变量或函数调用等。表达式的意思就是这个语句一定会有一个值返回,而插值的意思就是把表达式计算得到的值插入到视图中去。
2){}中,接收的是函数调用时,该函数需要有返回值。明确了{}中可以放什么样的代码之后,再来看看各种不同类型的数据,在插值之后去渲染视图的表现。
3)字符串、数字:原样输出。如:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/59_02.jpg?sign=1738888174-luPdcYvpYrFyl9ot5BKZ9IkYkuSY12E8-0-d8b1a704e45400d29b14b2ce3e785a30)
最后可以得到:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/59_03.jpg?sign=1738888174-C2hfXGmFHzpbgP6LTo6GNwAMsQxIgKDG-0-2172526becb8b92d2826394fe15def32)
4)布尔值、空、未定义:输出空值,也不会有错误。如:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/59_04.jpg?sign=1738888174-TTYlQfYrHxALoSnagJgJfuwG1avuuuvG-0-3da846423e7ee71628d2eac1592b175a)
最后可以得到:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/59_05.jpg?sign=1738888174-lCqzf0cPUbKHoMpawgMLA0k8ArQhkOWT-0-493121aba6566267f623fe7876742d1d)
5)数组:支持直接输出,默认情况下把数组的连接符“,”替换成空,然后直接输出。如下例:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/59_06.jpg?sign=1738888174-02shMdGXHbuj2XJJc0PFGEmOjcxaRNuk-0-6828ca22c65afec3153a389ce60e1e3e)
输出结果为
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/59_07.jpg?sign=1738888174-5QTu0cE529JF3otZ8sahY1gOEzxfapqG-0-0c0729f842b9b3ce78b876fee4857134)
6)对象:不能直接输出,但是可以通过其他方式,如Object.values、Object.keys等方法去解析对象,转换成数组之后进行输出。示例如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/60_01.jpg?sign=1738888174-PeGxrgBvBLXons8UC0H3wl2a5zqZmWgC-0-cefa33ea914541f61aa2005adc11b9fb)
输出结果为
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/60_02.jpg?sign=1738888174-Ab16UjvvOIVIwXbSE0iCgzEn1dN3nUKA-0-0874db90b73005b5d36f8b31f53d7c30)
了解了不同类型的数据在插值中的输出之后,来学习一些比较特殊的渲染情况。
1)列表渲染。所谓的列表渲染,就是需要把数据批量渲染到JSX中,示例如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/60_03.jpg?sign=1738888174-Eu5Wpg7ZMWh8biBNpTGIaeAxcncCj8Au-0-413e6da885fab58aa9e09fb48d85978c)
输出结果为
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/60_04.jpg?sign=1738888174-fwTegvkwI6wTrDsbimyqyTw2l5rtSHNW-0-c3741914909d570920bf40ce4fe1c3a7)
这里利用JSX可以插入数组的特性,利用数组在这里进行批量渲染。在开发环境下有些用户可能会看到这里有一个关于key的错误,这个问题在后面的章节会详细地进行讲解。
2)条件渲染。有些时候,React需要根据不同的情况来渲染不同的内容,但是在插值中不能直接使用if语句,该怎么处理这个问题?有以下几种选择。
①&&与运算符。&&运算有一个特征,左侧的运算结果为true时返回右侧内容。示例如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/60_05.jpg?sign=1738888174-23bxxt6NscLThP6PneufjPNO2Ht2dNAI-0-15765306864e77c845d9afd19f79972a)
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/61_01.jpg?sign=1738888174-fdkKCSnbz4RbEjfzDmj27X72GdX4OeWj-0-8f95ca02573d1d062c600d843d8282a5)
在该示例中,如果age的数值>=18才会输出:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/61_02.jpg?sign=1738888174-0cKhNh9OTt2kFWBV8hSEiUAYHKjK0ONI-0-86605d9a1feef05c74ea34bc40d64469)
否则不做任何输出。
②‖或运算符。‖运算的特征和&&相反,左侧的运算结果为false时,返回右侧内容。如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/61_03.jpg?sign=1738888174-YreZbDNd4nxUJl1zOPSDPjCicxSpjQRv-0-6ebf1016feed7f9b8c57f79d8f4bf138)
在该示例中,如果age的数值>=18不做任何输出,否则输出:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/61_04.jpg?sign=1738888174-6ZM7CnArFuSeQNetN68xhWflCrbh0fTC-0-3caa378fe911eacd15db3b4f8633909b)
③三目运算。示例如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/61_05.jpg?sign=1738888174-JZRSpIES5RYZGaX3VcSYOFQwkfMZNwsn-0-3a3df440a71a1d98b35393e9fa3b9a3c)
在示例中,如果age>=18则输出:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/61_06.jpg?sign=1738888174-3u2x0XT4PMpZEEa355VcM07aoRJiT4mo-0-d4f2d93ffc336052c3dbb371462d3bf7)
否则输出:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/61_07.jpg?sign=1738888174-4zYNmghU4FUEE7yxebmTaie23cym1f6L-0-65ddb33a4941253ae9565a3858ae4d4b)
④在逻辑特别复杂的情况下,也可以借用函数。在函数里进行相关的处理,最后把处理结果返回即可。示例如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/61_08.jpg?sign=1738888174-DpkuP1X8vYQKvKGOJGFuNjB5zGQ3CaeJ-0-be9cfdaf7230e2c6b5de40db6953a9e9)
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/62_01.jpg?sign=1738888174-S6AAJT14fhz19VQh6TIet7gCosN7Ypk1-0-e6f335f8b0ef6092b09db3f8c694e901)
2.JSX属性书写
通过JSX已经可以正常去编写视图了,但编写视图时肯定还需要去添加一些相应的属性,如class、id、type等。但要注意JSX并不是真正的HTML,所以书写时还是有一些注意事项。
1)所有的属性名都使用驼峰命名法。
2)如果属性值是字符串并且是固定不变的,则可以直接写,如:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/62_02.jpg?sign=1738888174-2QLbgizT3dt7tqKDhAA1YeTtYFw1Lvdi-0-0c587235303953d5e6cccaf4c133fb2f)
3)如果属性值是非字符串类型,或者是动态的,则必须用插值表单式。如:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/62_03.jpg?sign=1738888174-cnMnRgiZdNdRxhg4Dg9g37IHcRXFp4vy-0-1b335163acd6ae8d5b14e01847e1cd53)
4)有一些特殊的属性名并不能直接用,具体如下:
①class属性改为className。如:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/62_04.jpg?sign=1738888174-52VZTKhsk790vGIxvgWqwB3c5xxPgKrt-0-4a60855ee9300e58be07734bc8742e88)
②for属性改为htmlFor。
③colspan属性改为colSpan。
5)style在书写的时候要注意它接收的值是个对象,示例如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/62_05.jpg?sign=1738888174-UUDXl4A0G0HseBBUG2ewDDE2SEK6NJMD-0-f64fa03f820f9e667c517a19f665efb9)
这里可以看到style的值是个插值,接收的是一个对象。除了单独声明变量外,也可以直接简写成下例所示:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/62_06.jpg?sign=1738888174-O8AM0gtMQxjhBMDfsza69Y1rIjB7VAp2-0-29ca03baf3a43ba54b57c6d1e0966e02)
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/63_01.jpg?sign=1738888174-8AbxeeosuoTPBb00cPTAEpr9yWSpscCz-0-2309779c5f92333bdc751dcc5f4961c7)
这里是直接把对象写进了插值里。
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=1738888174-hZCdzFX73K1lGDtVU3bXQUBeYWvaVmtq-0-7406a6ce3c8b703443ae053a4546f6a7)
该例子中,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=1738888174-rc5CHHIWupazWSFUSQSGsJzNy80tl5lb-0-a9675bc4b4905c6888021493b6814981)
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/64_01.jpg?sign=1738888174-xMtnmKfmPBTdBh56QdCcYAg9jx0UMcQt-0-d0e51aabdc605ac7531d81f76863794c)
最终渲染出来的真实DOM如下:
![](https://epubservercos.yuewen.com/D960D7/19773741008833706/epubprivate/OEBPS/Images/64_02.jpg?sign=1738888174-IhyyYzFNTJY5MqCJBqWnqFdId3buR5SG-0-4cee08c56015260223560a7eccb1ae9c)
4)所有的标签名字都必须小写。
5)无论单标签还是双标签都必须闭合。
6)JSX并不是HTML,在书写时很多属性的写法不一样。
①属性名都必须遵循驼峰命名法,从第二个单词开始首字母大写。
②个别属性的属性名写法有变化,具体参考2.3.2节。
③style的值接收的是一个对象。
7)在JSX中,插入数据需要用插值表达式{数据}