环境:HBuilderX 4.36 / uni-app Vue3(Vite) / 小米12X (Android 13)

痛点:旧项目首屏1.4s,包体积超限,低端机卡顿。

一、坑 1:条件编译别只写在 Vue 层

Vue2 时代习惯用 process.env,但在 Vite 构建体系下,这会导致 Tree-shaking 失效。必须把平台差异下沉到 Hooks 层。

错误写法(导致 H5 编译失败):


   

   
// utils/privacy.ts
export const getPrivacy = () => {
  wx.getPrivacySetting({}) // H5 端 wx is not defined
}

正确写法(全链路条件编译):


   

   
// hooks/usePrivacy.ts
import { ref } from 'vue'

export const usePrivacy = () => {
  const needAgree = ref(false)
  
  // #ifdef MP-WEIXIN
  wx.getPrivacySetting({
    success: (res) => { needAgree.value = res.needPrivacy }
  })
  // #endif

  // #ifdef H5
  needAgree.value = false // H5 端无需微信隐私协议
  // #endif

  return { needAgree }
}

原理:uni-app 编译器在编译到 H5 时,会直接删除 #ifdef MP-WEIXIN包裹的代码块,实现真正的物理隔离。

二、坑 2:Pinia 持久化引发的 180ms 白屏

购物车数据需要持久化,最初直接配置了 persist: true,结果在冷启动时有明显的卡顿感。

排查:通过 console.time打点,发现 uni.getStorageSync耗时异常。原因是 Pinia 插件默认会递归遍历整个 State,包括计算属性和非序列化对象。

优化方案:关闭自动持久化,手动控制序列化过程,只存纯数据。


   

   
// stores/cart.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useCartStore = defineStore('cart', () => {
  const items = ref<any[]>([])
  
  // 计算属性不参与存储,避免序列化开销
  const totalPrice = computed(() => 
    items.value.reduce((t, i) => t + i.price * i.count, 0)
  )

  // 初始化:仅读取纯数据
  const init = () => {
    try {
      const localData = uni.getStorageSync('cart_v2')
      if (localData) items.value = JSON.parse(localData)
    } catch (e) {
      console.error('Cart parse error:', e)
    }
  }

  // 保存:只存 items 数组
  const save = () => {
    uni.setStorageSync('cart_v2', JSON.stringify(items.value))
  }

  init()
  return { items, totalPrice, save }
}, {
  persist: false // 关键:关闭默认持久化
})

性能对比

方案

冷启动耗时 (onLoad)

内存占用

默认全量持久化

180ms

手动 Pick + 关闭默认

62ms

三、坑 3:长列表分包 + 虚拟列表的必要性

商品列表页 60 个卡片,每个带 3 张 SKU 图。如果不做优化,即使分包了,运行时渲染压力依然巨大。

优化路径

  1. 单包全量:白屏 1.4s,包体积 2.83MB(超限)。

  2. 仅分包:白屏 820ms,滚动掉帧。

  3. 分包 + uni-list虚拟滚动:白屏 620ms,滚动丝滑。

核心代码


   

   
<template>
  <!-- 必须指定固定高度,否则虚拟滚动失效 -->
  <view style="height: 100vh;">
    <uni-list 
      :data="goodsList" 
      :height="600" 
      :row-height="128"
      :buffer-size="10"
    >
      <template #default="{ item }">
        <GoodsCard :item="item" />
      </template>
    </uni-list>
  </view>
</template>

<script setup lang="ts">
import { ref } from 'vue'
const goodsList = ref([])
// 模拟获取数据
goodsList.value = Array.from({ length: 60 }).map((_, i) => ({
  id: i,
  name: `商品${i}`,
  price: 99.9
}))
</script>

避坑:H5 端需配合 scroll-view,iOS Safari 需设置 enhanced属性防止抖动。

四、坑 4:iOS 键盘顶起布局的修复

登录页点击输入框,iOS 键盘弹起会将底部按钮顶飞。原因是 iOS Webview 将键盘算入了视口高度。

解决方案:使用 page-meta动态监听。


   

   
<template>
  <page-meta :page-style="`--kbh: ${keyboardHeight}px;`" />
  <view class="footer" :style="{ bottom: keyboardHeight + 'px' }">
    <button>提交</button>
  </view>
</template>

<script setup lang="ts">
import { ref } from 'vue'
const keyboardHeight = ref(0)
uni.onKeyboardHeightChange(res => {
  keyboardHeight.value = res.height
})
</script>
Logo

一站式 AI 云服务平台

更多推荐