骨架屏

微信小程序的骨架屏:指页面的一个空白版本,通常会在页面完全渲染之前,通过一些灰色的区块大致勾勒出轮廓,待数据加载完成后,再替换成真实的内容。 抖音小程序的骨架屏:框架在页面真正渲染前先展示"灰块轮廓",显著缩短白屏时长、提升 LCP;时机早于业务手写骨架。

一、微信小程序
js 复制代码
   //在页面wxml引入模板并用wx:if控制
  <import src="index.skeleton.wxml"/>
  <template is="skeleton" wx:if="{{loading}}" data="{{}}"/>
  //在页面 wxss 引入样式
   @import "index.skeleton.wxss";
  • 显隐控制:和普通模板一致,用wx:if/hidden等。
  • 生成配置:project.config.json里通过skeletonConfig控制灰块规则、局部显示、过滤等(页面配置可覆盖全局)。
  • 示例:
js 复制代码
//```json
{
  "skeletonConfig": {
    "global": {
      "outline": { "remain": false, "replace": "none" },
      "text": { "color": "#EEEEEE" },
      "image": { "color": "#EFEFEF" },
      "remove": [],
      "hide": [],
      "grayBlock": [".btn", ".banner"],
      "mode": "fullscreen",
      "cssUnit": "rpx",
      "decimal": 4
    }
  }
}

高级标注(可选):通过data-skeleton-*增强生成质量

  • data-skeleton-bgColor 指定块背景色
  • data-skeleton-listdata-skeleton-li="group" 识别列表与克隆第一项
  • data-skeleton-hide="key" 让局部骨架渐进显示(与真实内容重叠时配合绝对定位)
  • 注意事项:
    • 仅覆盖首屏可见区域;swiper超出屏幕部分忽略
    • 原生组件(video/map/input等)骨架效果受限
    • 不要手改生成文件;调配置重新生成更稳
  • 文档参考:微信开发者工具 · 骨架屏
二、抖音小程序
  • 形态与文件:每个页面对应一个 *.sk 模板文件,包含:
  • template:仅支持 div + id/class/style
  • style:标准 CSS
自动生成

1.在 IDE 3.2.6+,预览到目标页面 → 左下角选择列表 → 生成骨架屏 2.选择"保留/覆盖",可"自动引入"把 skeleton 配置写入 app.json/page.json 3.会在该页面同级生成 页面名.sk,可微调样式

  • 配置入口 1.全局:app.json["skeleton"]["config"] 2.页面:page.json["skeleton"]["config"](优先级更高) 3.常用字段:loading(spin/chiaroscuro/shine)、image(shape rect/circle, color)、button.color、backgroundColor、mode(fullscreen/auto)、cssUnit(px/rem/vw/vh/...)、decimal
  • 显示/隐藏 默认加载后 2000ms 自动隐藏;可在 timeout 调 0 关闭自动隐藏。 主动隐藏:this.removeSkeleton && this.removeSkeleton()(需在页面上下文) 局部隐藏:this.removeSkeleton({ id: 'node-id' }) 自定义导航栏适配(CSS 变量,2.51.0+): 可用 --status-bar-height、--menu-button-top、--menu-button-height、--pixel-ratio 等,结合 var()/calc() 计算布局。

注意与限制(官方提示):

  • 仅首屏可见区域;超出屏幕的 swiper 子项会被忽略
  • id 不能作为 CSS 选择器使用;原生/复杂组件效果有限
  • 生成骨架包含预览态数据(文案等)用作填充
  • 页面加载完成后再点击"生成骨架屏",效果更稳
  • 文档来源:抖音小程序 · 框架骨架屏
三、在uni-app 中用"微信文档风格"实现骨架屏(两种方案)

方案A:复用微信骨架文件(推荐用于仅 MP-WEIXIN) 目标:保留官方生成逻辑,又能在 uni-app 中稳定使用、不被构建覆盖。

步骤:

  • 1.在微信开发者工具里对目标页面(例如pages/theater/theater)生成骨架,拿到theater.skeleton.wxml/wxss
  • 2.将这两个文件改造成"原生自定义组件"(避免每次构建被覆盖):
  • 新建目录:src/native-wx/skeleton/theater/
  • 保存为index.wxml(把skeleton.wxml内容整体作为组件模板),index.wxss(原样复制) index.json
index.json 复制代码
{ "component": true, "usingComponents": {} }
  • 3.在 pages.json 为对应页面注册原生组件(仅微信端):
json 复制代码
 {
     "path": "pages/theater/theater",
     "style": {
       "navigationBarTitleText": "剧场",
       "usingComponents": {
         "skeleton-view": "/native-wx/skeleton/theater/index"
       }
     }
   }

