微信小程序复杂列表开发优化全攻略:高性能 + 高颜值,手把手带你玩转小程序复杂列表

​微信公众号:小武码码码

大家好,我们结束了 Flutter 和 RN 的复杂列表的攻略后,今天我想与大家分享一下在小程序开发中经常遇到的复杂列表需求,以及针对这些需求的开发技巧和性能优化策略。在小程序开发过程中,列表可以说是最常见、最重要的组件之一,很多功能都离不开列表的支持。但是,当列表变得复杂起来,尤其是在样式、交互、数据量等方面有特殊要求时,对开发者的技术水平就提出了更高的挑战。

那么,究竟什么是小程序的"复杂列表"呢?在我看来,小程序的复杂列表通常具有以下特点:

  1. 列表项样式丰富多变,可能包含图片、文字、按钮等多种元素,布局也各不相同。
  2. 列表项高度不固定,需要根据内容自适应。
  3. 列表数据量较大,需要考虑性能优化,如懒加载、节流等。
  4. 列表需要支持各种交互,如下拉刷新、上拉加载、事件响应等。

接下来,我将从复杂列表的常见样式、开发方式、自适应布局、性能优化、与原生开发比较等方面,详细阐述小程序中复杂列表的开发技巧和注意事项。

一、复杂列表的常见样式和应用场景

在小程序开发中,我们经常会遇到各种样式的复杂列表需求,下面是一些常见的样式:

  1. 商品列表:每个列表项包含商品图片、名称、价格、销量等信息,可能还有"加入购物车"等交互按钮。常见于电商类小程序。
  2. 文章列表:每个列表项包含文章标题、摘要、作者、发布时间等信息,可能还有封面图片。常见于资讯、博客类小程序。
  3. 聊天列表:每个列表项包含用户头像、昵称、最新消息、未读数等信息。常见于社交、即时通讯类小程序。
  4. 评论列表:每个列表项包含用户头像、昵称、评论内容、评论时间等信息,可能还有回复、点赞等交互。常见于电商、社区类小程序。
  5. 订单列表:每个列表项包含订单编号、商品信息、金额、订单状态等信息。常见于电商、外卖、出行等服务类小程序。

这些列表样式看似不同,但它们有一些共同点:列表项通常由多个子元素组成,布局和样式各不相同;有些列表项可能还包含图片等较重的资源;很多列表都支持一些交互操作,如点击、长按等。

这些特点决定了复杂列表在开发时需要特别注意布局、渲染和交互的性能优化,以及代码的可维护性和可扩展性。下面,我将具体介绍复杂列表的几种常见开发方式。

二、复杂列表的开发方式

在小程序中,开发复杂列表主要有以下几种方式:

  1. 使用 <scroll-view> 组件:这是最基本的实现方式,通过在 <scroll-view> 中嵌套多个 <view> 来实现列表项。这种方式的优点是简单直观,易于理解;缺点是性能不佳,不适合数据量大的场景。

示例代码:

<scroll-view class="list" scroll-y>
  <view class="item" wx:for="{{items}}" wx:key="id">
    <image src="{{item.image}}" mode="widthFix" />
    <text>{{item.title}}</text>
    <text>{{item.price}}</text>
  </view>
</scroll-view>
  1. 使用 <block> 包裹列表项:这种方式通过使用 <block> 标签来包裹列表项,可以提高渲染性能。因为 <block> 不会生成实际的 DOM 节点,所以可以减少不必要的渲染开销。

示例代码:

<scroll-view class="list" scroll-y>
  <block wx:for="{{items}}" wx:key="id">
    <view class="item">
      <image src="{{item.image}}" mode="widthFix" />
      <text>{{item.title}}</text>
      <text>{{item.price}}</text>
    </view>
  </block>
</scroll-view>
  1. 使用 <virtual-list> 组件:这是微信小程序提供的一个高性能列表组件,专门用于渲染大量数据的列表。它的原理是只渲染可视区域内的列表项,其他部分在需要时再进行渲染,从而大大提高了渲染性能。

