骨架屏必须手写并用v-show精准控制显隐,onShow生命周期配合缓存判断决定是否显示,确保DOM占位稳定、盒模型一致,避免v-if导致跳动和兼容性问题。

uni-app 里做骨架屏,不是追求“自动生成”,而是必须手写 + 精准控制显隐时机。所谓“一键生成”在跨端场景下基本不可靠,微信开发者工具生成的 xxx.skeleton.wxml 只能当参考,直接搬进 .vue 文件会出样式错位、平台兼容性崩塌、v-if 导致闪动等问题。
为什么不能用 v-if 控制骨架屏显隐
骨架屏的核心价值是「占位」,不是「条件渲染」。一旦用 v-if,DOM 节点被销毁,真实内容加载后重新挂载,页面会跳一下,滚动位置丢失,iOS WebView 中尤其明显。
-
v-show只切display: none,骨架结构始终在 DOM 中,布局稳定、尺寸不塌陷 - 哪怕骨架是独立组件(如
SkeletonCard.vue),也得确保它不依赖响应式props,否则v-show下仍可能触发无谓更新 - 常见错误现象:下拉刷新后骨架一闪就消失,但内容没对齐;或
onLoad里设loading = true,success回调才设false,结果骨架只闪 100ms,完全起不到占位作用
onShow 才是控制骨架显隐的正确生命周期
很多页面从 tabBar 切入时,onLoad 根本不执行,但数据要重拉——如果只监听 onLoad,骨架就不会出现,用户看到的是空白页。
-
onLoad只在首次进入页面时触发;onShow每次页面显示都触发(含从后台切回、tabBar 切换) - 把骨架显隐逻辑放到
onShow,配合uni.getStorageSync判断缓存是否存在,决定是否立即显示骨架 - 网络请求发完立刻执行
this.loading = true,而不是等uni.request的success回调才设为false
手写骨架组件的关键细节
别迷信“自动生成”,手写才是最稳的路径。重点不在代码量,而在结构语义和样式收敛。
- 用
<template></template>写骨架,避免data响应式开销;用props控制是否显示(如loading),而非v-if切换整个结构 - 颜色统一用
#f2f2f2(背景)和#e4e4e4(色块),避免暗黑模式下翻车 - 列表页用
uni-list结构模拟,详情页带头图+多段文本,表单页对应输入框+按钮——按真实页面结构来,别堆view数量 - 微信开发者工具生成的
xxx.skeleton.wxml可作参考,但要删掉自定义导航栏等静态区域,只保留需异步加载的内容区块
预加载 + 骨架屏组合使用才真正提感知性能
骨架屏解决的是“用户看到什么”,预加载解决的是“数据从哪来”。两者不绑定,但合起来才能把首屏从“白→转圈→内容”变成“结构→填充→完成”。
- 在
App.vue的onLaunch或首页onShow中,用uni.preloadPage提前拉取高频页面的骨架结构和基础数据 - 对非高频页面(如个人中心),在
onUnload时调用uni.unPreloadPage释放资源,避免内存占用过高 - 注意预加载时机:首页停留超 2 秒再预加载推荐页,比一启动就全量预加载更省 0.2–0.3 秒
最容易被忽略的点是:骨架屏不是“加载中提示”,它必须和真实内容共存于 DOM,并严格复刻真实内容的盒模型(padding/margin/圆角/宽高比)。哪怕少一个 border-radius: 8rpx,加载完成后都会产生肉眼可见的“顶上来”抖动。



