若需要条件编译,仅在微信端生效,可把这个 usingComponents 放到微信专用的页面配置块里或用分平台 pages.json。

  • 4.在 pages/theater/theater.vue 中使用(切记仅微信端显示):
vue 复制代码
 <template>
     <view class="theater">
       <!-- #ifdef MP-WEIXIN -->
       <skeleton-view v-if="loading" />
       <!-- #endif -->

       <view v-else>
         <!-- 真实内容 -->
       </view>
     </view>
   </template>

   <script setup lang="ts">
   import { ref, onMounted } from 'vue'
   const loading = ref(true)
   onMounted(async () => {
     // 拉数据...
     loading.value = false
   })
   </script>

要点:

  • 不要把骨架 wxml/wxss 放 dist 目录;构建会覆盖。放在 src/native-wx/ 并通过 usingComponents 引用最稳。
  • 骨架本身是"原生组件",仅适用于 MP-WEIXIN;其他端会被条件编译屏蔽。
方案 B:跨端 Vue 骨架组件(更通用)

目标:一个组件多端通用,不依赖微信骨架生成器。

步骤: 1.新建 src/components/SkeletonTheater.vue,写占位块(方块、圆形、文本条)+ 闪动骨架 CSS:

SkeletonTheater.vue 复制代码
<template>
  <view class="skeleton" v-if="loading">
    <view class="skeleton-banner shimmer" />
    <view class="skeleton-card shimmer" v-for="i in 3" :key="i" />
  </view>
</template>
<script setup lang="ts">
defineProps<{ loading: boolean }>()
</script>

<style scoped lang="scss">
.skeleton { padding: 24rpx; }
.shimmer {
  position: relative; background: #eee; overflow: hidden;
}
.shimmer::after {
  content: ""; position: absolute; inset: 0;
  transform: translateX(-100%); background: linear-gradient(90deg, transparent, rgba(255,255,255,.5), transparent);
  animation: shimmer 1.2s infinite;
}
@keyframes shimmer { 100% { transform: translateX(100%); } }
.skeleton-banner { height: 300rpx; border-radius: 16rpx; }
.skeleton-card { height: 160rpx; margin-top: 24rpx; border-radius: 16rpx; }
</style>

在页面中使用:

vue 复制代码
<SkeletonTheater :loading="loading" />
<view v-if="!loading"> ...真实内容... </view>

要点:

  • 完全跨端;不依赖微信文件格式,也无需 usingComponents
  • 若页面复杂,可拆分多个骨架组件,随不同请求的返回逐块替换。

实战建议与坑位 控制粒度:只覆盖"首屏可见区域",骨架越轻越好,降低首屏 CSS/节点体积。 loading 状态来源:首屏必要数据(如 banner/列表)全部到齐再切换;避免频繁闪烁。 与你现有样式规范: 组件内避免使用全局标签选择器(page/view/text 等),符合微信组件 WXSS 限制(你之前已踩过这个错误)。 骨架组件样式尽量 scoped,避免污染。 与 Loading 的配合: 骨架与 uni.showLoading/uni.hideLoading不冲突。建议首屏只用骨架,不弹系统 loading,避免"跳转后 hideLoading 报错"的问题。 结合 skeletonConfig 再生成:当页面结构变化时,可通过调整 project.config.json 后重新生成骨架,再覆盖到 src/native-wx/skeleton/... 组件中,保持一致性。

在uni-app(MP-TOUTIAO)中落地的建议 最省心路径(推荐)

  • 1.用抖音 IDE 对 dist 下的对应页面生成 *.sk 并"自动引入",无需改 .vue;页面结构变化后再生成一次保持同步
  • 若要在代码中"主动隐藏"骨架(可选):
  • 在该 uni-app 页面里(onReady/数据到齐时)取原生 Page 实例后调用:
js 复制代码
import { onReady } from 'vue'
    onReady(() => {
      const pages = getCurrentPages()
      const page = pages[pages.length - 1] as any
      page?.removeSkeleton && page.removeSkeleton()
      // 局部:page?.removeSkeleton && page.removeSkeleton({ id: 'node-id' })
    })

配置示例(自动生成时 IDE 会写入):

json 复制代码
  {
    "skeleton": {
      "page": "pages/your-page/index",
      "config": {
        "loading": "shine",
        "image": { "shape": "rect", "color": "#efefef" },
        "button": { "color": "#efefef" },
        "backgroundColor": "#fff",
        "mode": "fullscreen",
        "cssUnit": "vw",
        "decimal": 4,
        "timeout": 0
      }
    }
  }

说明:将 timeout 设为 0 后,需手动调用 removeSkeleton() 隐藏;若不需要手动控制,可保留默认超时隐藏。

