Chapter 09

性能优化与 Skyline 渲染引擎

分包加载、worklet 动画、Skyline 共享元素动画与手势系统,打造原生级流畅体验

9.1 性能优化的方向

小程序的性能问题主要体现在两个维度:启动速度(用户第一次打开到可交互的时间)和运行时流畅度(滚动、动画、交互的顺滑程度)。

影响启动速度的因素

  • 主包体积(代码 + 资源)
  • 首页数据请求数量
  • onLaunch 执行时间
  • 首页组件复杂度
  • 同步 Storage 读取阻塞

影响运行时流畅度的因素

  • 频繁 setData(跨线程通信)
  • setData 传输数据量过大
  • 长列表不回收节点
  • JS 主线程执行耗时任务
  • CSS 动画触发重排/重绘

9.2 分包加载策略

分包是小程序性能优化最重要的手段之一。将非首屏必要的页面划分为独立分包,用户进入对应功能时才按需下载。

// app.json — 分包配置
{
  "pages": [
    "pages/index/index",      // 主包:首页
    "pages/profile/profile"  // 主包:个人中心
  ],
  "subpackages": [
    {
      "root": "pkgShop",          // 商城分包
      "name": "shop",
      "pages": [
        "pages/list/list",
        "pages/detail/detail",
        "pages/cart/cart"
      ]
    },
    {
      "root": "pkgSettings",      // 设置分包
      "name": "settings",
      "independent": true,      // 独立分包:不依赖主包,可从分享直接进入
      "pages": [
        "pages/settings/settings",
        "pages/about/about"
      ]
    }
  ],
  // 分包预下载:在某些页面停留时,提前下载分包
  "preloadRule": {
    "pages/index/index": {
      "network": "wifi",          // 仅 WiFi 下预下载
      "packages": ["shop"]
    },
    "pages/profile/profile": {
      "network": "all",
      "packages": ["settings"]
    }
  }
}
💡
独立分包(independent)

设置 "independent": true 的分包是独立分包,它不依赖主包代码,可以独立运行(如从分享链接直接进入商品详情页,不需要先加载主包)。代价是:独立分包中无法访问 app.jsglobalData 和 App 生命周期,需要自行处理初始化逻辑。

9.3 首屏加载优化

// 优化策略 1:数据预拉取(小程序启动时提前请求数据)
// 在公众平台「开发 → 数据预拉取」配置预拉取 URL
// 在 onLaunch 中读取预拉取的数据
onLaunch() {
  wx.getBackgroundFetchData({
    fetchType: 'pre',  // 'pre' 定期预拉取 / 'periodic' 周期性
    success(res) {
      // res.fetchedData: 预拉取的数据(string)
      if (res.fetchedData) {
        const data = JSON.parse(res.fetchedData)
        // 缓存到内存,首页直接使用
        getApp().prefetchedData = data
      }
    }
  })
}

// 优化策略 2:骨架屏(Skeleton Screen)
// 在数据加载完成前显示骨架占位
Page({
  data: { loaded: false, list: [] },
  async onLoad() {
    const list = await fetchList()
    this.setData({ list, loaded: true })
  }
})
<!-- 骨架屏 WXML -->
<view wx:if="{{!loaded}}">
  <view wx:for="{{[1,2,3,4,5]}}" wx:key="*this" class="skeleton-item">
    <view class="skeleton skeleton-image"/>
    <view class="skeleton skeleton-title"/>
    <view class="skeleton skeleton-text"/>
  </view>
</view>
<view wx:else>
  <<!-- 真实内容 -->>
</view>

9.4 长列表优化

<!-- recycle-view:虚拟列表组件,只渲染可见区域节点 -->
<!-- 需要在 app.json 中引入:@miniprogram-component-plus/recycle-view -->

<recycle-view
  batch="{{batchSetRecycleData}}"
  id="recycleId"