示例代码:

<virtual-list class="list" items="{{items}}" item-height="100">
  <template name="item">
    <view class="item">
      <image src="{{item.image}}" mode="widthFix" />
      <text>{{item.title}}</text>
      <text>{{item.price}}</text>
    </view>
  </template>
</virtual-list>
  1. 使用第三方组件库:有一些成熟的第三方组件库,如 Vant Weapp,提供了功能强大、性能优化的列表组件。这些组件通常经过了充分的测试和优化,可以显著提高开发效率和代码质量。

示例代码(使用 Vant Weapp):

<van-list
  finished="{{ finished }}"
  finished-text="没有更多了"
  @load="onLoad"
>
  <van-cell wx:for="{{items}}" wx:key="id">
    <image src="{{item.image}}" mode="widthFix" />
    <text>{{item.title}}</text>
    <text>{{item.price}}</text>
  </van-cell>
</van-list>

以上就是几种常见的复杂列表开发方式。在实际开发中,我们需要根据具体的需求和场景,选择合适的方式。对于数据量较小、交互简单的列表,使用 <scroll-view><block> 就可以满足需求;对于数据量大、性能要求高的列表,使用 <virtual-list> 或第三方组件库会是更好的选择。

接下来,让我们详细探讨一下复杂列表的高度自适应问题。

三、列表高度自适应的实现方式

在复杂列表中,我们经常会遇到列表项高度不固定的情况。比如,一个文章列表,每篇文章的标题长度、摘要内容都不尽相同,导致列表项的高度也各不相同。在这种情况下,如果我们给每个列表项设置固定高度,就会导致内容显示不完整或者留白过多的问题,影响用户体验。

因此,我们需要实现列表项的高度自适应,让列表项的高度能够根据内容自动调整。下面是几种常见的实现方式:

  1. 使用 flex 布局:通过为列表项设置 display: flexflex-direction: column,可以让列表项的高度自适应内容。这种方式的优点是简单易用,兼容性好;缺点是对于复杂的列表项布局,可能需要嵌套多层 flex 容器,增加了 CSS 的复杂度。

示例代码:

<view class="item">
  <view class="title">{{item.title}}</view>
  <view class="desc">{{item.desc}}</view>
  <view class="footer">
    <text>{{item.time}}</text>
    <text>{{item.author}}</text>
  </view>
</view>


.item {
  display: flex;
  flex-direction: column;
  padding: 20rpx;
  border-bottom: 1px solid #eee;
}
.title {
  font-size: 32rpx;
  font-weight: bold;
}
.desc {
  margin-top: 10rpx;
  font-size: 28rpx;
  color: #666;
}
.footer {
  display: flex;
  justify-content: space-between;
  margin-top: 20rpx;
  font-size: 24rpx;
  color: #999;
}
  1. 使用 grid 布局:与 flex 类似,grid 布局也可以实现列表项的高度自适应。通过为列表项设置 display: gridgrid-template-rows: auto,可以让列表项的高度根据内容自动调整。

示例代码:

<view class="item">
  <view class="title">{{item.title}}</view>
  <view class="desc">{{item.desc}}</view>
  <view class="footer">
    <text>{{item.time}}</text>
    <text>{{item.author}}</text>
  </view>
</view>


.item {
  display: grid;
  grid-template-rows: auto auto auto;
  padding: 20rpx;
  border-bottom: 1px solid #eee;
}
.title {
  font-size: 32rpx;
  font-weight: bold;
}
.desc {
  margin-top: 10rpx;
  font-size: 28rpx;
  color: #666;
}
.footer {
  display: flex;
  justify-content: space-between;
  margin-top: 20rpx;
  font-size: 24rpx;
  color: #999;
}
  1. 使用 inline-block + vertical-align:这是一种传统的布局方式,通过为列表项设置 display: inline-blockvertical-align: top,可以实现列表项的高度自适应。这种方式的优点是兼容性好,可以支持更复杂的布局;缺点是需要处理换行和间距的问题。

示例代码:

<view class="item">
  <view class="image">
    <image src="{{item.image}}" mode="widthFix" />
  </view>
  <view class="content">
    <view class="title">{{item.title}}</view>
    <view class="desc">{{item.desc}}</view>
    <view class="footer">
      <text>{{item.time}}</text>
      <text>{{item.author}}</text>
    </view>
  </view>
</view>


.item {
  display: inline-block;
  vertical-align: top;
  width: 100%;
  padding: 20rpx;
  box-sizing: border-box;
  border-bottom: 1px solid #eee;
  white-space: nowrap;
}
.image, .content {
  display: inline-block;
  vertical-align: top;
}
.image {
  width: 200rpx;
  height: 200rpx;
  margin-right: 20rpx;
}
.content {
  width: calc(100% - 220rpx);
  white-space: normal;
}
.title {
  font-size: 32rpx;
  font-weight: bold;
}
.desc {
  margin-top: 10rpx;
  font-size: 28rpx;
  color: #666;
}
.footer {
  margin-top: 20rpx;
  font-size: 24rpx;
  color: #999;
}
  1. 使用 JavaScript 动态计算高度:对于一些特殊的列表项布局,可能无法单纯使用 CSS 实现高度自适应。这时,我们可以通过 JavaScript 动态计算列表项的高度,然后再设置到对应的元素上。这种方式的优点是可以支持任意复杂的布局;缺点是需要额外的 JS 代码,可能会影响性能。

示例代码:

<view class="item" wx:for="{{items}}" wx:key="id" style="height: {{item.height}}px">
  <view class="title">{{item.title}}</view>
  <view class="desc">{{item.desc}}</view>
  <view class="footer">
    <text>{{item.time}}</text>
    <text>{{item.author}}</text>
  </view>
</view>


Page({
  data: {
    items: []
  },
  onLoad() {
    this.setData({
      items: [...Array(10)].map((_, i) => ({
        id: i,
        title: `Title ${i}`,
        desc: `Description ${i}`.repeat(Math.floor(Math.random() * 10)),
        time: new Date().toLocaleString(),
        author: `Author ${i}`
      }))
    }, () => {
      this.getItemsHeight();
    });
  },
  getItemsHeight() {
    const query = wx.createSelectorQuery();
    query.selectAll('.item').boundingClientRect(rects => {
      const items = this.data.items.map((item, i) => ({
        ...item,
        height: rects[i].height
      }));
      this.setData({ items });
    }).exec();
  }
});

以上就是几种常见的列表高度自适应实现方式。在实际开发中,我们需要根据具体的需求和场景,选择合适的方式。对于简单的列表项布局,使用 flexgrid 就可以满足需求;对于复杂的列表项布局,可能需要结合使用多种方式,或者通过 JavaScript 动态计算高度。

四、复杂列表的性能优化

在小程序开发中,复杂列表的性能优化主要涉及以下几个方面:

  1. 减少不必要的渲染:在列表渲染过程中,我们要尽量避免不必要的渲染操作,以减少性能开销。例如,在使用 wx:for 指令时,要为每个列表项指定一个唯一的 wx:key,以帮助小程序优化渲染;在条件渲染时,要使用 wx:if 而不是 hidden,因为 wx:if 会阻止不必要的渲染。

示例代码:

<!-- Good -->
<view wx:for="{{items}}" wx:key="id">
  ...
</view>

<view wx:if="{{condition}}">
  ...
</view>

<!-- Bad -->
<view wx:for="{{items}}">
  ...
</view>

<view hidden="{{!condition}}">
  ...
</view>
  1. 图片资源优化:在复杂列表中,图片往往是最占用渲染资源的元素。为了优化图片渲染性能,我们可以采取以下措施:
  • 压缩图片大小,减少图片加载时间。可以使用一些在线工具或者本地工具进行压缩。
  • 使用 CDN 图片,利用 CDN 的分发能力和缓存机制,加快图片加载速度。
  • 使用 wx:ifwx:hidden 控制图片渲染,避免不可见图片的加载和渲染。
  • 使用图片懒加载,只在图片进入可视区域时再进行加载和渲染。可以使用第三方组件库或者自己实现懒加载逻辑。