选型对比与实践建议:

  • 需要"极致首屏体验"且目标端是抖音:优先用框架骨架屏(*.sk + skeleton 配置),注入时机更早。
  • 需要多端统一:可额外做一套 Vue 骨架组件(跨端可复用),但首屏时机会稍晚;抖音端同时开启框架骨架屏可叠加优化。
  • 与系统 loading 并用:建议首屏显示骨架,避免频繁调用系统 showLoading/hideLoading 造成跳转后报错或闪烁。

建议(结论先行)

  • 优先用自动生成(框架级骨架屏):目标端是微信/抖音,追求最早展示时机与更好 LCP 表现,且页面改动较频繁。
  • 补充用手写(业务级骨架组件):需要跨端统一(uni-app 多端共用)、样式高度自定义、或做局部骨架/渐进加载的页面片段。

何时选自动生成

  • 只面向 MP-WEIXIN / MP-TOUTIAO,且是首屏关键页(如剧场、播放器):框架骨架注入更早,体验最佳。

  • 页面结构经常变化:用工具一键再生,维护成本低。

  • 抖音可直接生成 *.sk 并通过 app.json/page.json 的 skeleton 注入;微信用工具生成 page.skeleton.wxml/wxss 引入即可。

  • 支持主动/局部隐藏:this.removeSkeleton() / this.removeSkeleton({ id }),能配合数据就绪精准消失。

  • 微信文档参考:骨架屏(开发者工具)

  • 抖音文档参考:框架骨架屏

何时选手写

  • 需要跨端(H5、App、抖音、微信)复用同一骨架,或希望在组件维度复用(如通用列表骨架、卡片骨架)。

  • 复杂的自定义动效/布局、或与业务占位强绑定(如占位图、品牌样式统一)。

  • 希望避免"生成文件随页面结构频繁再生"的流程。

对你项目的具体建议

  • pages/theater/theater、pages-toutiao/playlet/index:用自动生成(各自端启用框架骨架),并把超时隐藏设为 0,数据到齐后主动移除,避免闪烁。

  • 通用组件位(如列表卡片骨架):补一套手写的 Vue 骨架组件,供其他页面/端复用,保持风格一致。

一句话:首屏关键页用"自动生成"拿极致加载时机;通用组件/跨端一致性用"手写"兜底,两者组合最佳。

ly带你手把手来骨架屏:

  • 点击页面信息,然后点击生成骨架屏,弹出下面这段话,直接确定就可以了。

注意:骨架屏仅包括页面首屏中的可见区域,对于横向滚动的 swiper 等容器,超出屏幕的子元素将被忽略;

转成vue组件

  • 1.处理theater.skelecton.wxml

拷贝theater.skelecton.wxml文件的内容,然后到vscode中的uniapp应用中,生成一个新的PageSkeleton.vue,将theater.skelecton.wxml文件的内容粘贴进去。 文件开头的注释是针对在原生微信小程序中如何操作的提示,在uni-app中不需要这样操作,可以删掉这些注释。

页面中自定义导航看部分不涉及数据请求,所以不需要放在骨架屏中,因为骨架屏是针对要请求数据而暂时没有返回时的用户体验提升,所以吧这部分删掉。

可以改一下动态属性

处理样式文件

把wxss样式添加到这个PageSkeleton.vue页面去。

结构和样式都准备完之后,在首页根据数据加载是否完成条件骨架屏。

在首页先引入骨架屏文件。 改造首页页面插入骨架屏文件,然后条件显示。

小朋友们:看抖音怎么玩,找出来骨架屏

点击确定

勾选「自动引入 」,表示将在 app.json 中生成 skeleton 配置,不勾选则不作处理。生成配置如下:

确定后将在当前页面同级目录下生成 *.sk文件( 表示页面名称 ),文件内包括骨架屏的代码模板以及样式,开发者可以修改调整。

相关推荐
fruge11 小时前
仿写优秀组件:还原 Element Plus 的 Dialog 弹窗核心逻辑
前端
an869500111 小时前
vue新建项目
前端·javascript·vue.js
w***954911 小时前
SQL美化器:sql-beautify安装与配置完全指南
android·前端·后端
顾安r12 小时前
11.22 脚本打包APP 排错指南
linux·服务器·开发语言·前端·flask
万邦科技Lafite12 小时前
1688图片搜索商品API接口(item_search_img)使用指南
java·前端·数据库·开放api·电商开放平台
yinuo13 小时前
网页也懂黑夜与白天:系统主题自动切换
前端
Coding_Doggy14 小时前
链盾shieldchain | 项目管理、DID操作、DID密钥更新消息定时提醒
java·服务器·前端
用户214118326360214 小时前
dify案例分享-国内首发!手把手教你用Dify调用Nano Banana2AI画图
前端
wa的一声哭了14 小时前
Webase部署Webase-Web在合约IDE页面一直转圈
linux·运维·服务器·前端·python·区块链·ssh
han_14 小时前
前端性能优化之CSS篇
前端·javascript·性能优化