React 的文档里,在介绍生命周期的时候,提供了一个生命周期的插图:
React Lifecycle Methods diagramprojects.wojtekmaj.pl第一眼看到这个插图,就觉得传统布局不太好实现这个结构,有可能是设定了 position: absolute 之后再调整每一个块的位置。但是打开开发者工具,发现整个布局都是使用 grid 布局实现的,正好还不太懂 grid 布局的具体使用,所以决定模仿它自己实现一遍。最终的效果先不放在这,反正是模仿,应该和上面那张图长得差不多。
grid 布局的基本概念可以在 网格布局的基本概念 学习,最好有点基本了解,本文主要描述实现的流程,对概念的解释有点意识流,应该大家都能看懂,但是细节还是得查资料。
打开 React 插图网页,再打开 Chrome 开发者工具,把鼠标挪到插图上,可以看到
这些密密麻麻的蓝色块就是网格布局里面的一个块,但是一下子搞这么多东西太复杂了,我们先来画“创建时”、“更新时”、“卸载时”的后面三个块吧:
当然,图片里所有的元素都被定义在网格里,所以我们需要先定义一个网格容器。参数什么的我就完全复制 React 插图页面 CSS 里的值了(想改可以改,这里就是为了模仿的像一点)。
HTML
<!DOCTYPE html>
CSS
.
grid-template-columns & grid-gap
这里面有两个概念需要解释,grid-gap 和 grid-template-columns。
先看 grid-template-columns,一共定义了六列,每一列的宽度分别为 minmax(86px,auto),minmax(144px,auto),minmax(75px,auto),minmax(52px,auto),minmax(75px,auto),minmax(148px,auto)。(minmax 的含义就是这列宽度最小是第一个参数,最大是第二个参数)
grid-gap 的意思就是我们定义了列,那么列之间的距离应该是 20px。当然,grid-gap 同时定义了行和列的 gap,可以看下面的图来理解。
然后我们现在光定义了一个容器是 display: grid,定义了每一列有多宽,还定义了每行和每列之间应该间距 20px。那么我们怎么在里面画出一个元素呢。
我们在 div.diagram-container 里面放三个元素。
<
然后给这三个 section 设定一下背景颜色。
.
发现这三个块就画出来了。
grid-area
决定这三个块位置的是他们的 grid-area,grid-area 后面的意思分别是 行起始线编号 / 列起始线编号 / 行终止线编号 / 列终止线编号。我们之前定义了 6 列,就会产生 7 条列的分割线,分别是 1、2、...、7,如下图所示。由于行我们之前没定义,所以我们用到哪,浏览器就会画到哪。
比如第一个灰色的部分,grid-area: 1 / 2 / span 23 / span 1,就是从第 1 条行线开始,到第 1 + span 23 = 24 条行线截止,从第 2 条列线开始,到第 2 + span 1 = 3 条列线结束。span 这种写法等价于 grid-area: 1 / 2 / 24 / 3。
那么对与第二个灰色的部分,从第 1 条行线开始,第 3 条列线开始,第 24 条行线结束,第 6 条列线结束,它的 grid-area 就是 1 / 3 / span 23 / span 3。第三个灰色的部分其实同理。
然后我们来画一个 getDerivedStateFromProps 块吧,先观察一下它的位置。
行从 6 到 8,列从 2 到 6,写出来就应该是 grid-area: 6 / 2 / span 2 / span 4(也就是 6 / 2 / 8 / 6)。
所以我们在 HTML 中补上这个 getDerivedStateFromProps 块。
<
因为 React 的页面用了 ul 和 li,所以我也就这么写了。需要注意的是,需要把 ul 的 display 设为 contents。
display: contents
只简单介绍下它的效果,详细可以参考
<display-box>developer.mozilla.orgdisplay: contents 可以理解为把该元素的所有子元素都在 DOM 树中向上提一层,然后不显示该元素。以上面为例,给 ul 设定了 display: contents 之后,展示的效果就和下面一样了(li 默认的样式还会在,树的结构实际并没有改变,只是显示改变了)。
<
然后再给 getDerivedStateFromProps 这个块设定点样式,就能有个样子了。
.
剩下所有的生命周期方法都能用同样的方式画出来。
接下来考虑一下箭头怎么画。首先箭头有两个部分,一个是线,另一个是箭头。我们可以先产生一个块,线的话可以画成这个块的 border,箭头可以画成这个块的 ::after,然后用绝对布局调整一下箭头的位置。那么先考虑下图这个红色箭头(constructor 下面的)。
实现方式就是设定一个从行线 5 到行线 6,列线 2 到列线 3 的块,然后把它的 width 设成 0,同时加上左边框,然后用 relative 将它右移 50%。
(虽然这么做是 work 的,但我也没有搞明白,为什么 relative 50% 可以成功。relative 中使用百分比一般都是相对于父元素,按理来说,对于 grid 里面的一个 item,百分比相对的是 container 的大小,但实际的效果却是相对于 grid-area 所声明区域的大小...有点懒得翻标准了。)
所以这个 arrow 的 grid-area 就是 5 / 2 / 6 / 3。
这么写还不够,因为行线 5 到行线 6 围出来的块高度是 0,所以箭头的高度是 0,还需要将它的 margin-top 以及 margin-bottom 设成 -20px,让它的高度在上下两方延长(这也是个奇怪的 trick),填满上下各 20px 的 grid-gap。
所以写出来 arrow 的样式。
.
画箭头三角的过程用到了 clip-path,不了解的同学可以参考:
clip-pathdeveloper.mozilla.org然后再把箭头放到 HTML 中去。
<
然后我们就有个箭头了。
会画箭头和块之后就好办多了...剩下的基本都是重复的体力劳动,照猫画虎,或者想自己发挥着调也是可以的。还有很多很多细节比如边框、hover、字体大小、箭头旋转的调节,具体对照 React 原页面做就好了,说的很详细会很啰嗦。
在一步一步把每一个箭头和块画出来,还有调整它们的过程中,对 grid 布局的理解会深入一点(想会用总得写几遍吧)。
放一张最终的效果图,还有一些没有说的实现细节:
虽然不是像素级别的复制,但是大概样子还是差不多的。
详细代码我放在了
07akioni/mock-react-lifecycle-methods-diagramgithub.com只是这两天的一个练手,欢迎指正和讨论。