农村的师傅的博客

一个迫于生计,无法放飞自我,导致喜欢上了前端开发,并即将成长为强者(指头发)的程序猿。

0%

css布局

仍然是最近几个月闲来无事,想着自从毕业那几年之外,已经好久没有沉下心来好好看看css了,趁着年底的这几个月,我又重新找了本《深入解析CSS》来回顾了一下CSS的一些知识点,并且在阅读的过程中,收集了一下css中常见的布局需求以及布局方式,作为练习的同时,也方便后续可能的回顾。

布局模式

  • 通常,最简单的方式是先将网页的大块区域布局好,再逐级布局内部的小元素。
  • 整体拆分页面的大的骨架,然后为其整体选择布局方式,然后再细化各部分的内部元素的布局

web前端中,常见的页面布局场景有哪些?(常见或者经典的)

相关布局代码参考github,相关布局名称和文件一一对应(如果有的话):github

布局1:水平居中布局(单行 / 多行、定宽 / 不定宽)

  • 水平居中:一条横线的中间,即一个元素距离左右边距的大小是一样的,则该元素是水平居中的。
  • 实现要求:① 行内元素水平居中 ② 块级元素(定宽)水平居中 ③ 块级元素(不定宽)水平居中 ④ 多行文本水平居中
  • 核心考点:text-align:center、margin: 0 auto、inline-block 的特性

实现

  • 如果是行内元素,基本上是指文本或者行内块
    • 直接使用text-align:center
    • 如果是多行,则通常应该是固定宽度,设置为display: inline-block;
  • 块级元素(无论是否固定,都是可以的)
    • margin: 0 auto; 最简单,这个也叫双容器模式(double container pattern),即通过将内容放置到两个嵌套的容器中,然后给内层的容器设置外边距,让它在外层容器中居中
    • flex:麻烦一点点,使用主轴居中
    • absolute:不过有一个问题在于其内容会脱离文档流,导致容器元素高度无法自动撑开,需要一定的场景(例如固定宽高的元素)
    • gird:挺麻烦的,无论如何都感觉没有必要

布局2:垂直居中布局(单行 / 多行、定高 / 不定高)

  • 垂直居中:一条竖线的中间,即一个元素距离上下边距的大小是一样的,则该元素是垂直居中的。
  • 实现要求:① 单行文本垂直居中 ② 块级元素(定高)垂直居中 ③ 块级元素(不定高)垂直居中 ④ 未知宽高元素垂直居中
  • 核心考点:line-height 等于高度、定位 +translate(-50%,-50%)、Flex 的垂直居中逻辑
  • 注:由于高度默认是内容撑开的,所以,其这个场景下,基本上容器都有固定的高度,或者特定的外部高度。
  • vertical-align 属性通常用于调整行内元素或表格单元格内的元素在垂直方向上的对齐方式。直接使用它进行垂直居中对齐是不起作用的。

实现

  • 单行文本垂直居中
    • line-height设置为容器高度(line-height可以被继承):行高内的行内元素默认就是垂直居中的(文本就是行内元素,也是一种dom元素)
    • 给元素设置display: inline-block;那么该元素的就会因为是行内元素,在line-height默认垂直居中特性下,进行垂直居中。
  • 块级元素
    • flex:挺好
    • absolute + transform:万能
    • grid:通常不用

水平垂直居中

结合上面两点(尤其是垂直居中,通常高度都是固定的):

  • 行内元素:line-height + text-align
  • 块元素
    • 如果带有固定高度,可以考虑:absolute + transform
    • flex布局

布局3:左 - 右 两栏布局(左侧定宽,右侧自适应)

  • 实现要求:左侧宽度固定(如 200px),右侧宽度随屏幕自适应铺满剩余空间,高度自适应(左右两侧高度一致 / 不一致都兼容)
  • 核心考点:浮动的清除、overflow:hidden 触发 BFC、Flex 的弹性分配、calc() 计算宽度

实现

  • flex布局
  • grid布局
  • 使用inline-block布局,并且使用calc动态计算宽度
  • 使用float + BFC来防止浮动元素覆盖

布局 4:上 - 下 两栏布局(顶部定高,底部自适应)

  • 实现要求:顶部高度固定(如 60px),底部高度随内容自适应,页面滚动时顶部固定 / 不固定两种效果都要实现
  • 核心考点:position:fixed、box-sizing: border-box、min-height 防内容溢出

实现

  • 这个其实没啥好说的,如果是通用导航栏的话,使用fixed,并且padding-top顶开即可。看具体需求吧
  • 有时候min-height可以替代高度,来实现默认多高,如果超出,则根据内容自适应。
    • 即从内容高度和最小高度中选取最大的那个值作为高度。

布局 5:元素宽高比固定布局(等比容器)

实现要求:元素宽度自适应屏幕,高度根据宽度按「固定比例」变化(如 16:9、4:3、1:1),比如视频封面、图片容器、卡片等
核心考点:padding-top/padding-bottom 的百分比是相对于父元素宽度计算的(核心原理)

实现

  • padding-top设父容器元素的宽度,子容器使用绝对定位,设置top、left、bottom、right来让子容器填充为父元素的宽高。或者使用width/height
    • 父容器设置padding-top后,子容器由于是绝对定位,脱离文档流,此时height貌似是生效的。
  • 另外一个就是如果是基于视窗口的缩放,那么可以使用vw来设置宽度,然后高度由于也是基于vw,拥有一个统一的比例基数。所以height可以使用calc来设置和计算。

布局 6:浮动布局与清除浮动

怎么说呢,可以略过了。在现代 Web 开发中,float(浮动)布局已经不再是主流的页面布局方式,主要由更强大、更灵活的 Flexbox 和 CSS Grid 布局所取代;但 float 仍有其特定用途,比如图文混排(让图片围绕文字流动),以及在需要支持老旧浏览器(如 IE11)时的兼容性方案。

