下面,请坐好,泡杯咖啡☕️,听我给你唠唠我最近在一个项目里,是怎么用 uni-app
的 match-media
这个“神器”搞定复杂响应式布局的。
gitee仓库:gitee.com/Harvey-Andr…
别再用 JS 硬算屏幕宽度了!uni-app 的 match-media
才是响应式布局的王道!👑
嘿,各位朋友,我是你们的老朋友。今天不说虚的,咱们聊点实在的。
就在上个月,我接手了一个内容型平台的重构项目。需求听起来很简单:做一个文章阅读页面和一个后台数据看板。当时我心想,这不就是常规操作嘛,分分钟搞定!😉
我遇到的“魔鬼”需求 👹
然而,我还是太天真了。产品经理(PM)笑眯眯地拿着他的原型图过来了,然后开始了他的“表演”:
“老肖啊,这个文章页,我们要求体验极致:”
- 手机上,底部要有个悬浮工具栏,方便点赞评论。
- 但如果手机横屏,工具栏得消失,让用户沉浸式阅读,别挡着内容。
- 在平板上,我们要两栏布局,左边文章,右边作者信息。
- 哦对了,如果平板是横屏,而且高度足够,那右边的作者信息要更丰富一些。
- PC 大屏上,必须是经典三栏,左边目录,中间文章,右边作者。
- 还有个彩蛋,如果用户是 iPhone 8 (375x667) 的尺寸,给他一个特别的“生成海报”按钮!
听完这套组合拳,我的表情大概是这样的:😵💫
这还没完,关于后台数据看板,PM 的要求更“变态”:
“我们的运营总监办公室里有个 1920x1080 的大屏,当用这个分辨率访问时,要自动切换到一个全屏的‘数据作战室’模式,图表要酷炫!”
踩坑之旅:我的第一反应是...硬算!
我的第一反应,也是很多同学的第一反应:用 JS 啊!uni.getSystemInfoSync()
走起!
于是,我的 onLoad
和 onResize
函数里,很快就充满了这样的代码:
// ⚠️ 错误示范!别学我!
onLoad() {
const info = uni.getSystemInfoSync();
this.screenWidth = info.screenWidth;
this.screenHeight = info.screenHeight;
this.updateLayout();
},
onResize(res) {
this.screenWidth = res.size.screenWidth;
this.screenHeight = res.size.screenHeight;
this.updateLayout();
},
methods: {
updateLayout() {
if (this.screenWidth < 768) {
this.layout = 'mobile';
if (this.screenWidth > this.screenHeight) {
this.showToolbar = false; // 横屏
} else {
this.showToolbar = true; // 竖屏
}
} else if (this.screenWidth >= 768 && this.screenWidth < 1200) {
this.layout = 'tablet';
// ... আরও 更多的 if-else 地狱
} else {
this.layout = 'desktop';
}
// 那个彩蛋和作战室模式咋办?再加 if 判断?
if (this.screenWidth === 375 && this.screenHeight === 667) {
// ...
}
}
}
写到一半我就想掀桌子了。😫 这代码简直就是一坨面条,耦合度极高,维护性为零。每次 PM 再加个新尺寸,我就得重写一遍 if-else
地狱。这根本不是一个“资深开发者”该有的样子!
恍然大悟:从“命令式”到“声明式”的转变 ✨
就在我抓耳挠腮的时候,我无意间瞥见了 uni-app
官方文档里的一个组件:<match-media>
。
起初我没在意,但仔细一看它的用法,我瞬间“悟了”!
match-media
的核心思想,不是让你用 JS 去计算屏幕状态,然后命令界面改变。而是让你用类似 CSS 媒体查询的方式,去声明:“当满足XX条件时,这块内容就显示出来。”
这是一种从“命令式”到“声明式”的思维转变,代码会变得极其干净和直观。
我是如何用 match-media
解决问题的
话不多说,上代码!我把之前那个面条一样的 JS 逻辑,全部用 match-media
重构了。
场景一:搞定“手机、平板、PC”三端宏观布局
这是最基础的,核心就是用 min-width
(最小宽度) 和 max-width
(最大宽度) 来划分断点。
<template>
<view>
<!-- 桌面端布局 (宽度 >= 1200px) -->
<match-media :min-width="1200">
<view class="desktop-layout">左栏 | 中间 | 右栏</view>
</match-media>
<!-- 平板布局 (768px <= 宽度 < 1200px) -->
<match-media :min-width="768" :max-width="1199">
<view class="tablet-layout">文章 | 作者信息</view>
</match-media>
<!-- 手机布局 (宽度 < 768px) -->
<match-media :max-width="767">
<view class="mobile-layout">文章核心区</view>
<!-- 手机上的其他组件放这里 -->
</match-media>
</view>
</template>
看到没?没有一行 JS 判断!结构清晰得就像在读一篇文档。每个布局块各司其职,互不干扰。
场景二:处理“横竖屏”和“屏幕高度”的精细化控制
接下来是 PM 提的那些刁钻需求。match-media
同样轻松应对。
- 手机横屏隐藏工具栏? 用
orientation
(屏幕方向) 属性。 - 平板横屏且高度足够? 把
orientation
和min-height
(最小高度) 组合起来!
这是我重构后的手机布局部分,注意看我是怎么嵌套 match-media
的:
<!-- 手机布局内部 -->
<match-media :max-width="767">
<view class="mobile-layout">
<view class="main-content">文章正文...</view>
<!-- 子场景1: 仅在竖屏(portrait)时显示工具栏 -->
<match-media :orientation="'portrait'">
<view class="bottom-toolbar">点赞、评论、分享 👍</view>
</match-media>
<!-- 子场景2: 仅在横屏(landscape)时显示提示 -->
<match-media :orientation="'landscape'">
<view class="immersive-tip">沉浸式阅读中...</view>
</match-media>
</view>
</match-media>
而那个“平板横屏且高度足够”的需求,是让我最惊艳的地方。我之前以为要写复杂的逻辑,结果一行就搞定了:
<!-- 平板布局内部,显示更丰富的侧边栏 -->
<match-media :min-width="768" :max-width="1199" :orientation="'landscape'" :min-height="600">
<view class="rich-sidebar">这里是更丰富的作者信息和推荐!</view>
</match-media>
当 orientation
为 landscape
并且 min-height
大于 600
时,这个视图才会渲染。简直是为这个需求量身定做的!🤯 max-height
(最大高度) 的用法也类似,比如在分屏导致窗口很矮时,显示一个简化版UI。
场景三:实现“彩蛋”和“数据作战室”的精确打击
最后,是那两个需要精确匹配尺寸的需求。width
(页面宽度) 和 height
(页面高度) 属性就是干这个的。
iPhone 8 的彩蛋按钮:
<!-- 只有在 375x667 的尺寸下才会出现 -->
<match-media :width="375" :height="667">
<button class="special-poster-btn">✨ 生成我的阅读海报</button>
</match-media>
运营大屏的“数据作战室”:
<!-- 只有在 1920x1080 的大屏下才会全屏显示 -->
<match-media :width="1920" :height="1080">
<view class="war-room-mode">
<!-- 这里是各种酷炫的 ECharts 图表 -->
<h1>📈 数据作战室 🚀</h1>
</view>
</match-media>
这个功能上线后,运营总监特地跑来给我点了个赞,说这效果太棒了。而他不知道,我只用了几行声明式的代码就搞定了。😎
属性说明
属性名 | 类型 | 默认值 | 必填 | 说明 |
---|---|---|---|---|
min-width | number | 否 | 页面最小宽度( px 为单位) | |
max-width | number | 否 | 页面最大宽度( px 为单位) | |
width | number | 否 | 页面宽度( px 为单位) | |
min-height | number | 否 | 页面最小高度( px 为单位) | |
max-height | number | 否 | 页面最大高度( px 为单位) | |
height | number | 否 | 页面高度( px 为单位) | |
orientation | string | 否 | 屏幕方向( landscape 或 portrait ) |
总结:我的心法
uni-app
的 match-media
组件,远不止是文档上那几个属性那么简单。它教会我一种更优雅的架构思维:
- 声明式优于命令式:不要告诉程序“如何做”,而是告诉它“要什么”。代码会更干净、可读性更高。
- 化整为零,分而治之:将复杂的响应式需求拆解成一个个独立的
match-media
块。每个块只关心自己的显示条件。 - 属性组合就是超能力:单个属性很普通,但将
min-width
、orientation
、min-height
组合在一起,就能创造出极其精细和智能的布局规则。
所以,下次再遇到复杂的响应式布局需求,别再一头扎进 if-else
的泥潭了。试试 match-media
,你会回来感谢我的。
好了,今天就和大家唠到这。希望我的“踩坑”和“顿悟”经历能对你们有所启发。
Happy Coding! 😉