别再用 JS 硬算屏幕宽度了!uni-app 的match-media才是响应式布局的王道!

8 阅读6分钟

下面,请坐好,泡杯咖啡☕️,听我给你唠唠我最近在一个项目里,是怎么用 uni-appmatch-media 这个“神器”搞定复杂响应式布局的。

gitee仓库:gitee.com/Harvey-Andr…


别再用 JS 硬算屏幕宽度了!uni-app 的 match-media 才是响应式布局的王道!👑

嘿,各位朋友,我是你们的老朋友。今天不说虚的,咱们聊点实在的。

就在上个月,我接手了一个内容型平台的重构项目。需求听起来很简单:做一个文章阅读页面和一个后台数据看板。当时我心想,这不就是常规操作嘛,分分钟搞定!😉

我遇到的“魔鬼”需求 👹

然而,我还是太天真了。产品经理(PM)笑眯眯地拿着他的原型图过来了,然后开始了他的“表演”:

“老肖啊,这个文章页,我们要求体验极致:”

  1. 手机上,底部要有个悬浮工具栏,方便点赞评论。
  2. 但如果手机横屏,工具栏得消失,让用户沉浸式阅读,别挡着内容。
  3. 平板上,我们要两栏布局,左边文章,右边作者信息。
  4. 哦对了,如果平板是横屏,而且高度足够,那右边的作者信息要更丰富一些。
  5. PC 大屏上,必须是经典三栏,左边目录,中间文章,右边作者。
  6. 还有个彩蛋,如果用户是 iPhone 8 (375x667) 的尺寸,给他一个特别的“生成海报”按钮!

image.png 听完这套组合拳,我的表情大概是这样的:😵‍💫

这还没完,关于后台数据看板,PM 的要求更“变态”:

“我们的运营总监办公室里有个 1920x1080 的大屏,当用这个分辨率访问时,要自动切换到一个全屏的‘数据作战室’模式,图表要酷炫!”

image.png

踩坑之旅:我的第一反应是...硬算!

我的第一反应,也是很多同学的第一反应:用 JS 啊!uni.getSystemInfoSync() 走起!

于是,我的 onLoadonResize 函数里,很快就充满了这样的代码:

// ⚠️ 错误示范!别学我!
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 (屏幕方向) 属性。
  • 平板横屏且高度足够?orientationmin-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>

orientationlandscape 并且 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>

这个功能上线后,运营总监特地跑来给我点了个赞,说这效果太棒了。而他不知道,我只用了几行声明式的代码就搞定了。😎

属性说明

match-media

属性名类型默认值必填说明
min-widthnumber页面最小宽度( px 为单位)
max-widthnumber页面最大宽度( px 为单位)
widthnumber页面宽度( px 为单位)
min-heightnumber页面最小高度( px 为单位)
max-heightnumber页面最大高度( px 为单位)
heightnumber页面高度( px 为单位)
orientationstring屏幕方向( landscape 或 portrait )

总结:我的心法

uni-appmatch-media 组件,远不止是文档上那几个属性那么简单。它教会我一种更优雅的架构思维:

  1. 声明式优于命令式:不要告诉程序“如何做”,而是告诉它“要什么”。代码会更干净、可读性更高。
  2. 化整为零,分而治之:将复杂的响应式需求拆解成一个个独立的 match-media 块。每个块只关心自己的显示条件。
  3. 属性组合就是超能力:单个属性很普通,但将 min-widthorientationmin-height 组合在一起,就能创造出极其精细和智能的布局规则。

所以,下次再遇到复杂的响应式布局需求,别再一头扎进 if-else 的泥潭了。试试 match-media,你会回来感谢我的。

好了,今天就和大家唠到这。希望我的“踩坑”和“顿悟”经历能对你们有所启发。

Happy Coding! 😉