布局7:上 - 中 - 下 三栏布局(经典通栏布局,万能页面骨架)

  • 实现要求:页面分为「顶部 (header)+ 主体 (main)+ 底部 (footer)」三部分;顶部固定高度,底部固定高度,主体高度自适应,当主体内容不足时,底部固定在页面最底部;当内容超出时,底部被内容顶到页面下方。
  • 核心考点:粘性底部布局(核心需求)、min-height:100vh 的使用

实现

  • 使用flex布局,垂直方向为主轴,容器元素占满设置最小高度为100v
    • 然后上下两块固定高度,中间main使用flex:1
    • 由于高度height的特殊,其高度并没固定,当你中间的main内容超出时,容器元素会自行撑开,而不是使用flex-shrink收缩

布局 8:左 - 中 - 右 三栏布局(圣杯布局 / 双飞翼布局)

  • 实现要求:左侧定宽 + 右侧定宽 + 中间自适应,中间内容优先加载,三栏高度一致,自适应屏幕宽度,无横向滚动条
  • 核心考点:圣杯布局、双飞翼布局的实现原理、margin 负值的妙用、padding 的占位技巧、Flex 的简化实现
  • 业务场景:后台管理系统、电商商品详情页、博客内容页、文档类网站

实现

  • flex布局(推荐)
    • 左右固定宽度width,中间使用flex:1;属性
    • 天生高度相等
    • 使用order来调整弹性或网格容器中项的布局顺序(默认是0),以便中间的内容优先被解析
  • grid网格布局(main元素写在dom前面)
    • 比较简单,定义3列1行,将main显式固定到第二列中,同时指定grid-auto-flow: dense;来填充left到第一列中。
  • margin + 定位(绝对定位)
    • main使用marin设置两边边距为左右两边元素的宽度
    • 左右两边使用定位来放置到左右两边
    • 同时使用top和bottom来撑开左右容器的高度(定位元素的行为)
  • 浮动 + 负外边距
    • 三个区块都设置float:left,然后main占据100%
    • 此时left和right都会在最下面,不在一行
    • left设置margin-left: -100%;使其处于最左边(在同一行),这个100%是基于父元素的宽度的
    • right设置margin-left: -right元素宽度;此时right元素会在最右边
      • 这个负边距的原理,你可以理解为将宽减少一定的大小,使其可以放到一行中去。具体如果有需要可以再详细理解
    • 此时right和left会挡住main,给main设置一个左右的padding即可
      • 如果不想margin使用padding(因为此时背景会有问题),则需要给外容器设置padding,但是此时left和right都需要使用相对定位将其给偏移一下
    • 问题:高度一致貌似没有解决,有倒是有方法,不过非常的奇技淫巧,没有现实意义了。超大padding + 负margin抵消
  • 其他方案:calc()函数配合inline-block或者float浮动(没有啥用)
    • 但是高度一致怎么办?如何自适应?
    • 主要是这种布局方式的特性导致的问题,它的布局方式,都是孤立的,不像其他一样,拥有完善的布局规则(它们都有自己的格式化上下文,其元素带有自身的布局规则)

布局 8

布局 9:栅格布局(网格布局,24 栅格系统、网格系统)

  • 实现要求:实现「24 列栅格」,支持「1-24 列的宽度分配」,支持「列间距、行间距」,支持「响应式适配」(大屏多列,小屏少列),元素自动换行,无错位
  • 核心考点:Flex 弹性换行 flex-wrap: wrap、百分比宽度计算、响应式断点适配
  • 业务场景:电商商品列表、后台管理系统的卡片列表、首页模块布局、相册图片展示、表单布局
  • 核心说明:栅格布局是前端最常用的布局模式,ElementUI/AntD 等 UI 库的栅格系统都是基于这个原理实现的
  • 参考bootstrap的网格系统:bootstrap网格系统
    • 基本网格列(12、24)
    • 实现gap(gap-8或者gap-16)
      • 实现基于响应式设置不同的gap大小
    • 对齐网格
    • 换行
      • 行间隔
    • 实现offset偏移
    • 实现基于响应式应用不同的列栅格(6个尺寸)

实现

  • flex实现即可
    • 关于列(col-x)的flex是设置flex: 0 0 auto;还是flex: 0 1 auto;其实就是看你是否想要收缩
      • 看情况是否设置收缩为0,如果为0,则offset + col-x加起来超过100%时,为0则不会缩小元素,如果为1则会缩小
      • 例如收缩的话:offset = 8,col = 6,实际该元素会占据4,如果存在多个元素,那么会换行。
      • 还是设置不收缩吧(antd就不收缩)
  • 对齐的实现基本参照flex的对齐即可
  • offset使用margin来进行偏移即可(如果为元素设置margin为100(即offset-12),那么元素渲染会超出容器元素。
  • 换行使用flex的wrap即可
  • gap的实现
    • 水平的gap利用列元素的padding,容器行元素的负margin边距实现即可(参考下文)
    • 垂直的gap使用row-gap或者也是使用padding和负margin,不过可以使用@supports来优先利用row-gap,这也没有副作用
  • 其响应式设计(按照bootstrap)
    • 定义6个尺寸:xs、sm、md、lg、xl、xxl
    • 具有特定的列class,例如col-xs-xxx、col-sm-xxx、col-md-xxx、col-lg-xxx等
    • 其含义是:当页面的宽度符合xs、sm这些尺寸定义的宽度时,应用这些.col-xs-xxx或者.col-sm-xxx
    • 由于列是基于百分比定义,不同的尺寸下,宽度都设置为固定的列(12),且每一列都是固定的比例(100% / 12)
    • 例子:假设现在一个col列div中,设置了col-xs-12和col-md-3这两个类。其含义是:页面尺寸为xs时,该div占据12列。页面尺寸为md时,该div占据3列
      • 如果现在是xs的屏幕,那么会应用col-xs-12类,其div占据12列大小(col-md-3类失效)
      • 如果现在屏幕变成了md了,那么就会应用col-md-3这个类,其div就会占据3列大小(col-xs-12类失效)
    • 使用sass配合遍历map来减少代码编写

布局 9

栅格系统(网格系统)的列间距实现和对齐(水平和垂直的间距gap)

  • 栅格系统中的列间距如何简单实现的?且最后一个元素需要对齐?

  • 方案(最核心):使用margin的负值,可以使元素偏移或者两边都使用时,会拉伸元素,这个技巧在布局(例如列间隔gap)对齐时很有用

  • 实现:在栅格布局中,内部元素使用padding左右来实现gap间距

  • 此时一行的第一个和最后一个元素的无法对齐容器左右,

  • 所以给外层容器设置一个padding大小的负边距(左右都设置),来拉伸容器的宽度,使其内部第一个和最后一个元素对齐容器边界

  • 这个负边距是栅格系统对齐的关键,其除了内部元素和容器的边缘对齐之外,在实现offset偏移时,也是起到了非常重要的作用

  • 在实现多行的行距时,其设置上下的负边距,也可以帮你对齐元素。

  • 注意:要十分注意其中的问题,由于容器元素被拉伸了,所以,它会稍微超出容器元素的父元素的宽高,尤其是在间距比较大时的情况下,其元素的点击区域要注意。

  • 可以使用row-gap来直接设置多行的间距,如果不考虑兼容性的话

布局 10:流式布局(百分比布局,移动端基础)

  • 实现要求:页面所有元素的宽度都用「百分比」定义,高度自适应,元素随屏幕宽度等比缩放,无固定宽度,适配所有移动端设备
  • 核心考点:百分比的计算规则、max-width/min-width 限制最大 / 最小宽度、box-sizing: border-box 盒模型优化
  • 业务场景:移动端所有页面的基础布局、适配不同分辨率的手机、简单的 H5 活动页
  • 核心原则:宽度自适应,高度写死 / 自适应,禁止出现固定宽度,搭配 rem/vw 单位使用更佳

实现

  • 这和之前提到的视觉等比适配类似
  • 基于vw进行页面布局
  • 在实际开发中,通常基于设计稿使用px,然后在编译阶段,会通过插件将px转换为vw
    • 1px的边框、字体也可以转换,没有太大问题
  • 如果要为了防止在超大屏幕上无限拉伸元素,会给最外层添加一个最大宽度

布局 11:粘性布局(sticky 布局,吸顶 / 吸底 / 吸边,高频需求)

  • 实现要求:元素在页面滚动时,「滚动到指定位置后固定在屏幕上」,滚动离开后恢复原位;比如吸顶导航栏、侧边栏目录、商品筛选栏
    • 吸底:例如让底部导航栏一直固定在底部,其不需要给中间内容设置padding-bottom,底部导航栏不需要设置浮动定位,没有脱离文档流,滚动时有橡皮筋效果
  • 核心考点:CSS 原生属性 position: sticky 的使用(核心)、top/left/right/bottom 触发条件、z-index 层级控制
  • 业务场景:网站导航栏吸顶、电商商品筛选栏吸顶、博客侧边目录吸边、表格表头吸顶
  • 核心说明:sticky 是CSS3 新增属性,是定位的「第 4 种值」,比 fixed 更灵活,是实现吸顶效果的最优解,替代所有 JS 写的吸顶逻辑

实现

  • 粘性布局:它是基于滚动的
  • 吸底:底部导航栏一直固定在底部,内容不足时,也被撑满到底部,使用粘性定位:bottom:0;实现
    • 好处1:滚动到最底部时,底部导航栏没有脱离文档流,橡皮筋效果会一直随着底部导航栏滚动起来
      • 粘性定位特性,父元素和粘性元素底部重合时,其粘性失效
    • 好处2:主内容区域不需要设置padding-bottom来撑开

布局 12:弹性布局(Flex 经典布局,万能适配,开发首选)

  • 实现要求:掌握 Flex 的「主轴 / 侧轴」、「对齐方式」、「换行」、「弹性伸缩」、「占比分配」等所有核心属性,能实现任意的一维布局(横向 / 纵向)
  • 核心考点:display:flex、justify-content、align-items、flex-direction、flex-wrap、flex:1、flex-shrink、flex-grow
  • 业务场景:所有一维布局的场景都能用 Flex 实现,比如导航栏、列表、按钮组、卡片布局、表单元素排列、居中布局等
  • 核心地位:Flex 是前端开发的「万能布局」,是现阶段最主流的布局方式,90% 的布局都可以用 Flex 实现,必须熟练到极致

实现(参考下文的场景)

  • 一行内容中的元素垂直居中 + 某一块内容占据剩余宽度
  • 垂直方向多个元素水平居中
  • 固定列,多个相同元素自动换行(例如一列3个)

布局 12

布局 13:网格布局(Grid 布局,二维布局之王,进阶必备)

  • 实现要求:掌握 Grid 的「行列定义」、「单元格合并」、「间距设置」、「对齐方式」、「响应式适配」,能实现任意的二维布局(行 + 列)
  • 核心考点:display:grid、grid-template-columns、grid-template-rows、grid-gap、grid-column、grid-row、grid-area
  • 业务场景:复杂的表单布局、日历布局、商品详情页的多模块布局、九宫格、拼图布局、仪表盘布局
  • 核心地位:Grid 是CSS3 新增的二维布局,是 Flex 的「升级版」,Flex 擅长一维布局,Grid 擅长二维布局,两者互补,是前端进阶的核心技能

实现:根据具体场景自行实现即可。

布局 14:九宫格布局(等分布局,高频实战)

  • 实现要求:实现「3x3/4x4/5x5」的九宫格,单元格等宽等高,间距一致,自动换行,响应式适配(大屏多列,小屏少列),无错位
  • 核心考点:Flex 的弹性换行、Grid 的行列均分、百分比宽度计算
  • 业务场景:首页功能入口、相册图片展示、商品分类、小游戏布局

实现

  • 其每一格内容宽高等比缩放
  • grid实现
  • flex实现
    • 还是边距实现的问题,如果考虑直接使用gap + calc函数,则实现会简单不少(不过gap在flex生效还是太少了,那不如直接grid实现)

布局 15:全屏布局(满屏自适应,后台管理系统专属,必考)

  • 实现要求:页面占满整个浏览器视口,无滚动条,分为「侧边栏 + 顶部导航 + 主体内容区」三部分;侧边栏可折叠,顶部导航固定,主体内容区滚动,所有区域高度自适应 100%
  • 核心考点:height:100vh、Flex 垂直布局、overflow:auto 内容滚动、flex:1 自适应占比
  • 业务场景:所有后台管理系统的基础布局(Vue/React 项目的后台模板)

实现

  • 好吧,即使貌似是antd的layout组件,貌似还是需要自行实现侧边栏的吸顶、顶部导航栏的固定滚动啥的。
  • 通常不会给layout和main限制高度,让其自行撑开整个网页,然后基于网页的滚动即可
    • 而侧边栏和顶部导航栏,则自行为他们添加固定定位和粘性定位即可。
    • 侧边栏的高度,也需要自行设置(max-width: 100vh、calc(100vh - 68px) 这个68是顶部导航栏的高度

布局 16:瀑布流布局(不规则高度布局,高频业务)(待深入)

  • 实现要求:多列布局,元素按「高度从小到大」依次排列,每列的元素高度不一致,但整体无大的空白间隙,自适应屏幕宽度,自动换行,响应式适配
  • 核心考点:CSS 版瀑布流(column 多列布局)、JS 版瀑布流(动态计算高度)、图片懒加载
  • 业务场景:电商商品列表、图片相册、小红书 / 知乎的内容流、瀑布流商品展示

说明

  • 这个感觉,挺麻烦的
  • 以两列为例,每一个元素的高度不固定,如果一列高度较小,则将下一个元素内容放到列高度小写的那一列
  • 纯css可以做到吗?
  • 还是需要js动态计算高度?
    • 那如果列变化了,是否需要重新计算高度?例如两列变三列时

实现-css实现:css实现貌似没有太好的办法去实现瀑布流

grid实现

  • grid的实现不太行,除非你你能确定元素占据具体的几列(例如照片墙那种,配合grid-auto-flow: dense)
  • css3网格布局提到了一个原生 Masonry (未来/实验性)用来实现瀑布流布局,可惜,暂时没有任何一个浏览器支持,需要开启实验性功能才行
  • 除此之外,仍然需要使用js计算元素高度得出需要跨越的行高,然后确定放置在那一列的那一个网格区域(使用span)。
    • 设置网格容器:display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));。
    • 获取真实高度:渲染后,用 JavaScript 获取每个子元素(卡片)的真实 clientHeight。
    • 计算占用的行数:将卡片真实高度除以网格行高,计算出卡片需要跨越的行数。
    • 应用 grid-row-end:通过 JavaScript 动态给卡片添加内联样式 grid-row-end: span <行数>;,让卡片占据合适的网格空间。
    • 处理间距:别忘了在计算行数时考虑卡片之间的间距