示例代码(图片懒加载):

<image 
  wx:if="{{item.show}}"
  src="{{item.image}}" 
  lazy-load
  binderror="onImageError"
  bindload="onImageLoad"
  data-index="{{index}}"
/>

<image 
  wx:else
  src="{{defaultImage}}"
/>


Page({
  data: {
    items: [],
    defaultImage: 'https://example.com/default.png'
  },
  onLoad() {
    this.setData({
      items: Array.from({length: 100}, (_, i) => ({
        id: i,
        image: `https://example.com/image${i}.png`,
        show: false
      }))
    });
  },
  onImageError(e) {
    const { index } = e.currentTarget.dataset;
    const items = this.data.items;
    items[index].image = this.data.defaultImage;
    this.setData({ items });
  },
  onImageLoad(e) {
    const { index } = e.currentTarget.dataset;
    const items = this.data.items;
    items[index].show = true;
    this.setData({ items });
  }
});
  1. 列表项组件化:对于一些重复出现的列表项,我们可以将其封装成组件,以提高代码的可维护性和可复用性。同时,组件化也可以减少列表渲染的压力,提高性能。

示例代码:

<!-- List Item 组件 -->
<template name="listItem">
  <view class="item">
    <image src="{{image}}" mode="widthFix" />
    <view class="content">
      <text class="title">{{title}}</text>
      <text class="desc">{{desc}}</text>
    </view>
  </view>
</template>

<!-- 使用 List Item 组件 -->
<template is="listItem" data="{{...item}}" wx:for="{{items}}" wx:key="id" />
  1. 虚拟列表:对于数据量非常大(成千上万)的列表,我们可以使用虚拟列表技术来优化性能。虚拟列表的核心思想是只渲染可视区域内的列表项,其他列表项在滚动时动态创建和销毁。这样可以大大减少 DOM 节点数量和内存占用,从而提高列表渲染和滚动的性能。

示例代码(使用 <virtual-list> 组件):

<virtual-list 
  wx:if="{{items.length > 0}}"
  items="{{items}}"
  item-height="100"
  viewport-height="{{viewportHeight}}"
  bindscroll="onScroll"
>
  <template name="item">
    <view class="item">
      <image src="{{item.image}}" mode="widthFix" />
      <view class="content">
        <text class="title">{{item.title}}</text>
        <text class="desc">{{item.desc}}</text>
      </view>
    </view>
  </template>
</virtual-list>

<view wx:else class="empty">
  暂无数据
</view>


Page({
  data: {
    items: [],
    viewportHeight: 0
  },
  onLoad() {
    this.setData({
      items: Array.from({length: 10000}, (_, i) => ({
        id: i,
        image: `https://example.com/image${i}.png`,
        title: `Title ${i}`,
        desc: `Description ${i}`
      }))
    });
    this.setViewportHeight();
  },
  setViewportHeight() {
    wx.createSelectorQuery()
      .select('.empty')
      .boundingClientRect(rect => {
        this.setData({
          viewportHeight: rect.height
        });
      })
      .exec();
  },
  onScroll(e) {
    console.log(e.detail);
  }
});

以上就是复杂列表性能优化的几个主要方面。在实际开发中,我们需要根据具体的场景和需求,选择合适的优化策略。总的来说,我们要遵循以下原则:

  • 减少不必要的渲染和重绘
  • 优化图片等大资源的加载和显示
  • 将重复的列表项封装成组件
  • 利用虚拟列表等技术优化大数据量列表
  • 避免在列表滚动时进行复杂计算或者 DOM 操作
  • 使用 Chrome 开发者工具或者小程序开发者工具进行性能分析和监控

五、小程序列表与原生列表的比较

那么,小程序中的列表与原生应用中的列表有什么区别呢?它们各自的优缺点又是什么呢?

