ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Bootstrap 4栅格系统深度解析:Flexbox引擎如何重塑布局

Bootstrap 4栅格系统深度解析:Flexbox引擎如何重塑布局 前阵子帮同事排查一个改版项目他把Bootstrap 3里的col-md-push-6、col-md-pull-6这类排序类名直接搬到了Bootstrap 4页面上结果左右两栏错位、内容顺序乱成一团。这个场景我见过太多次。从Bootstrap 3升到4很多人只盯着组件长得好不好看却忽略了一件事整个栅格布局的底层引擎已经从float换成了Flexbox。这不是换个皮肤是换了发动机。如果你正在维护Bootstrap 4项目或者准备把老项目往4.0迁移我建议先停下来把Flex工具类和栅格源码一起读透。读透之后你会发现以前靠hack才能实现的布局——垂直居中、等高卡片、任意重排、宽度自适应——在Bootstrap 4里就是一行类名的事。这篇文章我会从引擎替换的原因讲起拆开栅格源码看它的Flex内核再把对齐、分布、响应式断点、实战中踩过的坑串成一条线。适合已经能写基本页面、但没系统研究过Flex的开发者也适合正在做技术升级选型的前端负责人。1. 为什么说Bootstrap 4的栅格是“换引擎”而不是“换皮肤”1.1 float栅格在真实项目里积累的三个老账Bootstrap 2、3时代的栅格体系本质上是一套基于float的百分比宽度切分方案。每个.col-md-6被设置成width: 50%然后向左浮动父容器再用.row的负margin和::after清除浮动。这套方案在等宽划分上够用但遇到下面三类需求就非常吃力。第一是垂直居中。float和width只能控制水平位置的分布垂直方向完全取决于内容撑开的高度。想让一个按钮在一个固定高度的容器里垂直居中Bootstrap 3时代只能靠line-height硬凑或者把容器display: table-cell再配合vertical-align: middle遇上更复杂的嵌套结构就得用绝对定位加transform: translateY(-50%)。这不是代码不好写的问题是这些方案都对内容高度变化极其敏感。线上出过一次文案变长导致按钮位置跑偏的事故后我就不敢再用hack方案了。第二是等高布局。多列卡片、左右两栏带背景色这种需求在float体系下是靠“背景欺骗”处理的——要么给每一列套一个等高背景图要么用display: table把三列伪装成单元格。列数一变CSS就得跟着改维护成本很高。第三是内容排序。Bootstrap 3提供了.col-md-push-8和.col-md-pull-4这类类名通过margin-left的百分比把列推到指定位置。这个机制能用但改一次顺序必须同时算清楚push和pull的对应值而且只支持固定偏移没法做“把第一项排到最后”这种灵活操作。三个问题叠加起来就是Bootstrap 4必须换引擎的直接原因。1.2 Flexbox在机制上改变了哪三件事Flexbox和float最大的区别是引入了主轴main axis与交叉轴cross axis的概念。主轴是内容排列的方向交叉轴是垂直于内容排列的方向。这两根轴的存在让“分布”和“对齐”第一次变成了排版的显式能力而不是靠width、margin、position硬算出来的结果。其次Flexbox引入了弹性尺寸。flex-grow控制剩余空间如何分配flex-shrink控制空间不足时如何压缩flex-basis控制项目在主轴上的初始尺寸。这三个属性组合起来让容器从“被动装内容”变成了“主动分配空间”。第三Flexbox支持层级排序。通过order属性Flex项目可以不修改HTML顺序直接在视觉层面对调位置。这一点是float体系完全做不到的也是Bootstrap 4把整个栅格重写后官方敢直接在类名上带响应式断点的底气。1.3 兼容性换引擎为什么能落地Bootstrap 4正式版发布于2018年官方把浏览器支持范围定在了IE10、Chrome、Firefox、Safari 8这一类现代浏览器。Flexbox在IE10和IE11下有兼容性问题但Bootstrap 4的源码里已经用Autoprefixer处理了-ms-前缀标准场景下直接用类名没有太大问题。真正需要注意的是如果你要在Bootstrap 4项目里手写自定义Flex样式必须记得自行处理IE的-ms-flex、-ms-flex-align这类属性或者干脆用官方工具类代替手写CSS。2. 先读懂d-flex家族Bootstrap 4对Flex能力的封装方式2.1 工具类与CSS属性的对应关系Bootstrap 4把Flexbox能力拆成了多组工具类全部放在utilities/flex.scss里命名严格对应一条CSS属性。这个设计的最大好处是类名即文档看到类名就知道它要干什么不需要翻源码。类名前缀对应CSS属性取值.d-flex / .d-inline-flexdisplayflex / inline-flex.flex-row / .flex-columnflex-directionrow / column 等.flex-wrap / .flex-nowrapflex-wrapwrap / nowrap 等.justify-content-*justify-contentstart / end / center / between / around.align-items-*align-itemsstart / end / center / baseline / stretch.align-self-*align-selfstart / end / center / baseline / stretch / auto.align-content-*align-contentstart / end / center / between / around / stretch.flex-grow-* / .flex-shrink-*flex-grow / flex-shrink0 / 1.order-*order0-12 / first / last这些类都带!important优先级最高不会被常规样式覆盖。这是Bootstrap工具类的统一策略好处是写了就生效不会跟组件内部的样式打架坏处是没法用普通class覆盖它想改只能换类名或者加一条同样带!important的规则。2.2 主轴方向flex-row和flex-column到底在控制什么默认情况下一个加了.d-flex的容器是水平排列子元素的因为flex-direction的默认值是row。如果需要把子元素改成纵向排列就用.flex-column例如侧边栏里一组菜单图标加文字垂直排比水平排更合适。这里最容易搞混的是“主轴”和“视觉方向”的关系。主轴方向决定的是justify-content作用的维度交叉轴方向决定的是align-items作用的维度。举个例子.flex-row里justify-content-center是水平居中align-items-center是垂直居中一旦换成.flex-column两者就交换了——justify-content-center变成垂直居中align-items-center变成水平居中。很多人布局对不齐不是因为类名写错而是没搞清楚当前容器的主轴方向。我见过一个真实的页面按钮在移动端纵向布局下怎么设置都偏左排查了半天原因是容器用了.flex-column而代码里却用了.justify-content-center去试图实现水平居中。这个类在column方向下作用的是垂直轴水平方向根本没被控制。改成.align-items-center之后才正常。2.3 flex-wrap的三态与栅格换行的底层逻辑Flex项目默认不换行一行装不下就压缩。想让项目超过容器宽度后换到下一行需要给容器加.flex-wrap。Bootstrap 4里.flex-wrap和.flex-nowrap都支持响应式前缀你可以让桌面端wrap、移动端nowrap这个在横向滚动标签栏的场景里很实用。.row栅格本身就是一个带flex-wrap: wrap的Flex容器所以栅格列才会在宽度总和超过12后自动换行。理解了这一点你就明白为什么栅格列可以不写行内的清浮动——Flex容器根本不需要clearfix这是Bootstrap 4删掉.row里::after清浮动代码的根本原因。3. 栅格系统内部的Flex实现等宽列、自动列、排序与偏移3.1 从.col到.col-*flex-basis和flex-grow的组合Bootstrap 4的栅格源码里最值得读的是这两段。等宽列.col的处理方式是把flex-basis设为0然后让flex-grow平均分配剩余空间.col { flex-basis: 0; flex-grow: 1; max-width: 100%; }固定比例列.col-4则直接用flex的简写属性锁定比例.col-4 { flex: 0 0 16.666667%; max-width: 16.666667%; }两段代码的差异很关键。.col没有预设宽度宽度完全由内容数量决定.col-4的flex-basis是16.666667%flex-shrink是0意味着它始终占满四分之一且不允许被压缩。这就是为什么同一行里放四个.col-4会自动均分而放一个.col-4加两个.col时两个.col会把剩下的空间自动平分。实际项目里.col这种自动均分写法非常适合“几列都差不多宽”的内容区。我曾经把一个后台管理页面的四个统计卡片从.col-lg-3全部改成.col删掉了一堆断点类名代码更干净换行逻辑也更符合直觉。3.2 order排序类的源码逻辑Bootstrap 4的order工具类长这样.order-first { order: -1; } .order-last { order: 13; } .order-0 { order: 0; } // 一直到 .order-12 { order: 12; }order的数值越小越靠前默认项目order为0。所以.order-first用-1把项目排到最前面.order-last用13把项目排到最后面中间的1到12可以细粒度控制顺序。这个能力最典型的场景是移动端优先的内容重排。例如一个商品详情页桌面端左图右文移动端希望文字在前图片在后且都占满整行。用Bootstrap 3你得在桌面端和移动端各写一套push/pull用Bootstrap 4直接这样写div classrow div classcol-md-6 order-md-2 img srcproduct.jpg alt商品图 /div div classcol-md-6 order-md-1 div classproduct-intro h3商品标题/h3 p商品描述文本/p /div /div /div没有加断点前缀的order默认情况下order-md-1会让图片在桌面端排到左侧移动端因为类名不生效图片按HTML顺序仍然在文字前面。这个“按断点覆盖”的特性是Bootstrap 4响应式排序的核心思路也是我在项目里用得最频繁的响应式手段之一。3.3 offset偏移类和margin:auto在Flex中的特殊价值Bootstrap 4保留了.offset-md-3这类偏移类源码实现就是margin-left的百分比.offset-md-3 { margin-left: 25%; }但Flex体系下更优雅的是auto margin。Bootstrap 4官方工具类里有.ml-auto、.mr-auto也就是margin-left: auto和margin-right: auto。Flex项目的auto margin会“吃掉”主轴上的空闲空间所以.ml-auto写在Flex容器的某个子元素上时这个子元素会被推到这一行的最右边。举个例子一个简单的顶部导航栏左边Logo右边操作按钮nav classnavbar bg-white d-flex align-items-center px-3 a classnavbar-brand href#Logo/a div classml-auto a href#登录/a a href#注册/a /div /nav这段代码里只要给右边容器加.ml-auto它就会自动吸到右侧中间的空隙全部由auto margin吃掉。这比用justify-content-between更灵活因为当你有三个元素、只想让后面两个靠右时justify-content-between是做不出来的。4. 对齐与分布的完整实战justify-content、align-items、align-self、align-content4.1 主轴上的分布策略Bootstrap 4提供的justify-content共有五个值对应五类常见的分布需求.justify-content-start默认子元素靠主轴起点排.justify-content-end靠终点排.justify-content-center居中排.justify-content-between首尾贴边中间等距.justify-content-around每个元素两侧间隔相等between和around的区别是布局场景里最容易混淆的。between是“两端对齐”第一个元素贴左、最后一个贴右中间均分适合导航栏里左右两侧各有一个内容块around是“每项左右都有空隙”首尾元素与容器边缘也有间隙适合一排图标按钮的均匀分布。我之前做过一个筛选器区域四个筛选按钮要均匀分布在一条工具栏上用justify-content-around很自然但如果换成between四个按钮就会变成左右各一个、中间两个悬空视觉效果完全不是一回事。所以选之前先想清楚你要的是“首尾贴边”还是“整体均分”。4.2 交叉轴对齐从align-items到align-selfalign-items控制的是Flex容器内所有项目在交叉轴上的对齐方式。默认值是stretch也就是子元素沿交叉轴方向被拉伸填满容器高度。这正是Bootstrap 4卡片能等高显示的底层原因——同一行里的.col会被默认拉伸到和其它列一样高。.align-items-start顶部对齐.align-items-end底部对齐.align-items-center垂直居中.align-items-baseline按基线对齐适合文字大小不同但希望文字底边对齐的场景.align-items-stretch默认的拉伸align-self的作用对象是单个子元素。它的优先级高于容器上的align-items所以当容器设置了align-items-center你想让某一个子元素保持在顶部就给它单独加.align-self-start。这个覆盖逻辑很直觉也避免了为了一个元素的状态去重写整个容器的对齐方式。4.3 多行场景下的align-contentalign-content只在两种情况同时满足时生效容器设置了flex-wrap: wrap且子元素产生了多行。它控制的是多行之间在交叉轴方向的整体分布你可以把它理解成“行与行之间的justify-content”。Bootstrap 4提供了align-content-start/end/center/between/around/stretch六个值。一个典型场景是标签云区域容器高度固定为300px子标签很多所以换成了三行想让三行整体在垂直方向居中就给容器加.align-content-center。如果没加这个类默认stretch会把每一行都拉高去填满容器视觉效果会很怪。这里有个高频误用很多人以为align-items-center就能让多行整体居中但实际上align-items只影响单行内每个项目在交叉轴上的位置不影响行与行之间的排布。多行场景不加align-content布局效果往往和预期完全相反。4.4 垂直居中三个常用Flex方案对比垂直居中曾经是CSS布局里最著名的难题Bootstrap 4里至少有三条路可以走我按使用频率排个序。方案一容器级别居中。给父容器加.d-flex和.align-items-center再配合.justify-content-center子元素就在水平和垂直方向都居中了。这个方案适合弹窗、空状态提示、上传区域这类“内容不知名高”的容器。方案二网格列内居中。在栅格项目上使用.d-flex.align-items-center让内容在列高度范围内垂直居中。这里需要注意栅格列默认会被拉伸到和同行的兄弟列等高在这个前提下再用align-items-center就能实现“不管左右列多高我的内容始终在中间”的效果。方案三auto margin。给子元素加.m-auto它会在主轴和交叉轴方向同时吃掉空闲空间实现双轴居中。这个写法和方案一效果相似但m-auto属于margin体系在某些需要同时控制间距的场景里更顺手。三条路的共同点是不需要知道内容高度也不需要绝对定位。这在Bootstrap 3时代是无法想象的。5. 响应式Flex的断点哲学sm、md、lg、xl的粒度决策5.1 断点前缀后置的规则Bootstrap 4里几乎所有的Flex工具类都支持响应式变体写法是断点名插在属性关键字后面.d-sm-flex、.justify-content-md-between、.flex-lg-column、.align-items-xl-center。断点档位一共五层从低到高是无前缀xs即小于576px、sm≥576px、md≥768px、lg≥992px、xl≥1200px。这里有个容易忽略的细节无前缀的类作用于所有宽度而带前缀的类只从对应断点开始生效。所以.d-flex.d-md-none的意思是默认一直是flex到md及以上变成none。理解了这个覆盖关系你才能正确地把移动端和桌面端的布局区分开。5.2 不同断点的Flex行为设计我做过的绝大多数后台管理页面Flex布局的断点策略可以归纳成一句话移动端用column桌面端用row。具体说.flex-column.d-md-row这个组合就是最常见的基础布局写法。移动端垂直堆叠桌面端恢复水平排列。但真实项目里总有例外。比如一个数据看板页面五个指标卡片在桌面端要一行全部放下在平板端宽度不够这时候就不该用nowrap强制不换行。正确的做法是给容器加.flex-wrap再控制每个卡片的宽度让Flex自己根据容器宽度决定换行位置。在具体分配断点时我的常用判断标准是看内容的“最低可读宽度”。一个卡片里的文字和留白至少需要多少px才不显得拥挤这个值决定了它在哪个断点换行。比如工具栏按钮一排四个每个按钮最低150px加上间距那么在md768px以下就应该考虑换行或者变成纵向排列。这个判断比死记断点规则靠谱得多。5.3 移动优先从无前缀类写起Bootstrap 4的响应式设计遵循移动优先原则。写类名时先把无前缀的移动端样式写出来然后用带断点的类逐级覆盖。这个顺序本身也是浏览器加载CSS的顺序从源码层面就保证了覆盖逻辑不会被破坏。实践中我会用一个固定的套路先写.d-flex.flex-column定义移动端为纵向布局再写.md:flex-row定义md及以上为横向布局。需要调整间距时先写.mb-3移动端下边距再写.md:mb-0覆盖掉。这样一套下来移动端效果不会因为桌面端样式被意外影响。div classd-flex flex-column flex-md-row div classmb-3 mb-md-0 mr-md-3区块一/div div区块二/div /div这段代码就包含了两个关键动作方向从column到row的切换间距从下边距到右边距的切换。掌握了这个套路绝大多数响应式布局都可以用工具类直接拼出来不需要为几个布局差异单独写媒体查询。6. 实战踩坑Bootstrap 4 Flex项目中高频的五个问题6
返回列表