css column属性设置

  • 它将元素的内容分割为设定的几列,并配上break-inside: avoid;等属性控制子元素分割行为
  • 缺点
    • 排序规则是「先列后行」(第一列从上到下填满 → 第二列 → 第三列),而非「先行后列」;
    • 无法手动控制元素的排序顺序,只能按 DOM 渲染顺序排列。
1
2
3
4
5
6
7
8
9
/* 核心3行CSS 实现瀑布流 */
.waterfall {
column-count: 3; /* 核心:设置展示的列数,PC端3列 */
column-gap: 10px; /* 核心:设置列与列之间的间距 */
}
/* 关键属性:防止子项内容被列分割(必加,否则卡片会被截断) */
.waterfall-item {
break-inside: avoid;
}

js实现

核心特点

  • 完全可控:排序规则、元素位置、填充逻辑、加载方式 全部由 JS 控制,解决 CSS 瀑布流的所有局限性;
  • 核心原理:JS 动态计算每一列的高度,将新的元素插入到「当前高度最矮的一列」中,实现绝对均匀的填充,无任何留白;
  • 开发成本比 CSS 高,需要写 JS 逻辑,但灵活性拉满;
  • 分 2 种核心写法:基础版JS瀑布流(一次性渲染)、进阶版JS瀑布流(滚动加载/懒加载),后者是工作中高频需求;
  • 依赖 DOM 渲染,需要等图片 / 元素加载完成后再计算高度,否则会有布局错位问题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 核心:瀑布流渲染函数
function renderWaterfall() {
const waterfall = document.getElementById('waterfall');
const items = document.querySelectorAll('.waterfall-item');
const columnCount = window.innerWidth >768 ? 3 : (window.innerWidth>480 ?2:1); // 响应式列数
const gap = 10; // 间距
const itemWidth = (waterfall.offsetWidth - (columnCount -1)*gap) / columnCount; // 计算子项宽度

// 1. 定义数组,存储每一列的当前总高度,初始值为0
const columnHeights = new Array(columnCount).fill(0);

// 2. 遍历所有子项,计算位置并渲染
items.forEach(item => {
item.style.width = `${itemWidth}px`;
// 2.1 找到高度最矮的列的索引
let minHeight = Math.min(...columnHeights);
let minIndex = columnHeights.indexOf(minHeight);

// 2.2 计算当前元素的top和left
let itemLeft = minIndex * (itemWidth + gap);
let itemTop = minHeight;

// 2.3 设置元素的绝对定位
item.style.left = `${itemLeft}px`;
item.style.top = `${itemTop}px`;

// 2.4 更新当前列的总高度(当前列高度 + 元素高度 + 间距)这里使用了offsetHeight获取元素高度,会触发重排
columnHeights[minIndex] = minHeight + item.offsetHeight + gap;
});

// 3. 设置瀑布流容器的高度,等于最高列的高度,防止容器塌陷
waterfall.style.height = `${Math.max(...columnHeights) - gap}px`;
}

// 执行渲染
window.onload = renderWaterfall;
// 窗口大小改变时重新渲染(响应式核心)
window.onresize = renderWaterfall;
  • 优点
    • 填充绝对均匀,无任何留白,解决 CSS 瀑布流的最大痛点;
    • 排序规则是「先行后列」,符合大众视觉习惯;
    • 列数、间距、元素位置完全可控,灵活性极高;
    • 原生 JS 无依赖,兼容性好。
  • 缺点
    • 需要等所有元素(尤其是图片)加载完成后才能渲染,否则高度计算错误导致布局错位;
    • 一次性渲染大量数据时,会有轻微的计算卡顿;
      • 可以考虑分批次,不过如果是响应式布局出现列数量变化时,可能会更加麻烦
    • 代码量比 CSS 方案多。
  • 动态加载:- 核心升级点
    • 监听浏览器的 scroll 滚动事件,判断是否滚动到页面底部;
    • 滚动到底部时,异步请求新数据 → 动态创建 DOM 元素 → 追加到瀑布流容器 → 重新执行瀑布流渲染逻辑;
    • 图片懒加载:先加载占位图,图片进入可视区域后再加载真实图片,优化性能。

布局 17:响应式导航栏布局(移动端适配,汉堡菜单,高频)(待深入)

  • 实现要求:桌面端「导航项横向排列」,移动端「导航项隐藏,显示汉堡菜单,点击展开下拉菜单」;导航栏吸顶,适配所有屏幕尺寸,无错位,交互流畅
  • 核心考点:媒体查询 @media、display:none/block 控制显示隐藏、position:sticky 吸顶、移动端适配的核心断点
  • 业务场景:所有网站的导航栏、移动端 H5 页面的头部导航
  • 核心断点:768px(移动端 / 平板分界)、1200px(平板 / 桌面分界),这是行业通用标准

实现

  • 默认基于移动端实现汉堡菜单导航组件
  • 然后实现pc端的水平列表导航组件
  • 最后基于媒体查询,控制这两个菜单的显示和隐藏
  • 注意,这两个组件会有相对较为通用的样式,例如导航栏高度(可选)、粘性定位等

布局 18:卡片悬浮布局(商品卡片 / 内容卡片,高频业务)

无单独示例,参考:布局 12:弹性布局 这一部分有实现类似卡片列表的效果

  • 实现要求:卡片宽度自适应,高度随内容变化,带阴影 / 圆角 / 悬浮效果,卡片之间间距一致,自动换行,响应式适配,hover 时无卡顿的过渡动画
  • 核心考点:Flex/Grid 栅格、border-radius/box-shadow 样式、transition 过渡动画、hover 伪类交互
  • 业务场景:电商商品卡片、博客文章卡片、后台管理系统的统计卡片、首页推荐模块

布局 19:绝对定位的层叠布局(弹窗 / 遮罩 / 固定层,高频交互)

  • 实现要求:弹窗 / 遮罩层「水平 + 垂直居中」,遮罩层占满整个屏幕,弹窗层在遮罩层之上,页面滚动时弹窗固定在屏幕中央,关闭弹窗后遮罩层消失
  • 核心考点:position:fixed、z-index 层级控制、background:rgba(0,0,0,0.5) 半透明遮罩、居中布局
  • 业务场景:弹窗、登录框、提示框、模态框、侧边抽屉、底部弹层

布局 20:等高布局(多列等高,无错位,高频细节)

无单独示例,参考flex(布局12)和grid(布局13)的布局即可,它们是默认等高的

  • 实现要求:多列布局中,「所有列的高度始终一致」,无论每列的内容多少,都不会出现一列高一列低的情况,布局整齐美观
  • 等高列:水平方向的元素,其元素高度都相等,且是随着元素内容动态改变也相等
    • 任意一列的内容增加(变高),所有列的高度都会增加,且所有元素的高度为所有水平元素中最高的那个元素的高度。即矮的那些会自动使用高度。
  • 核心考点:Flex 的默认特性(子元素自动等高)、Grid 的等高特性、display:table 表格布局、min-height 保底高度
  • 业务场景:商品卡片列表、导航栏选项、内容模块布局、表单的左右分栏
  • 核心说明:Flex 天生支持等高布局,这是 Flex 最大的优势之一,也是替代浮动布局的核心原因

实现:基于flex布局默认就是等高的,grid也可以,其网格轨道默认也是等高的

布局 21:文字环绕布局(图文混排,经典排版)

  • 实现要求:图片在左侧 / 右侧,文字自动环绕在图片周围,文字和图片之间有间距,适配不同屏幕宽度
  • 核心考点:float:left/right 浮动特性、margin 间距控制、overflow:hidden 清除浮动
  • 业务场景:新闻详情页、博客文章、产品介绍页、图文混排的内容页面

说明:很少见了,不论是移动端还是企业网站,都很少使用这种报纸类的排版风格

实现

  • 利用浮动是目前最方便实现图文混排的方式,了解清除浮动、父元素高度塌陷、margin实现浮动元素和文字间距等。
1
2
3
4
.article { width: 800px; margin:0 auto; line-height:1.8; }
.article img { float: left; width: 200px; margin:0 20px 20px 0; }
/* 清除浮动,防止父元素高度塌陷 */
.article::after { content:""; display:block; clear:both; }

布局 22:固定宽高的居中裁剪布局(图片不失真,高频细节)

无单独示例,参考布局 12:弹性布局中的实例,其中有类似的,也可以参考下文:图片宽高设定和卡片顶部的固定图片宽高

  • 实现要求:容器宽高固定,图片在容器中「水平 + 垂直居中」,超出容器的部分自动裁剪,图片保持比例不变,不失真、不拉伸
  • 核心考点:object-fit: cover(CSS3 核心属性)、overflow:hidden 裁剪、居中布局
  • 业务场景:商品图片展示、头像、封面图、相册图片、轮播图
  • 核心说明:object-fit 是处理图片变形的最优解,替代所有 JS 的图片裁剪逻辑,必须掌握

布局 23:移动端适配布局(rem/vw 等比适配、等比缩放布局,核心实战)

无单独示例,参考布局 10:流式布局中的内容,其中提到了rem和vw来编写页面的细节和可能的问题

  • 实现要求:基于设计稿(如 375px/750px),实现「移动端页面等比缩放」,所有元素的尺寸随屏幕宽度等比变化,在不同分辨率的手机上视觉比例一致,无错位、无变形
  • 核心考点:rem 单位的换算、vw/vh 的使用、postcss-px-to-viewport 插件自动转换 px 为 vw
  • 业务场景:所有移动端 H5 页面、小程序页面
1
2
3
4
5
6
7
8
9
/* ✅ 方案1:rem适配(主流,兼容所有移动端) */
/* 根节点设置font-size:1rem = 100px(设计稿375px) */
html { font-size: calc(100vw / 3.75); }
/* 元素尺寸用rem:设计稿100px → 1rem */
.box { width: 2rem; height: 1rem; }

/* ✅ 方案2:vw适配(最简,无JS依赖,推荐) */
/* 设计稿375px → 1vw = 375px /100 = 3.75px */
.box { width: 26.66vw; height: 13.33vw; }

布局 24:层叠上下文布局(z-index 层级控制,避坑必备)

  • 实现要求:掌握「层叠上下文」的创建规则,解决 z-index 层级失效的问题,正确控制元素的显示层级,避免弹窗被遮挡、导航栏被覆盖等问题
  • 核心考点:层叠上下文的触发条件、z-index 的生效规则、定位元素的层级关系
  • 业务场景:弹窗、遮罩、导航栏、悬浮按钮、下拉菜单
  • 核心避坑:z-index 只有在元素有定位(position:absolute/relative/fixed/sticky)时才生效,父元素的层叠上下文会影响子元素的层级
  • 核心规则(必记):
    • 层叠上下文的层级:定位元素 > 浮动元素 > 普通元素(可以参考之前文章中的层叠上下文和z-index这一节
    • z-index 的数值越大,层级越高,但只在同一层叠上下文中生效
    • 父元素的 z-index 层级低,子元素的 z-index 再高也无法覆盖父元素的兄弟元素

布局问题

  • 了解最常见的布局需求
  • 并了解最有效率或者比较有效率的布局方式
  • 并非要做一个布局高手

响应式媒体查询(移动端优先)

基于媒体查询的 xs、sm、md、lg(+xl) 断点规范写法(行业标准,可直接复制套用)

  • xs、sm、md、lg 是前端响应式开发的行业通用断点命名规范(源自 Bootstrap 框架,99% 的项目都遵循这个标准),对应的媒体查询写法是移动端优先(Mobile First) 最佳实践,也是最规范、最易维护的写法,没有之一。
  • 行业标准的 xs/sm/md/lg/xl 断点定义
    • 这套规则是移动端优先的核心,所有断点的宽度阈值是「设备视口宽度 (viewport)」,命名和对应宽度是行业统一标准,不要自己自定义数值,团队协作、项目维护都会更顺畅
    • xs → 超小屏 (Extra small):视口宽度 < 576px → 手机竖屏(iPhoneSE、小米、华为等主流手机)
    • sm → 小屏 (Small):视口宽度 ≥ 576px → 手机横屏 / 小尺寸平板
    • md → 中屏 (Medium):视口宽度 ≥ 768px → 平板(iPad)、大屏平板
    • lg → 大屏 (Large):视口宽度 ≥ 992px → 小尺寸笔记本、一体机
    • xl → 超大屏 (Extra large):视口宽度 ≥ 1200px → 桌面端电脑、大屏显示器
    • 补充:部分项目会加 xxl ≥1400px,按需加即可,核心还是前 5 个。
  • 移动端优先的精髓:默认样式就是 xs 超小屏的样式,不需要写媒体查询包裹,大屏的样式通过媒体查询「叠加 / 覆盖」默认样式即可,这样写的代码量最少、优先级最清晰,不会有样式冲突。
    • 也就是说,移动优先的媒体查询写法,大屏时,小屏幕的媒体查询样式也会被应用,需要在大屏媒体查询中,按需求覆盖

写法规则(重中之重)

  • 媒体查询用 @media (min-width: 阈值px) 编写 → 最小宽度匹配,符合「从小到大」的移动端优先原则
  • 所有断点按 sm → md → lg → xl 的顺序编写,从上到下写在 CSS 末尾
  • 默认样式 = xs 样式,无需写 @media (max-width:575px),减少冗余代码
  • 语法:@media screen and (min-width: 断点值) { ... },screen 可省略,不影响效果
  • 只写「变化的样式」,不重复写公共样式
    • 移动端优先的核心优势:默认样式是 xs 样式,媒体查询里只需要写「大屏和小屏不一样的样式」。
  • 单位建议:百分比 /calc () /rem/vw 优先,少用固定 px

通用样式的媒体查询写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/* ====================== 全局公共样式 & xs超小屏样式 (默认)  ====================== */
/* 所有<576px的设备生效,这就是xs的样式,无需媒体查询包裹 */
body { padding: 0 10px; }
.box { width: 100%; margin-bottom: 10px; }
/* 你的浮动圣杯布局/Grid布局的默认移动端样式 都写在这里 */

/* ====================== sm 小屏 (≥576px) ====================== */
@media (min-width: 576px) {
body { padding: 0 20px; }
.box { width: calc(50% - 5px); } /* 一行2列 */
}

/* ====================== md 中屏 (≥768px) ====================== */
@media (min-width: 768px) {
body { padding: 0 30px; }
.box { width: calc(33.333% - 6.666px); } /* 一行3列 */
}

/* ====================== lg 大屏 (≥992px) ====================== */
@media (min-width: 992px) {
body { padding: 0 40px; max-width: 1200px; margin: 0 auto; }
.box { width: calc(25% - 7.5px); } /* 一行4列 */
}

/* ====================== xl 超大屏 (≥1200px) ====================== */
@media (min-width: 1200px) {
body { max-width: 1400px; }
.box { width: calc(20% - 8px); } /* 一行5列 */
}

补充:两种常见的扩展写法(按需选择)

  • 扩展 1:大屏「最大宽度限制」,避免内容拉伸过宽
    • 大屏(lg/xl)时,如果页面宽度 100% 拉伸到 2k/4k 显示器,内容会很松散,给页面容器加 max-width + margin:0 auto 是行业标配,必加!
  • 扩展 2:反向断点 max-width(慎用,仅做局部调整)
    • 偶尔会遇到「某个样式只在小屏生效,大屏需要去掉」的场景,这时可以用 max-width 写「上限断点」,注意:不要和 min-width 混用,避免逻辑混乱。

响应式媒体查询(桌面端优先)

类似如下写法

1
2
3
4
@media (max-width: @screen-xs-max) { ... }
@media (min-width: @screen-sm-min) and (max-width: @screen-sm-max) { ... }
@media (min-width: @screen-md-min) and (max-width: @screen-md-max) { ... }
@media (min-width: @screen-lg-min) { ... }

这种 max-width单条件 + min-width and max-width区间 的写法,有一个专属名称:【桌面端优先(Desktop First)】响应式规则,也叫「固定区间匹配规则」,它的使用场景是分场景、分优先级的

  • 固定区间匹配规则就是指:该规则匹配的媒体查询只有一个生效(移动优先的是大屏样式覆盖小屏样式)
  • 场景 1:【项目的核心受众是 PC 端,移动端是「兼容适配」】【最核心、最常用】
    • 比如:企业官网、后台管理系统、文档网站、电商 PC 端主站,这些项目的主要流量 / 核心功能在 PC 大屏,移动端只是「让用户能看、能用」即可,不需要做极致体验;
    • 设计逻辑:先写好 PC 大屏 (lg) 的完整样式(默认样式),再通过「区间媒体查询」给「平板 (md)、小平板 (sm)、手机 (xs)」做降级适配;
    • 核心优势:PC 端样式不用被任何媒体查询包裹,代码最简洁、最易维护,移动端只是「补充样式」,开发效率极高。
  • 场景 2:【需要「不同尺寸区间,完全独立的样式 / 布局」,互不干扰】
    • 这是这种写法最核心的特性,也是它和「移动端优先」最大的区别,是不可替代的优势
    • 核心特性:min-width: A and max-width: B 是「精准区间匹配」,这个媒体查询里的样式,只在 [A,B] 这个宽度范围内生效,超出范围立刻失效,不会向上 / 向下层叠覆盖。
对比维度 Bootstrap3.x 桌面优先(区间型) 现代主流 移动端优先(纯 min-width)
默认样式 PC 大屏 (lg) 样式,无媒体查询包裹 手机小屏 (xs) 样式,无媒体查询包裹
媒体查询写法 max-width 单条件 + min+max 区间 只有 min-width 单条件,从小到大排列
适配逻辑 从大屏 → 小屏,「降级适配」移动端 从小屏 → 大屏,「升级适配」PC 端
样式生效规则 精准区间匹配,样式「隔离」,互不干扰 层叠覆盖,大屏样式「叠加」小屏样式,小屏样式大屏仍生效
代码量 PC 端简洁,移动端需要写更多重置样式 移动端简洁,PC 端只写变化样式,代码量更少
核心优势 适合「不同区间独立布局」,无样式冲突 适合「渐进式布局」,代码简洁、维护成本极低
核心缺点 移动端样式冗余,容易出现「小屏样式残留」 不同区间布局差异大时,需要写大量样式重置
适用项目 PC 端为主、移动端兼容;旧项目维护;独立区间布局 移动端为主、多端体验一致;新项目开发;流式布局
代表框架 Bootstrap 3.x Bootstrap 4.x/5.x、TailwindCSS、所有现代 UI 框架

注意:

  • 注意 1:不要「混用两种写法」
    • 比如:在同一个 CSS 文件里,既写 @media (max-width:767px),又写 @media (min-width:576px),会导致样式优先级混乱,出现「某个宽度样式闪烁 / 失效」的问题,一个项目只能选一种核心写法。
  • 注意 2:区间匹配时,「上下限不要重叠」
    • 比如:sm 的上限是 991px,md 的下限必须是 992px,绝对不能写成 991px,否则会出现「991px 宽度时,两个媒体查询都生效」的冲突
    • Bootstrap 的做法是:小屏上限 = 大屏下限 - 1px,比如 767=768-1,991=992-1,1199=1200-1,完美规避重叠。
  • 注意 3:不要给区间查询写「全局样式」
    • 区间查询适合写「局部模块的独立样式」,如果写全局样式(比如 body、.container),会导致样式碎片化,维护成本极高。

容器内的元素间距

这是很常见的,让容器内的元素在水平或者垂直方向的间距保持一致,且美观,而且能够自适应间距、高度等。例如让一个容器内的元素,在垂直方向的间距具有相同的大小,或者平分剩余部分的空白作为间距

迟钝猫头鹰选择器(lobotomized owl selector)(以下简称猫头鹰选择器)

  • 因为它长这样:* + *。该选择器开头是一个通用选择器(*),它可以选中所有元素,后面是一个相邻兄弟组合器(+),最后是另一个通用选择器。它因形似一只眼神空洞的猫头鹰而得名。
    • 它会选中页面上有着相同父级的非第一个子元素
    • 例子.class * + * { margin-top: 20px; }
  • 该方案的核心是相邻兄弟选择器,可以让你选择除了第一个子元素之外的其他子元素
  • 但是使用该方案有一定的局限性吧,局部可以使用,但是仍然需要考虑,他可能对所有后代元素生效。

实现照片墙

要求

  • 根据照片的大小,定义显示区域的大小
    • 这里只要求了小照片占1 x 1,大照片占据 2 x 2,是固定的
  • 这个布局很有意思,因为它用Flexbox或者浮动很难实现。这个例子充分展示了网格特有的能力。
  • 或者说它是瀑布流,可能需要配合js去实现

参考:照片墙效果图

实现

  • 方案:网格布局
  • 通过grid-auto-flow来设置grid的布局算法来填满网格)