从渲染机制上来说,小程序的列表渲染是基于 WebView 的,而原生应用的列表渲染是基于原生 UI 组件的。这导致了两者在性能上有一定的差异:

  • 在渲染速度上,原生列表通常比小程序列表更快。这是因为原生列表可以直接利用设备的 GPU 进行渲染,而小程序列表需要先在 WebView中渲染成 HTML/CSS,再由 WebView 转换为原生渲染,这个过程会带来一定的性能开销。
  • 在滚动流畅度上,原生列表通常比小程序列表更好。这是因为原生列表可以利用一些优化技术,如异步渲染、复用回收等,以保证滚动的流畅度。而小程序列表受限于 WebView 的性能,在滚动时可能会出现卡顿、白屏等问题。
  • 在内存占用上,小程序列表通常比原生列表更小。这是因为小程序的渲染机制是基于 Web 技术的,而 Web 技术在内存管理和回收方面比原生技术更加成熟和高效。

从功能和灵活性上来说,小程序列表和原生列表各有优势:

  • 在功能扩展上,小程序列表比原生列表更加灵活。因为小程序列表是基于 Web 技术实现的,所以可以很方便地引入各种第三方库和组件,实现各种炫酷的效果。而原生列表的扩展性相对较差,很多功能需要原生代码支持。
  • 在动态化能力上,小程序列表比原生列表更加强大。小程序列表可以直接使用 JavaScript 操作数据和 DOM,实现各种动态效果,如动画、交互等。而原生列表的动态化能力相对有限,很多操作需要借助原生语言(如 Java、Objective-C)来实现。
  • 在系统适配上,原生列表比小程序列表更加良好。原生列表可以充分利用系统提供的 UI 组件和 API,与系统 UI 风格保持一致,同时也可以避免很多兼容性问题。而小程序列表在不同系统和设备上可能会有不同的表现,需要开发者做更多的适配工作。
    总的来说,小程序列表与原生列表各有优劣:
  • 对于追求极致性能和流畅度的场景,如大数据量列表、高频刷新列表等,原生列表是更好的选择。
  • 对于追求开发效率和动态化能力的场景,如电商、资讯等经常变化的列表,小程序列表是更好的选择。
  • 对于追求功能扩展和炫酷效果的场景,如社交、游戏等个性化列表,小程序列表是更好的选择。
  • 对于追求系统一致性和兼容性的场景,如系统设置、管理工具等,原生列表是更好的选择。
    因此,在选择小程序列表还是原生列表时,我们需要综合考虑性能、功能、开发效率、适配等因素,根据具体的需求和场景来决定。

总结

以上就是我对微信小程序复杂列表的一些看法和经验总结。在小程序开发中,列表是一个非常重要和常见的组件,直接影响到用户的使用体验和对应用的评价。因此,我们在开发列表时,一定要重视起来,综合考虑列表的样式、性能、交互、兼容性等方面因素,并根据具体的需求和场景,选择合适的实现方案。

总的来说,我们在小程序列表开发中,要遵循以下几点原则:

  • 样式上,要注重列表的美观、整洁、易用,同时也要考虑不同设备和系统的兼容性。
  • 性能上,要尽量避免不必要的渲染和重绘,优化大资源的加载和显示,必要时使用虚拟列表等技术。
  • 交互上,要提供良好的滚动、刷新、加载等体验,同时也要注意手势、动画等细节。
  • 架构上,要合理组织和拆分列表的结构和逻辑,提高代码的可维护性、可扩展性、可复用性。
  • 监控上,要利用各种工具和手段,对列表的性能、错误、用户行为等进行监控和分析,持续优化和改进。
    当然,小程序列表的开发是一个复杂和系统的工程,我们不可能一蹴而就,也不可能面面俱到。我们需要在实践中不断积累经验,总结教训,与团队成员、社区伙伴交流分享,才能不断提高和突破。

这篇文章只是我对小程序列表开发的一点浅见,也难免有遗漏和错误之处。希望能抛砖引玉,引发更多的思考和探索,让我们共同进步,一起打造出更加优秀的小程序列表!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值