问题现象

页面一次加载了100条数据,页面滑动出现卡顿。

问题代码

<template>
  <div class="container">
    <div class="nav">
      <text class="nav-item">list</text>
    </div>
 
    <!-- List -->
    <list class="list" onclick="listClick" onlongpress="listLongPress"
      onscrollbottom="scrollbottom"  id="list" scrollpage="{{scrollPage}}">
 
      <list-item type="listItem" class="item"  onclick="listItemClick"
        if="{{listData.length>0}}">
           <div for="{{listData}}" style="flex-direction:column;">
                <text  class="txt">{{$item}}--{{$idx}}</text>
           </div>
      </list-item>
 
      <!-- Loading More  -->
      <list-item type="loadMore" class="load-more" if="{{loadMore}}">
        <progress type="circular"></progress>
        <text>More</text>
      </list-item>
    </list>
 
  </div>
</template>
<style>
 
  .container{
     flex-direction: column;
  }
 
  .list {
    padding-left: 10px;
    padding-right: 10px;
    columns: 1;
    flex-direction: column;
    border-color: #FF0000;
    border-width: 5px;
  }
 
  .item {
    flex-direction: column;
    align-items: flex-start;
    margin-bottom: 15px;
    border-color: #9400D3;
    border-width: 5px;
    margin-right: 20px;
    background-color: #f76160;
  }
 
  .load-more {
    justify-content: center;
    align-items: center;
    height: 100px;
    border-color: #bbbbbb;
    border-bottom-width: 1px;
  }
 
  .btn-little {
    flex: 1;
    height: 80px;
    margin-left: 15px;
    border-radius: 5px;
    color: #ffffff;
    font-size: 30px;
    text-align: center;
    background-color: #0faeff;
  }
 
  .nav {
    padding-left: 60px;
    padding-right: 60px;
    padding-bottom: 30px;
  }
 
  .nav-item {
    flex: 1;
    padding-bottom: 30px;
    border-bottom-width: 5px;
    border-color: #fbf9fe;
    font-size: 35px;
    color: #666666;
    text-align: center;
  }
 
</style>
<script>
  import prompt from '@system.prompt'
 
  export default {
    data: {
      componentName: 'list',
      loadMore: true,
      listAdd: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],
      listData: [],
      scrollPage: false,
    },
    onInit: function () {
      this.$page.setTitleBar({ text: 'list' })
      for(var index = 0;index < 100;index++){
          this.listData[index] = 'A';
     }
      
    },
  
    scrollbottom: function () {
      prompt.showToast({
        message: 'The list slides to the bottom and starts loading other data.'
      })
      // Load next page
      var that = this
      var renderData = [].concat(that.listData, that.listAdd)
      setTimeout(function () {
        that.listData = renderData
      }, 1000)
    },
 
    // monitoring during sliding
    scroll: function (e) {
      let msg = 'scroll' + '.scrollX:' + e.scrollX
        + ' .scrollY:' + e.scrollY
        + ' .scrollState:' + e.scrollState
      console.info(msg)
    },
  
  
    listItemClick: function (e) {
      e.stopPropagation()
      console.info('List Item is clicked.')
      prompt.showToast({
        message: 'List Item is clicked.'
      })
    },
    listClick: function (e) {
      e.stopPropagation()
      console.info('List is clicked.')
      prompt.showToast({
        message: 'List is clicked.'
      })
    },
 
    listLongPress: function (e) {
      e.stopPropagation()
      console.info('List is long pressed.')
      prompt.showToast({
        message: 'List is long pressed.'
      })
    },
  }
</script>

问题分析
以上代码使用list、list-item来加载大规模数据,但是使用方法不当,导致list-item的视图view没有被复用。

我们知道快应用的引擎是一个android apk,list、list-item的实现最终都是通过android的ListView、BaseAdapter等这些实现的,了解这些其实知道列表界面上超过屏幕显示的区域是不会重新创建视图的,而是复用第一次在界面上可见区域的那些view的,只需要把数据刷新一下即可。每一行的视图view其实就是list-item。

以上代码虽然看起来是列表,但是只有1个list-item,开发者在list-item内部使用了for循环,每循环一次,都会创建一个新的view,当数据量很大时,内存占用越多,手机内存吃紧,不断地做申请、释放内存的操作,应用性能受到严重影响,导致滑动卡顿。

解决方法
基于list组件的特点,在list-item内部内部需谨慎使用if指令或for指令,根据列表每行数据特点,在list-item上设置不同的type,尽可能复用list-item,在list-item上使用for语句。修改后代码如下(注意list-item部分):

<template>
  <div class="container">
    <div class="nav">
      <text class="nav-item">list</text>
    </div>
 
    <!-- List -->
    <list class="list" onclick="listClick" onlongpress="listLongPress"
      onscrollbottom="scrollbottom"  id="list" scrollpage="{{scrollPage}}">
     
      <list-item type="listItem" class="item item-color"  onclick="listItemClick"
        for="{{listData}}">
        <text  class="txt">{{$item}}--{{$idx}}</text>
      </list-item>
 
      <!-- <list-item type="listItem" class="item"  onclick="listItemClick"
        if="{{listData.length>0}}">
           <div for="{{listData}}" style="flex-direction:column;">
                <text  class="txt">{{$item}}--{{$idx}}</text>
           </div>
      </list-item> -->
 
      <!-- Loading More  -->
      <list-item type="loadMore" class="load-more" if="{{loadMore}}">
        <progress type="circular"></progress>
        <text>More</text>
      </list-item>
    </list>
 
  </div>
</template>

代码运行效果图对比:


图1 修改后效果


图2 修改前效果

从上面效果图中我们看到,虽然都是列表数据,但是图1每一行都是一个list-item(list-item的css中背景色设置的是红色),而且type值都一样,能很好地复用list-item,但是图2中只有1个list-item,里面列表数据都是作为list-item的子节点。图2的效果和使用普通的div加载大量列表数据是一样的,根源在于开发者没有很好地理解list、list-item的原理。

欲了解更多详情,请参见:

快应用list开发指导:

https://developer.huawei.com/...

原文链接:https://developer.huawei.com/...
原作者:Mayism


华为开发者论坛
352 声望56 粉丝

华为开发者论坛是一个为开发者提供信息传播、开发交流、技术分享的交流空间。开发者可以在此获取技术干货、华为源码开放、HMS最新活动等信息,欢迎大家来交流分享!


引用和评论

0 条评论