一行内容中的元素垂直居中 + 其中某一块内容占据剩余宽度

例如,榜单中的一行元素,从左到右:排名 + 头像 + 用户名 + 分数

flex布局:

  • align-items: center;实现垂直居中
  • > div + div:margin-left: 10px; 猫头鹰选择器,实现元素元素间隔(不用justify-content)
  • 不是flex:1的元素,可以设置为:flex:none; 等价于:flex: 0 0 auto;仅获取元素内容宽度大小,不会收缩和扩张

垂直方向多个元素水平居中

例如,从上到下:图片 + 用户名 + 标签(说明)

flex实现

  • flex-direction: column; 垂直排列
  • align-items: center; 实现水平方向居中
  • > div + div猫头鹰选择器,设置margin-top来控制元素间距。justify-content没用,因为其高度是根据内容来撑开的
  • 内部元素自行根据内容设置宽高即可。

固定列,多个相同元素自动换行(例如一列3个)

例如一列3个元素,每个元素占据差不多3分之一的宽度,超出一列时换行,优先向左对齐

flex布局

  • 普通的flex设置,添加一个flex-wrap为wrap来换行
  • 通常,根据列来固定宽度,例如3列就是width: 32%;左右(或者按照设计稿设置)
  • 水平和垂直间距
    • 使用justify-content: space-between;设置水平间距(通常自动处理即可)
    • 垂直间距可以使用row-gap,或者为了兼容性,设置margin-bottom也可以,只需要注意容器的底部padding看是否需要调整。
  • 如果要自行设置间距margin-right和margin-bottom,那么需要配合&:nth-child(3n)来移除每列最后一个元素的margin(如果是3列)
  • 或者需要自定义间隔且精确的话,可以使用负边距,则列数可以按照等比去分,然后使用padding来设置列边距。只不过需要内部多套一个容器

