简介:小程序布局微信案例.zip是一份面向微信小程序初学者的布局实战资源,围绕WXML与WXSS讲解页面结构搭建与样式设计,并通过完整案例演示布局方法,覆盖flex、grid、rpx、vw、vh等布局与单位技巧,以及定位浮动、组件化结构、响应式设计与性能优化等核心知识点。压缩包仅79KB,共33个文件,包含20个png图片、5个json配置、3个js逻辑、3个wxss样式、2个wxml页面;wxml定义结构、wxss控制样式、js承载逻辑、json进行配置,分工清晰,整体轻量,可直接导入微信开发者工具运行。已有381人学习下载,适合作为入门练手与课堂配套案例。资源目录结构清晰,包含钱包、首页等典型页面场景,可对照学习数据绑定与生命周期管理,理解scroll-view、wx:if等内置组件的实际用法,并借鉴其组件拆分和样式组织方式。完成学习后,可将其中的布局技巧迁移到个人小程序项目中,有效提升界面的适配与落地效率。
1. 小程序布局微信案例.zip:别把它当模板,把它当布局思路的索引
接这种压缩包的第一反应是捡了便宜——有现成的微信小程序布局案例,解压后直接改改就能上。真等动手你会发现,“案例”两个字既是参考也是坑:参考在于 rpx 换算、flex 排版、自定义导航、列表加载更多这些高频布局确实有通行写法,抄下来能少走弯路;坑在于案例里的页面和业务耦合得很深,直接搬过来经常遇到样式冲突、滚动错乱、安全区被遮挡。这篇笔记我按自己拆这类压缩包的习惯来写,先讲清楚小程序页面布局的底层规则,再按导航栏、双栏、加载更多三个高频场景拆代码,然后把我在真机上反复翻车又修好的几类问题列出来,最后给你一份能直接用的布局自检清单,照着它能少改一版 UI。
2. 小程序布局的地基:rpx 换算、flex 写法和页面滚动边界
2.1 rpx 不是玄学:750 是屏幕宽度,不是设计稿宽度
rpx 可以理解为“响应式像素”,微信小程序给它的定义是:不管什么屏幕,屏幕总宽永远等于 750rpx。也就是说,rpx 本质上是一个百分比单位——1rpx 等于屏幕宽度的 1/750。这个设计解决了 H5 里百分比和 px 混用的麻烦:你拿到 750px 宽的设计稿,稿子上写多少,代码里就写多少,不用手动换算。
但如果设计稿是 375px 宽(iPhone 6/7/8 的切图标准),换算规则是 1px 等于 2rpx,写样式时把稿子上的 px 数值乘以 2。比如一个按钮宽度 120px,代码里写 240rpx。换算逻辑和 CSS 里的 vw 非常像:750rpx 约等于 100vw,每 1rpx 相当于 0.1333vw。这里要注意,rpx 换算出来的实际像素随屏幕宽度变化,和 dpr(设备像素比)没有直接关系,很多人在这一步把 rpx 和物理像素搞混,导致在小屏上尺寸偏大。
真正容易踩坑的是 rpx 的边界条件,三条血泪经验:
第一,边框和 1px 分隔线不要用 rpx。rpx 放大会导致 1rpx 在某些安卓机上小于物理像素,细线直接显示不出来。我一般用小程序的border固定 1px,或者用.hairline工具类。
第二,横屏和平板场景下 rpx 会失真。屏幕宽度一大,750rpx 仍然铺满全宽,但里面的头像、间距、字号全部同比例放大,视觉效果会被拉得离谱。iPad 上打开一个 rpx 布局的商城页面,商品卡片看着像放大了 1.5 倍。
第三,rpx 不能用在 JS 的getSystemInfoSync()返回值里,也不能在行内 style 做动态拼接时直接当数字用。比如style="width: {{percent * 750 / 100}}rpx"这种写法,percent一变化,宽度也跟着变,但如果你在小程序里用wx.createSelectorQuery()去量这个元素,拿回来的宽度是像素值,不是 rpx,拿它去和别的值做运算就会对不上。
看一段最常用的根布局写法:
/* app.wxss —— 全局页面骨架 */ .page { width: 750rpx; min-height: 100vh; margin: 0 auto; padding: 24rpx; box-sizing: border-box; background: #f7f8fa; }这里的min-height: 100vh是小程序页面标准写法,vh 在 WebView 里被支持,能保证页面把安全高度撑开。配合box-sizing: border-box,padding 不会把元素本身撑破。rpx 用在宽度、间距、字号上都没问题,唯独边框和某些需要绝对尺寸的场景要回到 px。
说得更直白一点,rpx 带来的是“设计稿到代码的免换算”,代价是“所有尺寸都跟着屏幕走”。如果做的是带复杂交互的页面,建议局部混用:容器级尺寸用 rpx,图标和头像大小用 px 固定。这不是不规范,而是几个项目调下来后的选择。
2.2 flex 在小程序里并不“半残”:搞懂 flex-shrink 和 min-width 就够
Flex 在小程序里的支持度比很多教程描述的要好,日常用的 flex-direction、flex-wrap、justify-content、align-items 都正常,甚至在 Android 上的表现比 iOS WebView 更稳定。真正容易翻车的是 flex-shrink 的默认行为。
Flex 子项默认flex-shrink: 1,意思是容器宽度不够时子项会被压缩。很多人以为写了flex: 1就代表“占满剩余空间”,但没意识到剩余空间的计算受min-width影响——子项的 min-width 默认是 auto,也就是内容自身的最小宽度。当一个带长文本的 view 放进 flex 容器时,即使容器宽度固定,这个 view 也可能因为内容“撑”得比预期宽,要么导致溢出,要么压缩到看不见。
实际项目中我遇到最多的布局 bug:左右两栏,左栏固定 200rpx,右栏flex: 1,结果右栏里的长标题把右栏撑开,整个两栏布局崩了。解决方法是给右栏加一行min-width: 0,让 flex 容器忽略内容的“最小宽度约束”去真正分配空间。
/* 典型两栏布局:左窄右宽 */ .two-col { display: flex; width: 100%; flex-direction: row; align-items: stretch; } .two-col__left { flex: 0 0 200rpx; min-width: 0; } .two-col__right { flex: 1; min-width: 0; /* 这一行不加,长文本会把右栏撑破 */ padding-left: 24rpx; }flex: 0 0 200rpx表示不放大、不压缩、基础宽度 200rpx,这种写法比直接写width: 200rpx更稳,因为宽度语义更明确,不会受其它 flex 属性干扰。右栏的flex: 1等价于flex: 1 1 0%,意思是剩余空间全给右栏,但前提是 min-width 为 0,否则内容的最小宽度会覆盖 flex 的分配逻辑。
小程序里 flex 另一个需要小心的场景是竖向排列。微信小程序的 page 默认是纵向滚动,但 flex 容器默认不滚动。如果你在 page 里放了一个display: flex; flex-direction: column;的 view,并且内容超高,它会被 page 撑开而不是在容器内滚动。正确做法是让容器高度为 100vh,内部再加 scroll-view,避免页面级滚动和容器级滚动互相打架。
常见误用是用 flex 做动态栅格。栅格需要“固定间距 + 等分子项”,flex 可以做到,但只能控制子项数量固定的情况。数据动态变化时,flex 换行会出现最后一行只有一两个子项,左对齐视觉上很难看。这种场景我一般改用 CSS Grid 或者 flex + 百分比宽度配合。
2.3 页面滚动 vs scroll-view:什么时候必须用 scroll-view,什么时候绝对别用
小程序的滚动有两套体系,很多从 H5 转过来的开发者容易在这里懵。
页面级滚动是默认行为。page 本身就是一个滚动容器,用户滑到底部时触发onReachBottom。这套体系最简单,不需要写任何样式。绝大多数列表页,只要设置好padding-bottom和onReachBottom,就能完成下滑加载更多。
scroll-view 是显式声明“这个区域自己滚动”。它需要指定高度,不然内容会无限撑开。常用的写法是给 scroll-view 设置height: calc(100vh - 导航栏高度 - tab栏高度),然后把scroll-y设为 true。
什么时候必须用 scroll-view?三种场景。第一,左右滑动,比如 tab 横向切换,scroll-x 必须有显式高度;第二,局部列表,一个页面里同时存在两个独立的滚动列表,比如分类页左侧分类 + 右侧商品流;第三,吸顶效果,吸顶导航需要与页面滚动协调,用 scroll-view 可以控制滚动的触发频率并避免 onReachBottom 误触发。
什么时候绝对别用?页面里只有一个列表到底的情况。套 scroll-view 会带来一系列连锁问题:页面级滚动失效、加载更多触发要自己处理、滚动穿透要额外拦截,复杂度指数上升。我见过同事把整个商城首页包在一个 scroll-view 里,结果 onReachBottom 永远不触发,排查了两个小时。
<scroll-view scroll-y style="height: {{scrollHeight}}px;" bindscrolltolower="loadMore" enhanced show-scrollbar="{{false}}"> <block wx:for="{{list}}" wx:key="id"> <view class="list-item">{{item.title}}</view> </block> <view wx:if="{{loading}}" class="loading-tip">加载中…</view> </scroll-view>enhanced是微信基础库 2.10.0 之后提供的属性,解决 scroll-view 在 iOS 上滚动不流畅和回弹不一致的问题。show-scrollbar设置为 false 可以隐藏滚动条,配合scroll-with-animation可以获得更接近原生的滚动体验。核心是 scrollHeight 必须由 JS 计算出来,不能写 100%,因为父容器没有确定高度时,100% 会解析成 0,列表直接不显示。
scroll-view 里一旦用了height: 100%,在大多数情况下都会黑屏——父容器高度没有被撑开,100% 解析为 0。这是模板包里最常见的头号坑之一。正确做法永远是给 scroll-view 一个确定的像素高度值,或者用 calc 计算。
3. 案例里最值得抄的三个布局结构:自定义导航、双栏商品流、加载更多
3.1 自定义导航栏:胶囊按钮的高度不是玄学,量出来是 48px
微信小程序的默认导航栏是系统提供的,只能用navigationBarTitleText改标题文字。想放搜索框、自定义背景或者品牌元素,就得设置"navigationStyle": "custom"并自己写导航栏。
自定义导航栏的核心问题是高度计算。这个高度由三部分组成:状态栏高度(电池条)+ 胶囊按钮与状态栏的间距 + 胶囊按钮本身的高度。胶囊按钮的几何信息可以直接从 API 拿:
// utils/nav.js function getNavInfo() { const menuButton = wx.getMenuButtonBoundingClientRect(); const system = wx.getSystemInfoSync(); const statusBarHeight = system.statusBarHeight || 20; const navBarHeight = (menuButton.top - statusBarHeight) * 2 + menuButton.height; return { statusBarHeight, navBarHeight, menuButton, totalHeight: statusBarHeight + navBarHeight }; } module.exports = { getNavInfo };公式逻辑:胶囊按钮的 top 减去状态栏高度,得到按钮距离状态栏底部的高度,再乘以 2,加上按钮自身高度。乘 2 是经验公式,因为 iOS 和 Android 上胶囊按钮与状态栏的间距不一样,乘 2 可以让导航栏内容中心和胶囊垂直居中。
拿到的值挂到全局,页面里直接引用:
// app.js App({ globalData: { navInfo: null }, onLaunch() { this.globalData.navInfo = getNavInfo(); } });<view class="custom-nav" style="padding-top: {{navInfo.statusBarHeight}}px;"> <view class="custom-nav__bar" style="height: {{navInfo.navBarHeight}}px;"> <view class="custom-nav__title">商品列表</view> </view> </view>.custom-nav { position: fixed; top: 0; left: 0; right: 0; z-index: 999; background: #fff; border-bottom: 1rpx solid #eee; } .custom-nav__bar { display: flex; align-items: center; justify-content: center; }这段做完,导航的高度就和原生保持了一致。需要做刘海屏适配时,wx.getSystemInfoSync().safeArea可以用来做进一步的顶部和底部边距调整。
这里有一个必须注意的地方:胶囊按钮在不同基础库版本下返回的高度不同,iOS 上一般是 32px,Android 上有时是 32px 有时是 48px。所以不能用写死的高度,必须通过 API 动态取。这也是为什么我说“量出来是 48px”但不建议直接写在样式里——它不是一个固定值,而是一个由三个变量决定的动态值。
3.2 左右两栏布局:什么样的内容适合双栏,代码要怎么组织
两栏布局是小程序里最常用的信息架构,典型形态有左侧分类列表 + 右侧商品详情、左文右图的信息流、左侧标签 + 右侧内容。三种情况的写法差别很大。
分类 + 内容的双栏里,左侧通常是一个纵向 tab 列表,右侧是内容的楼层列表。这种场景的关键是左右两侧滚动独立,一般用两个 scroll-view 实现:
<view class="category-layout"> <scroll-view class="category-side" scroll-y style="height: {{listHeight}}px;"> <view wx:for="{{categories}}" class="category-item {{activeIndex === index ? 'active' : ''}}"> {{item.name}} </view> </scroll-view> <scroll-view class="category-main" scroll-y style="height: {{listHeight}}px;"> <block wx:for="{{products}}" wx:key="id"> <view class="product-item">{{item.name}}</view> </block> </scroll-view> </view>对应样式:
.category-layout { display: flex; flex-direction: row; height: 100vh; } .category-side { width: 180rpx; flex-shrink: 0; } .category-main { flex: 1; min-width: 0; }高度 100vh 配合两个 scroll-view,实现类似原生 app 的左右联动滚动。关键细节:左右两个滚动容器独立,点击左侧分类时,右侧需要设置scroll-top参数来切换位置。这个参数不设置的话,右侧滚动位置不会自动归位。
商品流双栏又是另一种做法。商品数据动态变化,通常用 flex + 百分比宽度:
.goods-grid { display: flex; flex-wrap: wrap; justify-content: space-between; } .goods-cell { width: 325rpx; margin-bottom: 20rpx; padding: 16rpx; background: #fff; border-radius: 12rpx; box-sizing: border-box; }justify-content: space-between处理两栏间距,但子项数量为奇数时最后一行只有一个单元格,靠左对齐。希望居中就得给 :last-child 特殊处理。微信小程序的 WXSS 已经支持 CSS Grid,写法会更干净:
.goods-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20rpx; }Grid 的好处:gap 语法简短,不需要在每一项上单独写 padding 和 margin,自动处理奇数对齐问题。如果基础库版本在 2.10.0 以上,强烈建议用 Grid 替代 flex + percentage 的老套路。不过 Grid 做等分时,如果设计稿要求“中间留白 + 两端不留白”,还是得回到 flex + margin 方案。
3.3 加载更多:页面级触发还是 scroll-view 触发,触底逻辑要写在哪个事件里
加载更多有两个触发入口:页面级onReachBottom和 scroll-view 的bindscrolltolower。两个入口的触发条件完全不同,写错的话会出现:页面高度只有一屏时,onReachBottom 不触发;scroll-view 高度设错时,bindscrolltolower 天天误触。
页面级加载更多是 page 滚动到距底部一定距离时触发。这个距离默认 50px,可以通过onReachBottomDistance调整:
{ "window": { "onReachBottomDistance": 100 } }Page({ data: { list: [], loading: false, hasMore: true, page: 1 }, onReachBottom() { if (this.data.loading || !this.data.hasMore) return; this.fetchNextPage(); }, fetchNextPage() { this.setData({ loading: true }); api.getList(this.data.page + 1).then((res) => { this.setData({ list: this.data.list.concat(res.list), page: res.page, hasMore: res.hasMore, loading: false }); }); } });这里的loading标志必须放在请求发起前,不能放在回调里。否则快速滑动时 onReachBottom 会连续触发多次,每次都发请求,造成重复数据。我真实遇到过一次滑动底部连续发出 5 个请求。
scroll-view 场景用不同事件:
<scroll-view scroll-y lower-threshold="100" bindscrolltolower="onScrollToLower" style="height: 100vh;"> <!-- 内容 --> </scroll-view>Page({ onScrollToLower() { if (this.data.loading) return; this.loadMore(); } });lower-threshold控制触底距离,默认 50px,设成 100 可以提前触发。tab 切换的页面建议每个 tab 独立成 scroll-view,让触底逻辑封闭在自己作用域内。写在一个页面里容易串信号,A 列表触底把 B 列表的数据也加载出来了。
4. 把案例 zip 跑起来并改成自己的页面:最小可执行路径
4.1 拿到 zip 后的第一步:检查 app.json 和页面的注册关系
解压之后先不要急着改 UI,先看 app.json。小程序的入口是 app.json,而不是 index.html,这和网页完全不同。app.json 里的 pages 数组决定哪些页面存在、入口是哪一个,window 字段控制全局窗口样式。
{ "pages": [ "pages/index/index", "pages/list/list", "pages/detail/detail" ], "window": { "navigationStyle": "custom", "navigationBarTitleText": "案例", "backgroundColor": "#f7f8fa", "onReachBottomDistance": 100 } }一个隐蔽问题:如果 pages 数组里第一个页面不是你想要的入口页面,编译后打开的是数组第一个页面。很多人把整个 zip 导入开发者工具后找不到入口,就是因为这个。我的习惯是先确认 pages[0],再把“首页”放到第一位。
另一个常见问题:使用了navigationStyle: custom之后,默认导航栏就被干掉了,页面顶部直接是状态栏。如果案例里没有实现自定义导航,页面会完全没有导航栏。初次接这类模板时,建议全局搜索navigationStyle,确认有没有配套的自定义导航组件。
zip 解压后还有一件事要做:确认根目录有project.config.json,这是微信开发者工具识别项目的关键文件。没有它的话,导入时会提示“不是有效小程序项目”,要手动指定 appid 和基础库版本。
4.2 替换主题色和间距体系:用全局变量还是写一套工具类
布局案例的样式通常写得很主观,主色调、圆角、间距都有特定设计。想快速改成自己的风格,我一般用两种方式。
第一种是 WXSS 的 CSS 变量。在 app.wxss 里定义全局变量:
page { --primary-color: #07c160; --bg-color: #f7f8fa; --text-color: #1f2329; --radius-lg: 24rpx; --spacing-page: 24rpx; }页面里通过var(--primary-color)引用。CSS 变量可以穿透自定义组件吗?基础库 2.10.0 之后可以,更早的版本里自定义组件内的样式是隔离的,CSS 变量会被隔离,必须在组件内部重新定义。这个坑在旧项目里很常见,升级基础库后问题自然消失。
第二种是写一套工具类,把高频的间距、字号、圆角封装好:
/* app.wxss 工具类 */ .mt-8 { margin-top: 8rpx; } .mt-16 { margin-top: 16rpx; } .px-24 { padding-left: 24rpx; padding-right: 24rpx; } .radius-md { border-radius: 16rpx; }工具类的问题是类名散,改的时候要满项目找。我的建议:只换颜色用 CSS 变量;整个设计体系要重做时,先提取间距、圆角、阴影这套“设计令牌”,用全局搜索替换。
4.3 哪些自定义组件要保留,哪些直接删掉
案例 zip 里通常带着自定义导航、toast、弹窗、下拉刷新、骨架屏这些组件。接手时按三个标准判断。
组件内部依赖了请求函数或固定接口数据,保留时要改接口地址,否则功能不可用。纯粹的展示组件,比如商品卡片、空状态、倒计时,保留并复用。组件有性能问题,比如用了大量wx:if嵌套或频繁 setData,及时删除用原生 view 重写。
自定义组件在小程序里有性能门槛。每个自定义组件实例都会触发一次 setData 的更新生命周期,组件数量多时页面初始化明显变慢。我的一般处理原则是:一屏内自定义组件不超过 15 个实例,超过这个数量就考虑用模板标签template替代:
<import src="../templates/product-item.wxml" /> <template is="product-item" data="{{item}}" />模板标签比自定义组件轻量,但牺牲了组件独立性和样式隔离。取舍标准:该区块在多个页面都用,用组件;只在本页面用且不含交互逻辑,用模板标签能获得更好的首屏性能。
5. 小程序布局避坑指南:五个翻车现场与排查指令
5.1 现象:iPhone 底部按钮被 Home 条遮挡
原因:没有把env(safe-area-inset-bottom)算进布局。iPhone X 之后的机型屏幕底部有 home indicator,自定义 tab 栏或吸底按钮高度不够时,按钮被遮住。
解决:
.bottom-action { padding-bottom: constant(safe-area-inset-bottom); /* iOS 11 写法 */ padding-bottom: env(safe-area-inset-bottom); /* iOS 11.2+ 写法 */ background: #fff; }constant()和env()两个都要写,顺序不能反。如果使用原生 tabBar,app.json 里设置"tabBar"会自动处理安全区;自定义 tab 就必须手动补 padding。
5.2 现象:输入框弹起后 fixed 输入框被顶上去,甚至和键盘重叠
原因:input 聚焦时键盘弹出会引起 viewport 变化,fixed 定位元素会被重绘影响,产生位置错乱。
解决:
<input class="comment-input" adjust-position focus="{{inputFocus}}" bindfocus="onInputFocus" bindblur="onInputBlur" />Page({ onInputFocus() { this.setData({ inputFocus: true }); }, onInputBlur() { this.setData({ inputFocus: false }); } });adjust-position是 input 组件自带的属性,默认 true,让键盘自动推起页面。另一个辅助做法是设置cursor-spacing,控制输入框底部与键盘顶部的距离,默认 0,改成 16 或 20 能避免打字时文字被光标所在行遮住。
5.3 现象:flex 子项在安卓上被压成一条线
原因:flex-shrink默认值 1,子项在容器宽度不够时被压缩。安卓 WebView 的 flex 压缩行为比 iOS 激进,长文本子项经常被压到只剩一两个字符。
解决:
.flex-item { flex: 1; min-width: 0; /* 允许 flex 容器压缩内容最小宽度 */ flex-shrink: 0; /* 不需要压缩的场景直接禁止压缩 */ }同时检查子项里有没有固定宽度元素。image组件的mode="widthFix"在某些场景下会导致宽度计算偏差,从而引起 flex 压缩。
5.4 现象:fixed 定位元素被父级 transform 带偏
原因:WebView 中,父容器有transform时,position: fixed不再相对视口定位,而是相对于该 transform 容器。常见于下拉刷新组件和动画排行榜里用了translateY()。
解决:
- 避免让 fixed 元素的父级加 transform。
- 如果动画必须加 transform,把 fixed 元素提到 page 层级渲染。
- 动画结束后移除 transform。
排查方法:在开发者工具里点击 fixed 元素,看计算出的 top/left 是否随父级 transform 改变。改变的话调整父子结构,不要在同一个容器里同时做动画和 fixed 定位。
5.5 现象:rpx 在 iPad 或大屏上被无限放大,布局直接糊了
原因:rpx 依赖屏幕宽度,750rpx 在大屏上等于一个很大的物理像素,内容等比放大。
解决:页面级做宽度判断,超过阈值改用 px 单位。
@media (min-width: 768px) { .page { width: 750px; margin: 0 auto; } .goods-cell { width: 350px; } }也可以利用 px 不受缩放影响的特性,把关键元素宽度从 rpx 改成 px,但要配合max-width约束避免在小屏机上溢出:
.goods-cell { width: calc((100vw - 60px) / 2); max-width: 350px; }这种写法在开发者工具模拟 iPad 预览时尤其重要,rpx 的放大效果非常夸张,px + 宽度策略能保住整个布局不糊。
6. 布局模板化:把案例里的经验变成一张自检清单
最后这个技巧比案例本身更有价值:把布局经验汇总成可以持续执行的自检步骤,让每次交付都少改一版 UI。
我的做法是每接手一个页面就过五步。第一步,打开开发者工具的模拟器,逐个机型选择 iPhone SE、iPhone 15 Pro、iPad、主流 Android 分辨率,确认页面没有横向溢出。溢出检查的真正对象是宽度链条——从 page 到每个 flex 子项,确认是否有min-width: 0。给根 view 加overflow-x: hidden是治标不治本。
第二步,真机预览一次,手动滑到底部,确认触底加载能触发。提前在 console 加日志,如果日志没触发,检查页面高度是不是刚好一屏而低于触底阈值。整屏内容配 50px 的默认onReachBottomDistance,永远触发不了。
第三步,用开发者工具的“性能”面板看首屏 setData 耗时。如果布局里用了一堆自定义组件,或者一屏内图片太多,把图片改成懒加载:
<image src="{{item.img}}" lazy-load mode="aspectFill" />第四步,检查自定义导航栏的标题文字。标题过长会超屏,加上max-width: 400rpx; white-space: nowrap; text-overflow: ellipsis; overflow: hidden;三件套。
第五步,特殊机型重点检查。iPhone 看安全区,安卓看虚拟导航栏,iPad 看 rpx 放大。这三项不能省。我吃过一个苦头:安卓模拟器一切正常,真机一打开底部按钮被安卓虚拟导航栏挡住,后来把 safe-area 适配写成全局工具函数,吸底按钮统一用 padding-bottom 兜底才解决。
这套清单我一般放在项目根目录的LAYOUT_CHECKLIST.md里,新同事接手时不用重复踩一轮。案例 zip 给你的是“别人怎么排”,这份清单帮你确认“自己的页面不会破”。布局这门手艺不是天赋,是把坑提前踩过一遍。希望这些经验能帮到你,下次解压类似的布局案例时,能少走我走过的弯路。
本文还有配套的精品资源,点击获取