Web 网页结构一般都比较复杂,但复杂的结构中又经常包含一些重复模式。我们可以对网页结构进行划分,从中抽象出一些组件,以此简化代码逻辑。
本节以一个简单的分类书单应用为例,讲解应用结构划分和 React 组件设计的基本思路。
HTML结构
我们要开发的分类书单应用按类别罗列书单,每本书只展示封面,点击即可跳至京东购买。就这么简单的应用,HTML 结构也层层嵌套,相当复杂了:
See the Pen for-var by fasionchan (@fasionchan) on CodePen.
如果是直接写 HTML 结构,想想都要发狂——因为有一堆重复的结构:
- 分类是一个 section 标签,因此每种类别都需要重复写一遍;
- 书是一个 li 标签,因此每本书都需要重复写一遍;
- 如果类别很多,每个类别下的书也多,那得写到怀疑人生;
数据驱动
我们在上节已经掌握了通过 React 虚拟 DOM 元素来组织网页的技巧,可以用 JS 根据数据生成虚拟 DOM 元素,事情得到极大简化:
See the Pen for-var by fasionchan (@fasionchan) on CodePen.
数据是一个书单分类数组,一般由后端接口返回。数组元素是一个代表图书分类对象,name 字段是分类名,books 字段是该类别下的图书列表:
|
|
图书列表也是一个数组,每个元素元素对象表示一本书,name 字段表示书名,image_url 字段表示图书封面图片 URL ,jingdong_url 表示京东购买链接:
|
|
利用数组 map 方法,我们可以根据数据生产任意多的虚拟 DOM 元素,以生成图书类别为例:
|
|
这段代码 map 方法会为 sections 数组每个元素生成一个 section 虚拟元素,section 包含一个 h1 虚拟元素来展示书名,还包含一个 ul 来展示图书列表。
注意到,我们传了一个 key 参数给 section 元素,这个参数在 React 中非常重要。React 渲染在列表元素时,依赖参数 key 对比新旧节点的差异,看哪些节点需要重新渲染。
渲染图书列表跟渲染类别类似,也利用了数组的 map 方法,为每本书生成 li 虚拟元素,li 元素内又嵌套着 a 和 img 元素。完整代码请查看 CODEPEN 中的 JS 部分。
不知您注意到没?这种代码设计方式虽然从一定程度上实现了代码复用,但还不够优雅:
- 代码嵌套过深,不利于阅读;
- 代码仍无法复用到其他地方,比如其他页面;
简而言之,这个版本的代码仍是简单堆砌在一起。我们可以将其中的部分逻辑抽象出来,组织成独立的函数。这样既能简化代码逻辑,又能提升可复用性。
结构分析
开始进行逻辑抽象之前,我们先来分析当前代码的结构。它负责生成分类书单的虚拟 DOM 元素,结构跟网页的结构高度相关,因此我们可以从分析网页结构入手:
如上图是分类书单的整体结构,红色方框代表一个类别,由 section 标签组成,包含类别名称和图书列表;蓝色方框为图书列表,由 ul 标签组成,内部包含一组 li 标签,每个代表一本图书;绿色方框代表一本书,由 li 标签组成,内部嵌套 a 标签实现点击跳转,又嵌套 img 标签展示图书封面。
函数是组织代码的最基本单位,因此我们可以将每种方框的生成逻辑,组织成一个函数。函数则根据参数提供的数据,创建并返回虚拟 DOM 元素。
这样一来,只要调用函数即可创建对应的虚拟 DOM 结构,逻辑更为清晰,也更好复用。
React组件
像函数这样根据参数完成虚拟 DOM 元素创建的代码实体,在 React 中称为 组件( component )。在 React 中,有两种不同的组件,一种是 函数组件 ,一种是 类组件 。
我们先来看函数组件,它本质上只是一个满足约定接口的常规函数而已:
|
|
组件函数接收一个参数 props ,代表传给组件的参数对象;然后返回创建好的 React 虚拟 DOM 节点。我们采用自底向上的思路,先编写展示单本书的组件练练手:
|
|
参数对象 props 向组件传递两个参数:book 是一个对象,传递待展示的书本信息;imgStyle 也是一个对象,传递封面图片的自定义样式。
书本只展示封面图片,需要创建 img 节点。为实现点击跳转,还需要在上面套一个 a 节点。因此,组件函数创建并返回 a 节点,参数指定 href 属性跳转到京东。a 节点嵌套 img 节点,参数 src 指定封面图片地址,参数 style 指定图片样式。
注意到,组件为 img 图片提供了默认样式,但用户可以传 imgStyle 参数对默认样式进行覆盖。这也是设计组件的最佳实践——既保证易用性,又不失灵活度。
React.createElement 创建 React 虚拟 DOM 元素时,还支持使用 React 组件:
|
|
这段代码创建虚拟 DOM 元素,渲染 Book 组件,最终展示图书《JavaScript权威指南》。注意到,书本信息通过 book 参数传给 Book 组件,imgStyle 不传则采用默认样式。
React 将虚拟 DOM 元素渲染成浏览器 DOM 元素后,结果大概是这样的:
|
|
接下来,我们可以在 Book 组件的基础上,编写用于展示图书列表的 BookList 组件:
|
|
BookList 组件接收两个参数,books 数组传递图书列表,style 对象传递自定义样式。图书列表组织成 ul 和 li 结构,因此我们调用 books 数组 map 方法,为每本书生成 li 列表节点,li 节点则包含 Book 组件。
将简单组件稍加组合,即可构造更复杂的组件,像搭积木一般!
看到这里,相信大家对 React 组件应该有了初步认识,可以自己试着开发图书类别组件练练手(红框部分)。温馨提示,类别数据可通过 section 参数传递,接口如下:
|
|
最后,创建一个 div 虚拟 DOM 节点,内部包含根据数据生成的多个 BookSection 节点:
|
|
虚拟 DOM 节点渲染到浏览器后,效果跟之前也是一样的,但代码要清晰很多:
See the Pen for-var by fasionchan (@fasionchan) on CodePen.
完整源码
|
|
【小菜学React】系列文章首发于公众号【小菜学编程】,敬请关注: