夜雨聆风学习资料网

ARTICLE · 1033686

uni-app避坑指南:5个90%开发者都踩过的坑

uni-app避坑指南:5个90%开发者都踩过的坑

uni-app「一套代码,多端运行」确实香,但真正上手后你会发现:所谓跨端,跨的是语法,跨不了平台差异。这篇文章把我带团队做 uni-app 三年、大大小小十几个项目里最常踩的 5 个坑整理出来,每一个都给出错误写法、正确写法和背后的原因。建议先收藏,再慢慢看。

坑 1:条件编译写错位置,代码「诡异失效」

条件编译是 uni-app 跨端的灵魂,但它有个极易被忽视的特性:它是注释驱动的,必须写在注释里。很多从 Vue 转过来的同学会把它当成普通指令写,结果在 H5 上跑得好好的,一打包小程序就「神秘消失」。

条件编译发生在编译期,不符合平台的代码块会被整体剔除

❌ 错误写法:手动判断平台,容易漏端

// 想只在 App 端隐藏某个按钮<view v-if="!isApp">导出数据</view>// 手动判断平台,忘记覆盖所有端const isApp = uni.getSystemInfoSync().platform === 'ios'        || uni.getSystemInfoSync().platform === 'android'

✅ 正确写法:交给编译器

<!-- #ifdef APP-PLUS --><button>导出数据</button><!-- #endif -->// JS 中同理,注意是写在注释里// #ifdef H5console.log('这段只在 H5 执行')// #endif

💡 实用技巧#ifndef H5 表示「除了 H5 以外的所有端」,往往比逐个列举更不容易漏。团队内建议统一约定:能用 ifndef 就用 ifndef。

坑 2:rpx 用在所有地方,宽屏上布局直接爆炸

rpx 的规则是「750rpx = 屏幕宽度」,在手机上很完美。但问题来了:当你在 iPad、PC 浏览器上打开 H5 时,屏幕宽度可能是 1200px 甚至更多,此时 1rpx 会被等比放大,一个按钮能宽得像条横幅

rpx 等比放大:屏幕越宽,元素越大,宽屏下布局会失去控制

✅ 解法:rpx 只用于「随屏幕缩放」的部分

/* 容器、卡片留白等跟随屏幕缩放:放心用 rpx */.card { padding: 24rpx; border-radius: 16rpx; }/* 按钮、弹窗、内容区最大宽度:用 px + max-width 锁死 */.btn {  width: 600rpx;  max-width: 320px;   /* 宽屏下不再放大 */  margin: 0 auto;}/* page 级别也可以限制内容宽度 *//* #ifdef H5 */.page-content { max-width: 720px; margin: 0 auto; }/* #endif */

⚠️ 重点提醒:uni-app 官方在 H5 端默认开启了 rpx 最大宽度限制(通常 960px),但这个值可以在 manifest.json 中修改。接手老项目时,先检查这个配置,别被前人埋的雷炸到。

坑 3:onLoad、onShow、created 分不清,数据重复加载

uni-app 页面的生命周期比普通 Vue 页面多了一层「页面生命周期」,很多同学用惯了 created/mounted,结果发现:小程序里 created 拿不到页面参数;返回上一页时数据没有刷新,或者刷新了两次。

onLoad 只在首次加载执行一次;onShow 每次可见都会触发

✅ 正确的使用姿势

export default {  // onLoad:接收页面参数 + 只需执行一次的初始化  onLoad(options) {    this.goodsId = options.id    this.loadDetail()  },  // onShow:需要「每次回来都最新」的数据  onShow() {    this.refreshCartCount()   // 购物车角标、未读消息数等  },  methods: {    async loadDetail() { /* ... */ }  }}

💡 经典 bug:把「列表数据加载」写在 onShow 里,用户从详情页返回时整个列表闪一下重新加载。正确做法是首屏数据放 onLoad,只有「可能被上一页修改过」的状态(如收藏、点赞数)才放 onShow。

坑 4:uni.setStorage 的同步异步之争

uni-app 的存储 API 有 Sync 和非 Sync 两套,很多人无脑用同步版,图省事。同步 API 的问题是:它会阻塞 JS 线程。数据量小的时候无感,一旦往 storage 里塞了几 MB 的缓存数据,页面会明显卡顿。

✅ 推荐实践:封装一层,业务层无感知

// utils/storage.jsconst PREFIX = 'myapp_'export const storage = {  async get(key, defaultValue = null) {    try {      const val = uni.getStorageSync(PREFIX + key)      return val === '' ? defaultValue : val    } catch (e) {      console.error('storage get error:', e)      return defaultValue    }  },  async set(key, value) {    try {      uni.setStorageSync(PREFIX + key, value)      return true    } catch (e) {      // 10MB 上限:写入失败通常是空间不足      if (e.errMsg && e.errMsg.includes('exceed')) {        uni.clearStorageSync()  // 按业务改成 LRU 清理更佳        return uni.setStorageSync(PREFIX + key, value)      }      return false    }  },  remove(key) {    uni.removeStorageSync(PREFIX + key)  }}// 业务代码统一调用,将来换方案只需改这一处await storage.set('userInfo', { name: '张三' })

💡 两个小知识点:① storage 存的都是字符串,uni-app 的 Sync API 已自动处理对象,但读出来要做类型校验;② 小程序端 storage 总上限 10MB,别把图片 base64 塞进去。

坑 5:scroll-view 与页面滚动打架,下拉刷新失灵

「我的页面滚动不了了!」「下拉刷新怎么不触发?」——这几乎每个 uni-app 新手都会遇到。根因是要先想清楚:你的滚动到底发生在哪一层?

  • 页面级滚动(默认):内容超出屏幕时页面自然滚动,支持下拉刷新 onPullDownRefresh;
  • 用 scroll-view 固定高度滚动:页面本身不滚了,onPullDownRefresh 失效,要用 scroll-view 自己的 refresher-enabled。

❌ 典型错误:height: 100% 不生效

<scroll-view scroll-y style="height: 100%">  /* 无效! */  <view v-for="item in list">...</view></scroll-view>

✅ 正确写法:给 scroll-view 一个确定高度

/* 方案一:css 撑满视口 */page { height: 100%; }.container {  height: 100vh;  display: flex;  flex-direction: column;}.scroll-area { flex: 1; overflow: hidden; }/* 方案二(推荐):scroll-view 用 flex 剩余空间 */<scroll-view  scroll-y  class="scroll-area"  refresher-enabled  :refresher-triggered="isRefreshing"  @refresherrefresh="onRefresh">  <view v-for="item in list" :key="item.id" class="item">    {{ item.title }}  </view></scroll-view>async onRefresh() {  this.isRefreshing = true  await this.loadData()  this.isRefreshing = false  uni.stopPullDownRefresh()}

⚠️ 记住一条铁律:scroll-view 必须有确定的固定高度(vh / flex 计算结果均可),否则它会被内容撑开,滚动条永远不会出现。

写在最后

跨端框架的本质是「用约定换效率」:你遵守框架的平台约定,框架帮你抹平 80% 的差异,剩下 20% 就靠条件编译和踩坑经验补齐。这 5 个坑覆盖了我见过 90% 的线上事故,建议:

  • 把条件编译的规范写进团队代码评审 checklist;
  • 封装 storage、request 等基础层,业务代码不直接调 uni API;
  • 每个端都真机跑一遍,别只盯微信开发者工具。

下一篇我会带来完整实战案例:从零开发一个多端待办清单应用,数据持久化、组件拆分、跨端细节全部展示,代码可直接跑。关注不迷路。

相关学习资料