关于列宽度是否需要固定

  • 如果列不设置固定宽度,要使用flex: 1,因为flex布局中,最后一个元素如果是flex为1,则会占据全部,需要设置max-width: 33%或者32%
  • 那其实这样,也就是等于设置了width: 33%,因为会被自动填充宽度

grid布局

  • 使用grid会简单不少,毕竟定义三列等宽即可,同时使用gap直接设置间距即可。
  • 注意一个坑在于,子元素中使用了文本的超出隐藏:text-overflow: ellipsis; 如果直接为每一列设置1fr,那么文本超长的列会撑开列的网格轨道宽度,且文本无法超出隐藏。
  • 解决方案是使用minmax函数,即用grid-template-columns: repeat(3, minmax(0, 1fr)); 替代 grid-template-columns: repeat(3, 1fr);

图片宽高设定和卡片顶部的固定图片宽高(具有响应式)

图片元素

  • 图片是「替换元素」,天生有【固定的宽高比】(比如常见的 16:9、4:3、3:2、1:1),这个比例是图片本身的属性,不会改变。
  • 当你只写 img { width:100%; } → 浏览器会自动按图片原始宽高比,计算出对应的高度,图片完全不变形 (推荐)
    • 不过由于在图片加载完成之前,去图片元素的高度是0,所以会在布局上可能会出现跳动,height: auto也一样
  • 当你写 img { width:100%; height:200px; } → 强制给图片指定高度,宽高比被打破,图片必然拉伸变形(尽量不要这样)
  • 当你写 img { width:100%; height:auto; } → height:auto是浏览器默认值,效果和只写 width:100% 完全一致,图片按原始比例自适应
  • object-fit(通常给图片或者video使用):它定义了图片元素如何适应到其使用高度和宽度确定的框。(即如果你的img元素宽、高都设置了的时候,它可以根据宽高确定图片内容的显示适配方式,例如拉伸、填充、覆盖等)
    • img图片元素设置了宽和高
    • 给img图片设置object-fit:cover;等值

说明

  • 实现要求:一个卡片中的,图片元素宽度占据100%,且高度和宽度按照固定的比例缩放,图片内容需要自适应
  • 元素宽高等比、图片元素的object-fit

实现:

  • 方案一:如果是响应式设计(其卡片宽度会随着页面宽度改变的话)则需要图片元素的展示等比
    • object-fit:cover实现图片内容自适应
    • 使用padding-top来基于宽度设置元素高度,内部使用绝对定位和top、left、right、bottom占满元素宽高。
    • img元素宽高设置为定位元素的100%
    • 这是这个方案由于其图片宽高是确定的了,不会触发元素的偏移,即使图片加载是异步的
  • 方案二:css3属性:aspect-ratio,为图片元素设置固定宽高比,那么此时图片只需要设置固定宽或者100%,然后再配合object-fit:cover来让图片自适应即可
    • 注意:兼容性差了点(ios 15才支持)
    • 理论上由于能够给予宽度定义高度了,它不应该产生布局跳动才对
  • 如果是纯移动端h5,则可以利用移动的等比适配,利用rem和vw来设置固定宽高
  • 如果是pc端,其卡片宽度不会动态变化,则直接写死卡片宽度,此时图片的宽高就可以也写死了。
  • 其他方案:背景图方式 background-image + background-size: cover
    • 这个的问题在于,图片和背景是有区别的,一个是seo、一个是无障碍这种,特殊情况可以用吧,例如「纯装饰、无实际内容」的(比如渐变背景、纹理图),不需要 SEO 和无障碍支持。