首页 APP开发 正文内容

uni-app 在安卓上长列表非常卡顿应该怎么优化?

2023年09月25日 , 291

长列表渲染性能问题一直是手机端的难题,本文介绍几种解决方法。

问题描述

在使用 uni-app 开发小程序或者是 APP 时,实现滚动到底部自动加载下一页,当列表项很多有几百条时,会出现卡顿的情况,特别时在安卓手机时,这个问题会更严重。

出现这个问题的原因其实也很明显,就是因为列表中经常有图片和文字,如果渲染过多就会占用很大的内存,这时候的滚动操作非常耗资源,导致手机卡顿。

解决方法

在小程序端

小程序端需要用到 uni-scroll-view 这个官方组件,原理是监听滚动事件,计算出现在可视区域内的列表,只渲染这部分列表,这样就能大大减少资源损耗。核心实现代码如下:

<template>
  <uni-scroll-view :style="{height: height + 'px'}" @scroll="onScroll">
    <ul>
      <li v-for="(item, index) in visibleData" :key="index">{{ item }}</li>
    </ul>
  </uni-scroll-view>
</template>

<script>
export default {
  data() {
    return {
      // 总数据
      data: [],
      // 可视区域高度
      height: 0,
      // 可视区域顶部数据序号
      startIndex: 0,
      // 可视区域底部数据序号
      endIndex: 0,
      // 可视区域数据
      visibleData: []
    };
  },
  mounted() {
    // 初始化数据
    this.initData();
    // 监听可视区域高度变化
    uni.getSystemInfo({
      success: (res) => {
        this.height = res.windowHeight;
      }
    });
  },
  methods: {
    initData() {
      // 这里为了演示,生成大量数据
      for (let i = 0; i < 10000; i++) {
        this.data.push(`数据 ${i+1}`);
      }
      // 初始化可视区域数据
      this.visibleData = this.data.slice(0, 30);
      this.endIndex = 29;
    },
    onScroll(e) {
      // 滚动事件
      const scrollTop = e.detail.scrollTop;
      const itemHeight = 50; // 每个数据项高度
      const visibleCount = Math.ceil(this.height / itemHeight); // 可见数据项个数
      const startIndex = Math.floor(scrollTop / itemHeight); // 开始渲染的数据序号

      if (startIndex !== this.startIndex) {
        this.startIndex = startIndex;
        this.endIndex = this.startIndex + visibleCount - 1;
        this.visibleData = this.data.slice(this.startIndex, this.endIndex + 1);
      }
    }
  }
}
</script>

上面的这段代码可以运行在小程序端,其实运行的 APP 端也没问题,但在 APP 端我们有更好的解决方式。

在 APP 端

在开发 app 端时,uni-app 官方为了解决长列表的问题,专门写了 list 这个组件,这是一个 nvue 专用组件,在这个组件只能在 nvue 下使用,性能非常好,原理是 list 在不可见部分的渲染资源回收时,有特殊的优化处理。

<template>
  <list>
    <!-- 需要注意,因为回收机制问题,这里不能使用 index 作为 key 的唯一标识 -->
    <cell v-for="(item, index) in dataList" :key="item.id">
      <text>{{item.name}}</text>
    </cell>
  </list>
</template>

<script>
  export default {
    data () {
      return {
        dataList: [{id: "aaa", name: 'AAA'}, {id: "bbb", name: 'BBB'}, {id: "ccc", name: 'CCC'}]
      }
    }
  }
</script>

跨端兼容方案

如果我们写的页面需要跨端,也就是一套代码想要同时运行在 APP 或者小程序,最好的解决方法是用官方的 uni-list 组件,代码也很简洁:

<uni-list>
    <uni-list-item  title="我是一个骚标题" ></uni-list-item>
    <uni-list-item  title="我是一个骚标题" ></uni-list-item>
    <uni-list-item  title="我是一个骚标题" ></uni-list-item>
</uni-list>

这是一个符合 easycom 规范组件,只需组件文件放到项目中,在页面 template 中可直接使用,不需要在页面中 import 和注册 components。详细的用法可以查看 uni-list 官方的使用文档