HTML+CSS宝典 CSS基础 定位
视觉格式化模型大体将页面中盒子的排列分为三种方式
1. 常规流
2. 浮动:浮动涉及到的是 float 属性
3. 定位:定位涉及到的 css 属性是 postiton(位置的意思)
视觉格式化模型里面有三个,常规流、浮动都学习了,最后就剩下定位了
什么是定位呢?
定位:手动控制元素在包含块中的精准位置。
以前元素在包含块里面的位置还要受到其它元素的影响,在定位里面就不是了,定位是手动控制元素的精准位置
一、postiton属性
postiton 有那些取值?
静态定位 position: static 默认值 static 叫静态定位,可以认为就是不进行定位,之前该是常规流的还常规流,该是浮动的还是浮动
相对定位 position: relative
绝对定位 position: absolute
固定定位 position: fixed
只要一个元素的 position 取值不是 static(静态定位),就认为该元素是一个定位的元素
1、定位元素的特点
定位元素会脱离文档流( relaitve 相对定位除外 )
这跟浮动是一样的,浮动元素也会脱离文档流(脱离常规流),定位也会脱离文档流(脱离常规流),
但有一个特殊情况相对定位( relative )除外,相对定位不会脱离文档流。
相对定位 relative 元素是不会脱离文档流的,元素之前是什么还是什么,这块比较特殊一会详细探讨这个。
2、一个脱离文档流的元素,有这样一些特点
1. 文档流中的元素摆放位置的时候,会忽略脱离了文档流的元素。
意思可以想象成浮动那样,常规流在摆放的时候会忽略浮动元素,就跟浮动元素不存在一样。
定位这里也是一样,如果一个元素脱离的文档流,
比如使用了绝对定位 absolute 或者使用了固定定位 fixed,相对定位 relative 除外,相对定位没有脱离文档流,
如果使用了固定定位 absolute 或绝对定位 fixed,会导致元素脱离文档流,在正常常规流下摆放元素的时候,就不会看这些脱离文档流的元素,跟浮动哪块是一样的。
2. 常规流中,元素计算自动高度时,会忽略脱离了文档流的元素。
跟浮动是一样的,浮动会导致高度坍塌,因为正常的高度计算的时候,会忽略脱离了文档流的浮动元素,所以会造成高度坍塌。
那么定位也是一样,只要设置为 absolute、fixed 这两个定位,就会脱离文档流,
脱离文档流容器元素在计算高度的时候,会忽略里面的这两种定位元素。
上面大概了解一下 position 的几个属性值,然后下面一个一个详细的学习,首先了解一下相对定位。
二、相对定位
相对定位比较特殊,因为不会导致元素脱离文档流,只是让元素在原来位置上进行偏移。
就是说元素之前是什么样子还是什么样子,
如果一个元素之前是浮动,设置了相对定位后,它还是浮动,
之前是常规流,设置了相对定位后,它还是常规流,
相对定位 relative 不会对元素是否脱离文档流产生任何影响,之前是什么还是什么,只是在原来的位置上进行偏移。
示例,
容器里面有三个 div 元素,现在改变第二个 div,给他设置一些样式
1. 首先设置 position: relative ,设置过后这个 div 元素就是一个相对定位的元素
2. 但是另外两个 div 元素没有受到影响,而且第二个 div 本身没有发生任何变化
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <style> .item{ height: 50px; line-height:50px; background-color: red; border: 2px solid #000; color: #fff; } .container{ width:300px; } </style> <title>相对定位</title> </head> <body> <div class="container"> <div class="item"></div> <div class="item" style="position:relative;">相对定位的元素</div> <div class="item"></div> </div> </body> </html>
第二个 div 本身没有发生任何变化
相对定位的元素
那么设置这个 position: relative 属性有什么意义呢?
意义在于第二个 div,从此以后就可以进行偏移了,现在可以让第二个 div 在原来的位置上进行偏移
怎么偏移呢?
可以通过这个四个 css 属性设置位置
left 离左边的偏移量
right 离右边的偏移量
top 离上边的偏移量
bottom 离下边的偏移量
设置第二个 div 元素相对定位左偏移 left: 90px;
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <style> .item{ height: 50px; line-height:50px; background-color: red; border: 2px solid #000; color: #fff; } .container{ width:300px; } </style> <title>相对定位</title> </head> <body> <div class="container"> <div class="item"></div> <div class="item" style="position:relative; left:90px;">相对定位的元素,left偏移90像素</div> <div class="item"></div> </div> </body> </html>
第二个 div 完成了 left 偏移 90 像素
相对定位的元素,left偏移90像素
left 偏移和 margin 有什么区别呢?
1. 第二个 div 用的是 left 偏移 left: 90px;
2. 然后给第一个 div 设置 margin-left: 90px;
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <style> .item{ height: 50px; line-height:50px; background-color: red; border: 2px solid #000; color: #fff; } .container{ width:300px; } </style> <title>相对定位</title> </head> <body> <div class="container"> <div class="item" style="margin-left:90px;">margin-left: 90px</div> <div class="item" style="position:relative; left:90px;">相对定位的元素,left偏移90像素</div> <div class="item"></div> </div> </body> </html>
看到区别了了吗?
margin-left: 90px相对定位的元素,left偏移90像素
1. 第一个div用 margin-left: 90px ,因为margin-left也是盒子的一部分,常规流保证盒子要撑满包含块,所以第一个div的宽度会减小
2. 而 left: 90px 这个值只是偏移,其它地方全部不变,所以不会导致div宽度的减小,div盒子是距他原来左边的偏移量90像素,意思是这个盒子的左边,离他原来左边的有90个像素的偏移量
再比如,
设置第二个div盒子 right: 30px; ,意思这个盒子右边,离他原来右边有30个像素
相对定位的元素,right:30px;
同样设置 top: 30px; 离原来上边有30像素的距离
相对定位的元素,top:30px;
再设置 bottom: 30px 盒子往上移动了,意思是盒子的下边,离他原来下边的距离
相对定位的元素,bottom:30px;
到这里我们发现,盒子的偏移不会影响到其它盒子,
这个很重要,相对定位下盒子的偏移不会对其它盒子造成任何影响。
偏移可以理解为,只是在视觉效果上产生的偏差,实际上盒子以前在那里,他现在还在那里。
现在有四个属性,
如果同时设置 left: 50px、right: 50px;
或者同时设置 top: 30px、bottom: 30px;
平时设置的时候,最好避免设置同时有冲突的值,这种情况冲逻辑上说不通,是有矛盾的
当出现矛盾的时候,可以这样简单理解,
水平出现矛盾的时候,以左边left为准,
上下出现矛盾的时候,以上边top为准。
三、绝对定位
1、宽高为auto时候,尺寸适应内容。
在绝对定位下 postiton: absolute ,
手动设置宽、高的时候, 按照我们设置的尺寸,
如果宽、高值为auto(自动)尺寸适应内容,跟浮动是一样的。
2、包含块的变化,如果一个元素是absolute绝对定位。找祖先中第一个定位元素,该元素的填充盒为其包含块。若找不到,则他的包含块为整个网页(初始化包含块)。
什么是包含块?
包含块是盒子的参考坐标系,他决定了盒子的活动范围,
在绝对定位的时候,盒子的包含块变了,这块知识是非常非常重要的,如果没有理解清楚包含块在哪,位置是定不准的。
那绝对定位下盒子的包含块在哪呢?如果一个元素是absolute绝对定位,他包含块的变化
1. 找祖先中第一个定位元素,该元素的填充盒为其包含块
2. 若找不到,则他的包含块为整个网页(初始化包含块)
1、找祖先中第一个定位元素
1. 找祖先元素,
从父元素开始找,先找父元素,然后再找父元素的父元素,再找父元素的父元素的父元素,依次往上找
2. 还要知道什么是定位元素,定位元素上面说了,只要 position 的取值不是 static,那么这个元素就是定位元素,
relative相对定位是定位元素,absolute绝对定位是定位元素,fixed固定定位是定位元素
如果一个元素是固定定位,他的包含块要找祖先元素中,第一个定位元素,祖先元素的填充盒 (padding box)为其包含块
绝对定位元素的包含块
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>绝对定位的包含块</title> </head> <body> <div style="width:600px;height:300px;padding:30px;border:2px solid;"> <div style="width:400px;height:200px;padding:30px;border:2px solid;"> <div style="width:100px;height:100px;background-color:red;"></div> </div> </div> </body> </html>
没有定位时候,页面效果
下面把最里面的第三个div,设置成绝对定位 position: absolute;
<div style="width:600px;height:300px;padding:30px;border:2px solid;"> <div style="width:400px;height:200px;padding:30px;border:2px solid;"> <div style="width:100px;height:100px;background-color:red;position:absolute;"></div> </div> </div>
会发现,现在第三个 div 的位置没有什么变化
我们通过 left、right、top、bottom,这四个属性来设置第三个 div 的位置,
left 表示这个绝对定位的元素,他的左边 离 包含块左边的距离,那这个元素的包含块在哪呢?
包含块是这样找的
1. 看一下这个元素的父元素(中间的div)是不是定位元素,不是定位元素
2. 然后在看他父元素的父元素(最外面的div)是不是定位元素,也不是定位元素
3. 再看最外面 div 的父元素 body 元素,也不是定位元素
4. 再看 body 的父元素 html 元素,也不是定位元素,都不是定位元素的,下面一会在说
如果这个定位元素的爷爷(就是最外面的div)设置成一个定位元素
<div style="width:600px;height:300px;padding:30px;border:2px solid;position:relative;"> <div style="width:400px;height:200px;padding:30px;border:2px solid;"> <div style="width:100px;height:100px;background-color:red;position:absolute;left:0;"></div> </div> </div>
1. 把最外面的 div 元素设置为 position: relative; ,
设置为 relative 的好处是,不影响他原来的位置,最外面的 div 元素原来的位置不会受到任何影响,
而且他原来是常规流现在还是常规流,原来是浮动现在还是浮动,他不会脱离文档流,但是他变成了一个定位元素了
2. 接下来给最里面绝对定位的 div 元素设置 left: 0 ,这个盒子的左边缘会靠 包含块 的左边缘
看一下他的包含块在哪?
他的包含块不在是他父元素的内容盒了,他的包含块是他第一个祖先定位元素(最外面绝对定位div元素)的 padding-box 盒(也就是填充盒)
再捋一下,最里面红色绝对定位div元素是怎么找他的包含块
1. 首先看他的父元素,父元素不是定位元素,不是忽略
2. 继续往上看,父元素的父元素是一个定位元素 position:relative,找到了第一个定位元素
3. 这个祖先元素就是第一个定位元素,这个定位元素的的填充盒(包含 padding)就是包含块
下面用浅黄色背景画出来的就是包含块,调整的位置都是相对这浅黄色区域来调整的
我们测试一下,
这个浅黄色区域是不是包含块,设置最里面红色的div left: 0; top: 0;
再设置最里面红色的div right: 0; bottom: 0;
红色方块 div 的活动范围是在这个浅黄色背景的坐标里面,所以说学习定位,特别是绝对定位,关键点在于找到他包含块在哪
绝对定位下可以通过这四个属性设置位置
left 表示距包含块,左边的距离
top 表示距包含块,上边的距离
right 表示距包含块,右边的距离
bottom 表示距包含块,下边的距离
找到包含块就可以轻松的搞定位置在哪,这是绝对定位的包含块
上面是给里面绝对定位的祖先元素(最外面的div元素)设置的 position: relative ,下面把这个属性去掉,
去掉之后,最里面绝对定位元素(红色div)的包含块在哪呢?
2、若找不到,则他的包含块为整个网页(初始化包含块)
这个是特殊情况,绝对定位元素如果找不到祖先元素是定位元素,他的包含块为整个网页,
整个网页通常我们称之为初始化包含块,也就是整个网页的区域是绝对定位的包含块。
以整个网页为包含块,比如设置 right: 0; bottom: 0;
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>绝对定位的包含块</title> </head> <body> <div style="width:600px;height:300px;padding:30px;border:2px solid;"> <div style="width:400px;height:200px;padding:30px;border:2px solid;"> <div style="width:100px;height:100px;background-color:red;position:absolute; right:0; bottom:0;"></div> </div> </div> </body> </html>
3、绝对定位可以做那些事情?
比如猫眼电影电影列表图片上的小图标
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>猫眼电影</title> <style> *{ margin: 0; padding: 0; } .clearfix::after{ content:''; clear: both; display: block; } .panel-content{ width: 750px; margin: 20px auto; /* background-color: palegoldenrod; */ } .panel-content .item{ width: 160px; height: 260px; border: 1px solid #efefef; float: left; margin-right: 32px; margin-bottom: 30px; position: relative; } .panel-content .item:nth-child(4n){ margin-right: 0; } .panel-content .item img{ width: 160px; } .panel-content .item span{ width: 69px; height: 25px; background-color: orange; color: #fff; font-size: 12px; text-align: center; line-height: 25px; font-style: italic; position: absolute; top: 10px; left: -2px; } </style> </head> <body> <div class="panel-content clearfix"> <div><span>2DIMAX</span></div> <div><span>3DIMAX</span></div> <div><span>3D</span></div> <div><span>2DIMAX</span></div> <div><span>2DIMAX</span></div> <div><span>3D</span></div> <div><span>2DIMAX</span></div> <div><span>2DIMAX</span></div> </div> </body> </html>
relative 相对定位的主要作用,就是给 absolute 绝对定位定义包含块的
四、固定定位
position: fixed 取值为 fixed,表示固定定位
固定定位其它情况和绝对定位完全一样,就是包含块一个地方不同
所以以后面试时候,经常会问这样的问题,
绝对定位 和 固定定位的区别?
专业的回答是包含块不一样(只需要回答一句话),
如果问怎么不一样,再回答绝对定位的包含块是怎么找的,固定定位的包含块是怎么找的
包含块不同:固定定位的包含块为视口(什么是视口?浏览器的可视窗口就是视口)
浏览器的可视窗口 跟 绝对定位 初始化包含块 的区别是什么?
1、网页的初始化包含块是什么呢?
可以认为初始化包含块是这个 html 元素的宽高,
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>初始化包含块</title> </head> <body> <div style="height:1000px;background-color:palegoldenrod;"></div> </body> </html>
选中 html 元素宽高是 830 x 1016,初始化包含块就是整个网页的区域
2、视口是什么呢?
视口是我们能看得见的区域( 也叫可视窗口 ),红色视口区域 616 x 231
所以 初始化包含块 有的时候(注意是有的时候) 比视口要小,
比如网页内容很少的时候,初始化包含块 比视口要小,
有的时候初始化包含块 比 视口要高,他们两个肯定是有区别的。
初始化包含块表示整个网页,视口表示可以看到的一小部分区域,
当然如果初始化包含块(整个网页)尺寸,大于视口浏览器就会出现混动条( 滚动条在视口里切换不同的显示部分 )。
固定定位的元素的包含块直接是视口,可以做出什么效果呢?比如,红色方块的位置是相对于视口的
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>固定定位</title> </head> <body> <div style="height:1500px; background-color: palegoldenrod;"></div> <div style="width:100px;height:100px;text-align:center;line-height:100px;color:#fff; background:red; position:fixed; left:0; top:0;"> 红色元素(固定定位) </div> </body> </html>
滚动滚动条的时候,视口没有发生变化,所以固定定位是相对于视口的
网页上的广告、回到顶部,这些都可以使用固定定位
无论固定定位这个元素写在哪里,保存到什么位置都无所谓,
只要这个元素是固定定位,他的包含块一定是视口,跟父元素没有任何关系,无论嵌套多少深,写到那个位置都无所谓,一定是视口作为包含块。
这就是固定定位 跟 绝对定位的最本质的区别,其它没有任何区别都一样。
Ps:
相对定位 通常为 绝对定位做包含块
五、定位下的居中
相对定位就不说了,相对定位不会脱离常规流,之前是浮动还是浮动,之前是常规流还是常规流,所以没有什么变化,
主要看绝对定位 和 固定定位,怎么在包含块里面居中。
元素在某个方向(水平、垂直)上居中
1. 水平方向上固定宽度,垂直方向上固定高度
2. 将左右距离设置为 0 或者 上下距离设置为 0(或固定像素)
3. 将左右的 margin 设置为 auto 或者 将上下的 margin 设置为 auto
比如设置 .ad 元素是固定位置的元素,设置
left: 0;
top: 0;
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>固定定位 在视口里面居中</title> <style> .ad{ width:100px; height:100px; background:red; position:fixed; left:0; top:0; } </style> </head> <body> <div style="height:1500px;background-color:palegoldenrod;"></div> <div class="ad"></div> </body> </html>
位置在视口的左上角
左边设置为0、右边设置为0,又要靠左右要靠右,矛盾了怎么办?
left: 0;
top: 0;
right: 0;
.ad{ width:100px; height:100px; background:red; position:fixed; left:0; right: 0; top:0; }
1. 上面说过,当左、右方向上有矛盾的时候,以左边为准,直接忽略掉右边
2. 他就忽略掉了右边,只保留了左边
如果不给设置右边,右边会怎么样?
.ad{ width:100px; height:100px; background:red; position:fixed; left:0; /* right: 0; */ top:0; }
1. 右边会自动进行计算,计算出来是 662
2. 下面没有设置,也会自动进行计算,计算结果是 131
就是如果不设置 right,右边的 right 值是 auto, right: atuo 会自行计算(剩余空间)
然后接下来,
把右边设置为 0 之后 rigth: 0 ,我们给它一个出路,什么出路呢?
把 margin 设置为 auto margin: auto ,
margin设置为auto的意思是, 在绝对定位 和 固定定中,margin为auto时,会自动吸收剩余空间(这一点跟常规流很像)
固定定位在视口里面居中
1. 宽高设置固定的尺寸
2. 上、下、左、右四个方向都设置为0
3. 这个时候 margin 设置为auto margin: auto; ,这些剩余空间只能去吸收掉
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>固定定位在视口里面居中</title> <style> .ad{ width:100px; height:100px; background:red; position:fixed; left:0; right: 0; top:0; bottom:0; margin: auto; } </style> </head> <body> <div style="height:1500px;background-color: palegoldenrod;"></div> <div class="ad"></div> </body> </html>
红色 div 在可视区窗口正中央,有些弹出广告就是这样做的
只看左右,把 bottom: 0 注销掉
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>固定定位在视口里面居中</title> <style> .ad{ width:100px; height:100px; background:red; position:fixed; left:0; right: 0; top:0; /* bottom:0; */ margin: auto; } </style> </head> <body> <div style="height:1500px;background-color: palegoldenrod;"></div> <div class="ad"></div> </body> </html>
1. 盒子固定了宽高
2. 宽度是固定的,只能靠 margin了,margin 也是盒子的一部分,靠外边距延伸出去,把两边靠拢
3. 所以把 margin 设置为 auto 后,有这些剩余空间,他会把剩余空间吸收掉
跟常规流不太一样的地方是:
常规流只能水平方向居中,
在固定定位 和 绝对定位中,上下居中也可以居中
如果 right 没有设置为 0
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>固定定位 在视口里面居中</title> <style> .ad{ width:100px; height:100px; background:red; position:fixed; left:0; /* right: 0; */ top:0; bottom:0; margin:auto; } </style> </head> <body> <div style="height:1500px;background-color: palegoldenrod;"></div> <div class="ad"></div> </body> </html>
效果又不一样
1. 因为 right 默认就为 auto
2. right 自动计算剩余空间,并吸收完了
在固定定位或者绝对定位中,要想居中在包含块里面居中,
1. 固定宽高,
2. 然后上下左右距离设置为0,
3. magin设置为auto
六、多个定位元素重叠时,谁在前谁在后?
这里涉及到一个概念叫堆叠上下文,但是这个概念的细节太多太多非常的复杂,我们平时开发也不太会用到这个概念,这里用最简单的一个方式。
z-index
电脑屏幕有,
x 是横向坐标
y 是纵向坐标
z 坐标是垂直穿过屏幕的轴,z 轴就会影响到谁在前谁在后的问题
通常情况下 z-index 的值越大,越靠近用户
示例,奥运五环
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>奥运五环</title> <style> .item{ position: absolute; width:70px;height:70px; border: 5px solid; box-sizing: border-box; border-radius: 50%; } .circle1{ left:0; top:0; border-color: blue; z-index: 9; } .circle2{ left:70px; top:0; border-color: #000; z-index: 3; } .circle3{ left:140px; top:0; border-color: red; z-index: 8; } .circle4{ left:35px; top:40px; border-color: yellow; z-index: 7; } .circle5{ left:105px; top:40px; border-color: green; z-index: 5; } </style> </head> <body> <!-- div.item.circle$*5 --> <div class="item circle1"></div> <div class="item circle2"></div> <div class="item circle3"></div> <div class="item circle4"></div> <div class="item circle5"></div> </body> </html>
重叠的元素通常情况下后边的元素在前,当然这是可以设置的( z-index 默认值是 auto,auto 的计算涉及到堆叠上下文,太复杂了 )
1. 设置 z-index,通常情况下 z-index: 99 值越大,越靠近用户
2. 关于 z-index 属性有一个前提条件,只有定位元素设置 z-index 有效,常规流、浮动元素设置没有效果
3. z-index 可以是负数,如果是负数,遇到常规流和浮动元素,则会被其覆盖
常规流元素和浮动元素,会覆盖 z-index: -1 为负数的元素,
比如,文字是行盒是常规流,它排列的时候,会完全忽略掉绝对定位、固定定位这些元素
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>z-index可以是负数</title> <style> .container{ width:200px; height:200px; font-size:42px; text-align:center; line-height:200px; position: relative; } .circle{ display:block; width:200px; height:200px; border-radius: 50%; background:#e0c6ad; position: absolute; left:0; top:0; } </style> </head> <body> <div class="container"> CIRCLE <span class="circle"></span> </div> </body> </html>
和浮动还不一样,浮动排列的时候,行盒还会避开浮动元素。但是绝对定位是完全看不到,所以会忽略掉
定位元素默认情况下会覆盖常规流,但是只要设置 z-index: -1 定位元素跑到文字下面去了
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>z-index可以是负数</title> <style> .container{ width:200px; height:200px; font-size:42px; text-align:center; line-height:200px; position: relative; } .circle{ display:block; width:200px; height:200px; border-radius: 50%; background:#e0c6ad; position: absolute; left:0; top:0; z-index:-1; } </style> </head> <body> <div class="container"> CIRCLE <span class="circle"></span> </div> </body> </html>
文字显示出来了
CIRCLE
七、补充一定知识,前面没讲到
1、绝对定位 和 固定定位,这两种定位元素他一定是块盒
span 元素是行盒,设置为相对定位 position: relative;
<span style="position:relative;"></span>
看一下 dispaly 属性还是 inline
relative 虽然是定位元素,但不会改变盒子的特征,之前是常规流还是常规流,之前是行盒还是行盒
如果 span 元素设置 position 属性值为 absolute 或者是 fixed
<span style="position:absolute;"></span>
再看display属性变成了block 块盒
变成了绝对定位,他一定是块盒
2、绝对定位、固定定位元素一定不是浮动
页面上排列盒子只有三种方式,常规流、浮动、绝对定位,
不可能既是常规流、又是绝对定位、又是固定定位
比如,给 span 元素加浮动 和 绝对定位
<span style="float:left; position:absolute;"></span>
查看 float 属性强行变成 none,不可能浮动是的元素
如果这样一道面试题问:
span 是浮动元素,还是一个绝对定位元素?
一定是绝对定位元素,浮动值被强制改为 none
3、没有外边距合并
总结:
绝对定位 和 固定定位都为了精准的放置元素,只不过他们的包含块不一样的,
固定定位的包含块是视口,
绝对定位的包含块是依次往上找,找到祖先元素中第一个定位元素,祖先元素中第一个定位元素的填充盒是包含块
Ps:
sticky 粘性定位
https://blog.csdn.net/weixin_51081257/article/details/121305642