ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从Flex到Grid:页面布局选型、响应式适配与踩坑实录

从Flex到Grid:页面布局选型、响应式适配与踩坑实录 页面布局这件事说起来像是最基础的活儿但真正在项目里干过几年的人都知道它是最容易让人翻车的地方。设计稿看着干净利落落到代码里却总有一个卡片宽度不对、某一个断点下两栏突然叠在一起、打包上线后左边栏莫名少了两像素。我自己接手过被前任开发者留下的页面一个简单的左右两栏布局用了三层浮动加两个绝对定位改一行间距要调半个小时。所以这篇内容我想聊的不是Flex 怎么写这种查文档就能得到的答案而是从页面设计里的 Layout 布局出发把选型逻辑、实操细节、以及那些文档里不会写的排查经验完整梳理一遍。不管你是刚接触页面设计的新手还是做了几年仍在被布局问题折磨的人应该都能从下面这些内容里捞到点能直接抄走的东西。1. 页面布局到底在解决什么问题1.1 布局的本质是把设计稿的空间关系翻译成约束关系很多人把布局理解成把元素摆到对的位置这个理解只对了一半。设计稿是静态的一张图它描述的是某个固定尺寸下的结果而页面是活的浏览器窗口可以被拉宽到 2560也可以被压缩到 320字号还可能被用户调大。所以布局真正要做的事情是把设计稿里那些看起来该在一起的关系翻译成一组约束让浏览器在任意尺寸下都能自己算出合理的位置。这个视角的转变非常关键。举个具体例子设计稿上左边是一个 240px 的侧边栏右边是主内容区。如果你把它理解为左边 240右边剩下的那你会写出width: 240px加margin-left: 240px这种硬编码一旦项目要求在大屏下侧边栏变成 280px或者在小屏下侧边栏折叠你就要改好几处。但如果你把它理解为侧边栏宽度固定主内容区吃掉剩余空间那就是一句flex: 1的事。我自己习惯在动手写代码前先做一遍关系翻译把设计稿里的区块画成方框标注每个方框和相邻方框的关系——是并列、是嵌套、还是覆盖。并列关系基本就是 Flex 或 Grid 的一行一列嵌套关系是容器的层层包裹覆盖关系才需要考虑定位。这一步做扎实后面写样式几乎是机械劳动这一步偷懒跳过后面就是在用试错法堆 CSS改到哪算哪。还有一点值得强调布局的约束应该尽量交给父容器去描述而不是让每个子元素自己规定自己占多宽。子元素越是自知之明复用性就越差。一个卡片组件在首页要占三分之一宽在列表页要占满如果卡片自己写死宽度你就得加两个修饰类如果是父容器决定卡片只管自己内部长什么样放哪都行。1.2 布局方案的选型逻辑文档流、浮动、Flex、Grid 各自的主场现在能用的布局手段很多但每一种都有它最舒服的场景。我先给一张对照表这是我这些年总结下来的判断标准基本上拿到需求就能往上套。布局手段适用场景主要优势明显短板普通文档流 margin文章类、垂直堆叠的内容零心智负担语义清晰水平排列很勉强间距靠边距易塌陷浮动float图文环绕、老项目兼容文字环绕效果自然需清浮动父容器高度塌陷逻辑反直觉Flex 一维布局导航栏、按钮组、表单行、卡片内部对齐控制强自适应简单二维网格需要嵌套多层Grid 二维布局整页骨架、仪表盘、图片墙行列同时控制无需嵌套老浏览器心智成本auto计算有学习曲线绝对定位角标、遮罩、悬浮按钮精确覆盖脱离文档流尺寸变化易重叠判断的第一步是问自己这是一维还是二维如果元素只在一个方向上排队——一行按钮、一列表单、一排卡片——Flex 是最省事的。如果元素同时受行和列约束比如一个后台仪表盘上面是标题栏左边是侧边菜单右下角是两块统计面板加一块折线图这种用 Grid 一行grid-template-areas就能把整页骨架写清楚可读性远超嵌套三层的 Flex。第二步是问尺寸由谁决定内容驱动文字多长就多宽、容器驱动平均分几份、还是两者混合内容驱动的场景 Flex 更合适因为它天生按内容分配平均分配或者带比例的固定分配Grid 的fr单位更直观。第三步才是兼容性和团队约定。现在还在用浮动做整页布局的项目多数是历史包袱不是技术选型的结果。如果维护老代码改浮动之前先想清楚很可能牵一发动全身我的建议是在外层包一个新的 Flex 容器让浮动区域整体作为 Flex 的一个子项逐步替换而不是一次性重写。2. 核心布局技术拆解Flex 与 Grid 怎么选、怎么用对2.1 Flex 布局的四个关键属性与三个高频误区Flex 的 API 不多但真正用得准的人不多。我把它的属性分成两组容器属性决定子元素怎么排队子项属性决定自己占多大。容器上最常用的四个是display: flex、flex-direction、justify-content、align-items。这里有个新手最容易搞混的点justify-content管的是主轴align-items管的是交叉轴而主轴方向是被flex-direction决定的。也就是说当flex-direction从row改成column时这两个属性的作用方向会整体旋转九十度。我见过不少人在做垂直居中时一行代码没改对方向就开始乱试其实只要记住先定方向再看主交叉问题立刻清晰。子项属性里真正需要吃透的是flex的简写。flex: 1实际上是flex-grow: 1; flex-shrink: 1; flex-basis: 0%。注意最后那个0%它意味着基础尺寸是零空间完全靠 grow 分配所以多个flex: 1的元素会严格等宽哪怕内容长短差很多。而flex: auto是flex-basis: auto基础尺寸按内容算剩余空间再按 grow 分结果就是内容多的更宽。这两个看着像实际表现差很远选错了就是为什么这列比那列宽。再说三个高频误区。第一个是弹性子项默认不收缩到内容以下。Flex 子项的min-width默认值是auto意思是不能小于内容的最小尺寸。这会导致一个经典 bug一行里放两个元素右边放一个长链接或长英文单词窗口缩小时左边被挤爆、右边撑破容器、横线出现。解法就是给需要收缩的那一侧加min-width: 0或者给文本加overflow-wrap: anywhere。这个坑我至少踩过五次每次排查半天现在写响应式布局基本会习惯性加上。第二个误区是用margin做间距而不了解gap。早年用margin-right给最后一个元素也加上边距导致换行后右侧留白也有人用:last-child { margin-right: 0 }结果布局一变又要重写。现在gap支持度已经很好了Flex 和 Grid 都能用直接写gap: 16px不用担心首尾元素也不用处理百分比 margin 的取整误差。第三个是对齐属性用错层级。想让卡片内部内容水平垂直居中justify-content和align-items必须写在卡片这个容器上而不是卡片自己身上。这个错误之所以常见是因为很多人写样式时是在子元素的规则块里编辑写着写着就忘了当前选择器指的是谁。2.2 Grid 布局二维场景的真实用武之地Grid 刚出的时候被吹成终极布局方案后来大家发现日常项目里真正必须用它的场景没那么多。但有几个场景我强烈建议切到 Grid用起来是真的舒服。第一个是整页骨架。用grid-template-areas配合命名区域整个页面的结构可以用几行文本画出来可读性极强.page { display: grid; grid-template-columns: 220px 1fr; grid-template-rows: 56px 1fr auto; grid-template-areas: sidebar header sidebar main sidebar footer; min-height: 100dvh; gap: 0; } .sidebar { grid-area: sidebar; } .header { grid-area: header; } .main { grid-area: main; min-width: 0; } .footer { grid-area: footer; }这段代码的好处是任何接手的人扫一眼就知道页面结构要调整区域位置改字符串就行不用去推演层叠关系。相比之下同样的结构用 Flex 嵌套实现至少需要三层容器而且每层都要处理宽度和高度改一处容易漏一处。第二个是自适应的卡片墙。一行代码搞定响应式列数不需要写任何媒体查询.card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 20px; }auto-fit会自动计算当前宽度能塞下几列minmax(240px, 1fr)保证每列最小 240、最大平分剩余空间。窗口从 320 拉到 1920列数自动从 1 变到 6全程没有断点也不需要 JS。这套写法我几乎每个列表页都会用唯一要注意的是auto-fit和auto-fill的区别当空轨道存在时auto-fit会把空轨道折叠掉让现有元素拉伸填满auto-fill会保留空轨道导致元素保持最小宽度。多数卡片墙场景要的是前者。第三个是显式定位的复杂面板。后台项目里常有那种不规则的面板组合左上角一个大图右上角两个小块下面横贯一条。用 Grid 的grid-column、grid-row手动指定位置比用浮动或者绝对定位稳得多因为元素依然在文档流里容器高度会自动撑开。Grid 有一个需要提醒的细节1fr不是剩余空间的百分之多少它是按比例分配的弹性单位。当fr和固定尺寸混用时fr分的是减去固定尺寸和 gap 之后的空间。另外fr的最小尺寸默认受内容影响遇到长内容同样会撑破需要配合minmax(0, 1fr)来约束。2.3 两者混用的实战组合真实项目里很少纯用一种。我常用的分工是Grid 定骨架Flex 管内部。外层用 Grid 把页面切成几个稳定的区域每个区域内部再用 Flex 处理一行按钮、一个表单、一组标签。这样职责清晰改骨架不用碰内部样式改内部也不用担心影响整体结构。举个具体的登录页例子。整页用 Grid 居中一个卡片卡片内部用 Flex 纵向排列表单行内部再用 Flex 横向排列输入框和按钮。三层容器三层职责每层代码都很短。反过来如果全用 Flex 做外层居中你需要在body上设高度、在中间层设flex: 1、再在卡片上设对齐链路更长更容易出错。还有一个小技巧别怕多包一层容器。很多布局难题本质上是没有足够的容器来承载约束。想给主内容区单独设置滚动又不想让侧边栏跟着滚就需要把主内容区包成一个独立的容器给它overflow: auto和min-height: 0。这层容器不承担任何视觉职责纯粹是为了让约束有个落点。新手常犯的毛病是想在一个 div 上同时干三件事结果哪件都干不好。3. 从设计稿到代码一次完整页面布局实操3.1 拆解设计稿先划分区块再确定断点假设我拿到一张常见的资讯首页设计稿顶部是一条横向导航左边 logo 右边菜单下面是一个横幅区左边大图右边三条热点再下面是内容区左侧主列表右侧推荐栏最底部是版权信息。宽屏 1440 设计同时要求适配到 375 的手机。我第一步不写代码而是先画区块图。把页面拆成五个横向带导航、横幅、内容、页脚内容区内部再分成主列表和推荐栏。这个拆分方式决定了代码结构五个同级的 section 纵向堆叠这是文档流天然就能搞定的不需要任何布局技术。这就是选型的第一层判断——能用文档流解决的绝不引入布局方案。第二步是确定断点。这一步很多人凭感觉我觉得应该看内容什么时候撑不住而不是看设备型号。具体做法是把浏览器窗口从 1440 慢慢往窄拉盯住两处导航菜单什么时候开始挤压 logo内容区两栏什么时候变得难以阅读。这两个临界点就是断点位置。按这个设计稿实测下来大概在 1024 附近导航需要折叠在 768 附近内容区需要变成单栏。至于 375 这种窄屏不需要单独的断点靠流式宽度自适应就够了。断点数量也要控制。我见过一个页面写了七个媒体查询因为每改一次需求就加一个断点最后互相干扰。我的做法是控制在三个以内分别对应大屏、平板、手机中间状态靠minmax、clamp和百分比自动过渡。3.2 骨架搭建语义化结构与容器划分骨架阶段我坚持用语义化标签这不是纯粹为了好看。header、nav、main、aside、footer这些标签在无障碍访问和 SEO 上是有实际收益的而且它们天然表达了区块之间的关系后来接手的人看结构就能理解意图。body header classsite-header a classlogo href/站点名/a nav classsite-nav aria-label主导航 ul classnav-list lia href/news资讯/a/li lia href/docs文档/a/li lia href/about关于/a/li /ul /nav button classnav-toggle aria-label展开菜单菜单/button /header section classbanner div classbanner-main/div ul classbanner-side/ul /section main classcontent section classarticle-list/section aside classrecommend/aside /main footer classsite-footer版权信息/footer /body注意这里没有任何布局相关的 class骨架阶段我只关心有哪些块。先结构后样式好处是当视觉方案调整时你只需要换 CSSHTML 不用动。反过来如果一开始就把布局类和结构类混在一起写后期改版会非常痛苦。3.3 样式实现从整体到局部的书写顺序写样式我有固定的顺序先重置和变量再整体骨架再逐个区块最后响应式。这个顺序让每个阶段都有明确的检查点。:root { --gap: 20px; --sidebar-w: 300px; --content-max: 1200px; --header-h: 60px; } * { box-sizing: border-box; } body { margin: 0; font-family: system-ui, -apple-system, Segoe UI, sans-serif; line-height: 1.6; } .content { display: grid; grid-template-columns: 1fr var(--sidebar-w); gap: var(--gap); max-width: var(--content-max); margin: 0 auto; padding: var(--gap); } .site-header { display: flex; align-items: center; gap: 24px; height: var(--header-h); padding: 0 var(--gap); position: sticky; top: 0; z-index: 10; } .nav-list { display: flex; gap: 20px; list-style: none; margin: 0; padding: 0; }这里的几个决定值得说清楚。box-sizing: border-box是必设的它让padding算在宽度之内否则你写width: 100%加padding: 20px会直接撑破容器。max-width配margin: 0 auto做居中比用position: absolute或者 Flex 居中更稳因为它不依赖父容器高度。--sidebar-w用变量存起来是因为它是后面媒体查询里要改的值用变量能保证只有一处需要改。min-width: 0在.content的子项上该加就加新项目我一般会写一条全局规则来兜底长内容溢出。横幅区我通常用 Grid.banner { display: grid; grid-template-columns: 2fr 1fr; gap: var(--gap); max-width: var(--content-max); margin: var(--gap) auto; padding: 0 var(--gap); }用2fr 1fr而不是固定像素是因为横幅图片本身是自适应的固定宽度会在中等尺寸下比例失衡。3.4 响应式适配媒体查询与流式单位的配合响应式这块我的原则是能用流式解决的绝不用媒体查询。字号用clamp()容器宽度用百分比或minmax间距用变量加相对单位。真正需要媒体查询的只有两件事结构变化和显隐切换。media (max-width: 1024px) { .banner { grid-template-columns: 1fr; } .banner-side { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--gap); } } media (max-width: 768px) { .site-nav { display: none; } .nav-toggle { display: inline-flex; margin-left: auto; } .content { grid-template-columns: 1fr; } :root { --gap: 16px; } }注意我在 768 断点里改的是--gap这个变量而不是各个组件的间距所有用到var(--gap)的地方会自动跟着变。这是用 CSS 变量做响应式的一个实用技巧能省掉大量重复的媒体查询。字号也可以用同样的思路.banner-title { font-size: clamp(1.25rem, 2.5vw, 2rem); }clamp的三个参数是最小值、理想值、最大值在 375 到 1440 之间它会平滑过渡完全不需要断点。我实测下来标题类的字号用clamp效果比媒体查询更自然因为不会有那种过了一个点突然变大的跳动感。还有一个经验移动端不要用100vh。在移动浏览器上100vh是不含地址栏高度的导致全屏区域底部被裁掉出现多余的滚动。现在的做法是用100dvh动态视口单位会跟随地址栏变化兼容性已经覆盖主流设备。如果必须支持老旧浏览器退路是用 JS 把真实高度写进一个 CSS 变量。另外底部固定按钮要记得加env(safe-area-inset-bottom)否则在带手势条的设备上会被挡住。4. 布局踩坑实录那些让人熬夜的诡异问题4.1 打包后布局变了样优先怀疑样式加载顺序这个问题我自己遇到过两次同事也问过我三次症状高度一致本地开发一切正常构建部署之后布局错位打开控制台看样式发现某个元素的宽度被覆写了。根因基本都落在样式加载顺序上。开发环境下样式可能通过不同的入口引入——有的是组件内部的style块有的是外链的 UI 库有的是import进来的全局样式文件。构建工具在生产环境下会对 CSS 做合并、压缩、抽离合并后的顺序不一定和开发时一致。而布局样式往往依赖后面的覆盖前面的这个隐含前提顺序一变覆盖关系就反了。我的处理原则是布局样式不要依赖加载顺序。具体做法有几条。第一不要把优先级寄托在谁写在后面而是用具体的选择器把意图表达清楚比如.card .title而不是.title。第二避免在同一优先级下靠顺序决胜如果两个规则可能冲突就说明设计上有歧义应该合并或者用明确的作用域区分。第三UI 库的全局重置样式要谨慎很多库自带的box-sizing、line-height、margin重置会影响到你自己的组件引入前先确认它做了哪些全局改动。第四构建配置里如果能固定 CSS 的合并顺序就固定下来别让它随文件引入顺序随机变化。排查这类问题有个笨办法但很好用把出问题的元素选中在开发者工具里看它的宽度是从哪条规则来的逐条往上翻对比开发环境和生产环境的规则列表差异。差异点就是答案通常不需要猜。4.2 元素重叠与溢出三个高频原因布局重叠的报错很少见因为它不报错就是视觉上不对。我总结下来三个高频原因。第一是绝对定位的父元素没有定位。子元素写position: absolute; top: 0; left: 0本意是相对于卡片定位但卡片没写position: relative结果子元素相对整个页面定位跑到左上角去了看着就像元素消失了或者重叠了。这个错误的隐蔽性在于页面首次加载时可能看着正常滚动之后才暴露。我的习惯是给任何可能出现绝对定位子元素的容器都加上position: relative即使当前没有子元素用到。第二是 Grid 显式定位到同一格。手动写grid-column: 1 / 2时如果两个元素都指定了同一格它们会叠在一起而且不会有任何警告。排查方法是在开发者工具里打开 Grid 高亮轨道和区域一目了然。第三是负边距的滥用。为了挪动元素写margin-top: -20px在小屏下元素本身高度变小负边距就把它顶到了上一个元素身上。负边距不是不能用但每次用都要想清楚如果这个元素高度变化了会怎样我更倾向于用transform: translateY()因为它不影响其他元素的排布。溢出问题同样值得说。横向滚动条突然出现第一个要查的是有没有元素超出了容器宽度。最容易超的是固定宽度的元素、padding加在width: 100%上的元素、以及一个不换行的长文本。快速定位的方法是给所有元素加一个临时的outline: 1px solid red超出容器的那个立刻现形。另外轮播、跑马灯这类组件经常用translateX移动元素如果父容器没写overflow: hidden同样会撑出横向滚动。4.3 移动端 H5 的专属坑视口、安全区与 1 像素移动端 H5 的布局问题有自己的特点因为视口会动。第一个是前面提过的100vh。除了用100dvh还有一个细节是键盘弹出时会改变视口高度如果页面用了height: 100vh加上底部输入框键盘一弹出可能把输入框顶出屏幕。这类页面我一般用 Flex 布局让输入框区域固定在底部内容区flex: 1加overflow: auto键盘弹出时浏览器会自动调整布局不会崩。第二个是安全区。全面屏设备底部有手势条固定在底部的按钮如果不加内边距会被遮挡。正确写法是在底部操作栏上加padding-bottom: env(safe-area-inset-bottom)同时配合viewport-fitcover的 meta 设置这个设置是让页面延伸到屏幕边缘的前提缺了它env()会返回零。第三个是 1 像素边框在部分高密度屏上显示不一致。这类问题的根源是设备像素比不同解决思路是用transform: scaleY(0.5)加伪元素或者干脆改成用背景渐变做细线。说实话现在很多设计已经不太纠结 1px 边框的绝对精确了如果视觉能接受用普通边框省事得多。4.4 布局问题速查表我把这些年问得最多的现象整理成一张表遇到类似的直接按现象查。现象最可能的原因处理方向弹性子项被内容撑破容器子项min-width默认auto加min-width: 0或overflow: hidden图片下方多出几像素空白img默认inline基线对齐留隙设display: block子元素绝对定位后跑到页面左上角父元素未设position: relative给父容器加相对定位出现横向滚动条某元素宽度超出容器临时描边定位检查固定宽度和长文本两栏在中等宽度下挤成一团缺少中间断点或auto-fit未设最小值补断点或改用minmax换了页面滚动条消失导致整体偏移有无滚动条时视口宽度不同scrollbar-gutter: stable卡片墙最后一行元素被拉得特别宽auto-fit折叠空轨道后拉伸改auto-fill或限制最大宽度隐藏元素后布局跳动用了display: none改变了参与布局的元素数需要占位就改visibility: hidden排查布局问题的通用思路是先看盒模型再看约束关系最后看优先级。开发者工具的盒模型面板能告诉你元素的真实内容宽度、内边距和外边距很多疑惑在这一步就解开了。5. 布局思维的迁移从页面到其他载体5.1 桌面端与后台面板的容器思路我在做一些内部的桌面工具或者后台面板时发现布局的思考方式和 Web 页面高度相似只是 API 换了名字。后台面板的典型结构——顶部工具栏、左侧导航、右侧内容、底部状态栏——本质上和前面讲的 Grid 骨架是同一个问题谁固定、谁伸缩、谁滚动。Qt 里的做法很能说明问题。常见的坑是直接把布局设置到主窗口上结果主窗口的菜单栏、工具栏和中心内容抢空间。正确的做法是新建一个独立的QWidget作为中心部件把布局设置到这个中心部件上再把它设为窗口的 central widget。这样工具栏和状态栏各自管自己中心区域的布局只负责内容划分职责就分开了。这个思路和 Web 里给滚动区域单独包一层容器是完全一致的都是为了让约束有明确的归属。后台面板里还有一类布局特别常见就是分栏可拖拽的布局。左侧宽度可由用户拖动调整右侧自适应。实现上通常是监听拖动事件把新的宽度写进一个状态左侧绑这个宽度右侧用剩余空间。这里有个容易忽略的点拖动过程中要限制最小宽度和最大宽度否则用户可以把某一栏拖到零宽布局直接不可用。另外宽度值一旦持久化到本地存储就要考虑用户换设备或者换分辨率后的兼容我的做法是不存绝对像素而是存百分比并且在恢复时做一次范围校验。5.2 版图与物理布局里的约束思维Layout 这个词在工程领域不只指页面。做硬件的人说 layout指的是元件在板子上的摆放和走线做工业设计的人说 plant layout指的是设备在厂房里的排布。这些场景跟我讲的页面布局看似毫不相干但底层逻辑是同一套在有限的边界内分配空间同时满足一组相互冲突的约束。硬件布局里的约束通常是电气性能、散热、可制造性页面布局里的约束是可用性、可维护性、适配范围。有意思的是两边的经验都能互相借鉴。硬件里讲究先放关键器件再补周边页面布局也一样先把影响结构的几个大块定下来小的装饰元素最后调。硬件里会关注走线不要交叉太多页面则对应层级不要嵌套太深。硬件里做布局会留出工艺余量页面则对应给文字留出换行余量和字号放大的空间。我以前做的一个后台项目右侧信息栏的内容长度完全不可控短的只有两行长的能到几十行。后来发现把它当成不定高内容区来设计比强行指定高度靠谱得多外层 Grid 的行高用auto滚动交给整个主内容区信息栏自己不滚动。这样无论内容多长页面结构都不会被撑变形。这个思路其实和电路布局里不对不确定负载做硬性空间分配的处理方式很像都是给不确定性留出弹性。5.3 让布局可维护命名约定与结构收敛布局写完能跑只是及格三个月后还能改才是及格线以上。我这些年坚持几个习惯收益很大。一是布局类命名按角色而不是外观。.content、.sidebar、.toolbar表达的是职责.left-box、.gray-area表达的是外观。外观会变职责相对稳定。项目改版时前者不用改名后者改一次要全局搜索替换还容易漏。二是间距统一走变量。整个项目的间距只用四到五个值比如 4、8、16、24、32全部写成变量。这样既避免了这个间距是 17px 那个是 18px的随意感也让整体节奏更稳。设计师临时说这里再紧一点你只需要改一个变量或者换一个档位不用去算像素。三是控制嵌套层数。我给自己定的上限是四层超过就想办法合并。嵌套过深的直接后果是样式优先级越来越复杂你想改一个深层元素发现要写一长串选择器改完还可能影响到别的地方。层数少的结构任何一处修改的影响面都是可预测的。四是给关键结构写注释。不是每行都写而是在区块开始处写一句这一层负责滚动、这里是两栏骨架。半年后的自己会感谢现在的自己新接手的同事更是如此。再分享一个我自己的小习惯每个页面做完之后我会把窗口从最宽拉到最窄来回拉十次专门看有没有跳动、有没有文字挤在一起、有没有滚动条突然出现。这个动作花不了两分钟但能发现八成的响应式问题。比写一堆自动化测试实在因为很多布局异常是靠视觉判断的脚本很难覆盖。布局这个东西工具能帮你的有限最后还是要靠对约束关系的清醒认识以及对细节的耐心。
返回列表