
2.3 React视图渲染
构建视图一直是React的重点,从createElement到JSX,React构建视图的方法一直深受开发者喜爱。
2.3.1 ReactElement
当需要用React创建虚拟DOM时,React专门提供了一个方法createElement()。注意该方法并非是原生DOM中的createElement。具体使用方法如下:

该方法区别于上文中讲的ReactDOM,它属于React对象,不要混淆。利用createElement方法,就可以来创建ReactElement,也就是React中的虚拟DOM。具体参数如下。
1)type要创建的标签类型。如要创建的是个div标签,则写React.createElement("div"),一定注意type的类型是一个字符串。
2)congfig参数是设置生成的节点的相关属性,这里要注意congfig的类型是一个纯对象,具体代码如下:


在使用congfig的时候,有两个问题需要注意。
①没有属性需要定义,但又需要传递children参数时,congfig可以给null,React.createElement("h1",null,"hello React")。
②congfig中有两个固定的参数key和ref,最好不要乱用,后续章节会详细讲到。
3)children代表该元素的内容或者子元素。具体有三种不同的写法。
①children是字符串时,则代表在元素里添加文本内容,如:
React.createElement("h1",null,"hello React"),最终渲染到DOM里的内容为

②children是数组时,则会把数组中的内容展开放入元素中,如:
React.createElement("h1",null,["hello React"]),最终渲染到DOM里的内容为

当然这里也可以在数组中放入新的ReactElement,具体代码如下:

最终生成结果如下:

③children是ReactElement时,会直接当作元素的子节点进行添加。需要添加多个子元素时,可以一直跟在后边写。具体代码如下:

上述代码的展示效果跟数组的案例并没有什么不同,就不再过多复述。通过createElement已经可以正常来构建视图了,但是利用createElement构建视图时,如果视图结构特别复杂,写起来就特别麻烦,而且结果极其不清晰。具体代码如下:

生成结果如下:

通过上述demo可以看到一个比较复杂视图的编写,但是这种代码从层级结构上来看极其不清晰,所以在真正开发时不推荐使用ReactElement的这种方式来编写视图。React中提供了一个编写视图的神器JSX。
2.3.2 JSX
JSX是什么呢?展开来说就是JavaScript+XML,是一个看起来很像XML的JavaScript语法扩展。具体代码如下:

从上述示例中可以看到,可以直接在JS中利用前端开发者熟悉的html标签来构建视图,这样的代码结构层级非常清晰,也便于维护,当然上手也更便捷。但是在使用JSX的时候,还有些问题需要开发人员注意。
JSX是JS的语法扩展,但是浏览器并不识别这些扩展,所以需要借助babel.js来对JSX进行编译,使其成为浏览器识别的语法,也就是React.createElement,具体用法如下:

这里有两点需要注意:使用JSX时必须引用babel对代码进行编译;该script标签内的代码需要使用babel编译时,必须设置type="text/babel"。上述代码经过babel编译之后的代码如下:

JSX本身是一个值,这个值是一个ReactElement,而非字符串。在编写的时候一定要注意,如果给一个字符串类型的值时,代码如下:

最终在视图上h1并不会被解析成一个标签,而是解析成文本内容。这里主要是因为JSX在解析的时候会被编译,字符串内容会进行转义,这样在设置innerHTML的时候,就不会被解析成标签了,最终呈现结果如图2-3所示。

●图2-3 最终呈现结果
1.插值表达式
使用JSX时,如果需要视图和数据进行绑定,就需要使用插值表达式,也就是在视图中去插入数据。写法跟ES6中的模板字符串类似,不过用的是{数据},而非${数据}。示例如下:

在使用插值表达式时,要注意以下几个问题。
1){}中,接收一个JS表达式,可以是运算式,变量或函数调用等。表达式的意思就是这个语句一定会有一个值返回,而插值的意思就是把表达式计算得到的值插入到视图中去。
2){}中,接收的是函数调用时,该函数需要有返回值。明确了{}中可以放什么样的代码之后,再来看看各种不同类型的数据,在插值之后去渲染视图的表现。
3)字符串、数字:原样输出。如:

最后可以得到:

4)布尔值、空、未定义:输出空值,也不会有错误。如:

最后可以得到:

5)数组:支持直接输出,默认情况下把数组的连接符“,”替换成空,然后直接输出。如下例:

输出结果为

6)对象:不能直接输出,但是可以通过其他方式,如Object.values、Object.keys等方法去解析对象,转换成数组之后进行输出。示例如下:

输出结果为

了解了不同类型的数据在插值中的输出之后,来学习一些比较特殊的渲染情况。
1)列表渲染。所谓的列表渲染,就是需要把数据批量渲染到JSX中,示例如下:

输出结果为

这里利用JSX可以插入数组的特性,利用数组在这里进行批量渲染。在开发环境下有些用户可能会看到这里有一个关于key的错误,这个问题在后面的章节会详细地进行讲解。
2)条件渲染。有些时候,React需要根据不同的情况来渲染不同的内容,但是在插值中不能直接使用if语句,该怎么处理这个问题?有以下几种选择。
①&&与运算符。&&运算有一个特征,左侧的运算结果为true时返回右侧内容。示例如下:


在该示例中,如果age的数值>=18才会输出:

否则不做任何输出。
②‖或运算符。‖运算的特征和&&相反,左侧的运算结果为false时,返回右侧内容。如下:

在该示例中,如果age的数值>=18不做任何输出,否则输出:

③三目运算。示例如下:

在示例中,如果age>=18则输出:

否则输出:

④在逻辑特别复杂的情况下,也可以借用函数。在函数里进行相关的处理,最后把处理结果返回即可。示例如下:


2.JSX属性书写
通过JSX已经可以正常去编写视图了,但编写视图时肯定还需要去添加一些相应的属性,如class、id、type等。但要注意JSX并不是真正的HTML,所以书写时还是有一些注意事项。
1)所有的属性名都使用驼峰命名法。
2)如果属性值是字符串并且是固定不变的,则可以直接写,如:

3)如果属性值是非字符串类型,或者是动态的,则必须用插值表单式。如:

4)有一些特殊的属性名并不能直接用,具体如下:
①class属性改为className。如:

②for属性改为htmlFor。
③colspan属性改为colSpan。
5)style在书写的时候要注意它接收的值是个对象,示例如下:

这里可以看到style的值是个插值,接收的是一个对象。除了单独声明变量外,也可以直接简写成下例所示:


这里是直接把对象写进了插值里。
3.JSX注意事项
前文中详细讲解了JSX的使用,不过真正使用JSX的时候,还需要注意它的一些问题,下面对JSX的使用问题进行一个汇总。
1)浏览器并不支持JSX,在使用时要使用babel编译。
2)JSX不要写成字符串,否则标签会被当作文本直接输出。
3)JSX是一个值,在输出时只能有一个顶层标签,示例如下:

该例子中,JSX输出了三个顶层标签header、div、footer,这样运行时就会报错。如果JSX的顶层标签不希望在DOM中被解析出来,则可以使用<React.Fragment></React.Fragment>作为顶层标签。Fragment是React提供的一个容器组件,它本身并不会在真实的DOM中渲染出来。利用Fragment对上述案例进行修改,具体代码如下:


最终渲染出来的真实DOM如下:

4)所有的标签名字都必须小写。
5)无论单标签还是双标签都必须闭合。
6)JSX并不是HTML,在书写时很多属性的写法不一样。
①属性名都必须遵循驼峰命名法,从第二个单词开始首字母大写。
②个别属性的属性名写法有变化,具体参考2.3.2节。
③style的值接收的是一个对象。
7)在JSX中,插入数据需要用插值表达式{数据}