>
  <recycle-item
    wx:for="{{recycleList}}"
    wx:key="id"
  >
    <product-card product="{{item}}"/>
  </recycle-item>
</recycle-view>

9.5 worklet 动画

worklet 是 Skyline 渲染引擎提供的动画机制,代码直接运行在渲染线程,无需跨线程通信,实现真正的 60fps/120fps 动画。

worklet 函数
'worklet' 指令标记的函数,在渲染线程执行,可以直接读写 SharedValue 而不需要跨线程
SharedValue
逻辑层和渲染层共享的响应式值,类似 React Native 的 Animated.Value,修改时自动触发动画
applyAnimatedStyle
将 SharedValue 绑定到组件样式,由渲染线程直接驱动样式变化,不经过逻辑层
// worklet 动画示例:拖拽卡片
Page({
  onLoad() {
    // 创建共享值(初始位置)
    this.translateY = wx.worklet.shared(0)

    // 将 SharedValue 绑定到样式
    this.applyAnimatedStyle('#draggable-card', () => {
      'worklet'
      return {
        transform: `translateY(${this.translateY.value}px)`
      }
    })
  },

  // 触摸事件处理(手势驱动动画)
  handleTouch: function(e) {
    'worklet'  // 此函数在渲染线程执行!
    const ty = e.touches[0].pageY - e.touches[0].startY
    this.translateY.value = ty
  },

  // 松手后弹簧回弹
  handleTouchEnd: function() {
    'worklet'
    this.translateY.value = wx.worklet.spring(0, {
      mass: 1,
      stiffness: 200,
      damping: 20
    })
  }
})

9.6 Skyline 共享元素动画

共享元素动画(Shared Element Transition)是 Skyline 的明星特性,实现页面跳转时元素平滑过渡(类似 iOS 的 Hero 动画)。

<!-- 列表页 list.wxml -->
<view
  wx:for="{{products}}"
  wx:key="id"
  bindtap="goDetail"
  data-id="{{item.id}}"
>
  <!-- shared-element-root 标记共享元素容器 -->
  <!-- share-key 是元素在两个页面间的唯一标识 -->
  <image
    src="{{item.cover}}"
    mode="aspectFill"
    class="product-thumb"
    data-share-key="product-cover-{{item.id}}"
  />
</view>
<!-- 详情页 detail.wxml -->
<image
  src="{{product.cover}}"
  mode="aspectFill"
  class="product-cover"
  data-share-key="product-cover-{{productId}}"
/>
// 配置 Skyline 路由动画
// detail.json
{
  "renderer": "skyline",
  "pageStyle": "bottom-sheet",  // 底部弹出动画
  "navigationStyle": "custom"
}

// 或者使用 wx.navigateTo 时传入路由动画类型
wx.navigateTo({
  url: '/pkgShop/pages/detail/detail?id=123',
  routeType: 'zoom-fade-out'   // 缩放淡出动画
})

9.7 声明式手势系统

Skyline 提供了声明式手势识别系统,解决了 WebView 时代手势冲突难处理的痛点:

<!-- 手势冲突解决:scroll-view 内的可拖拽卡片 -->
<scroll-view scroll-y>
  <!-- pan-gesture: 拖拽手势 -->
  <pan-gesture-handler
    worklet:ongesture="onPan"
    simultaneous-handlers="{{['scroll']}}"
  >
    <view class="draggable">可拖拽元素</view>
  </pan-gesture-handler>
</scroll-view>

<!-- 双指缩放 -->
<scale-gesture-handler worklet:ongesture="onScale">
  <image src="{{photoUrl}}" class="photo"/>
</scale-gesture-handler>

9.8 使用 DevTools 分析性能

微信开发者工具内置了强大的性能分析工具:

ℹ️
体验评分

微信开发者工具的 Audits 面板会对你的小程序进行体验评分(0-100分),并按类别列出问题:包大小、渲染性能、网络请求、合规性等。建议发布前确保评分在 85 分以上。体验评分也会影响微信搜索排名。