Weapp影视评分项目开发(11):下拉刷新与上拉加载的实现

267 阅读6分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第11天,点击查看活动详情

前言

本篇将实现 “正在热映” 与 “即将上映” 两个页面模块,他们很相似,都是整屏的列表页,在这两个页面中,我们将首次使用计算属性 computed、使用官方 api 实现下拉刷新与上拉加载更多功能。

知识点

computed 的使用 下拉刷新 上拉加载 position: sticky 1px 边框

一、影视信息卡组件的实现

上一篇我们详细介绍了 movie-row 组件的实现,movie-item 组件与它很像,所以这一篇,我们不再将它如何实现,而是介绍下这个组件需要注意的地方。

1. 组件预览

image.png

2. flex 弹性布局

该组件使用 flex 实现左右布局,左侧为海报区域固定宽度 164rpx,右侧为内容区域,宽度为 flex: 1 弹性。此时如果 flex 区域内容的宽度超出 calc(750rpx - 164rpx - 60rpx) (即去除左边海报区域,两侧外边距)宽度时,海报区域的宽度将会被压缩,这是因为 flex 的默认收缩规则为 flex-shrink: 1; ,我们将其收缩规则改为 flex-shrink: 0; 即可。
主要代码如下:

<!-- components/movie/movie-item/index.wxml -->
<!-- 内置的下边框 m-hairline--bottom -->
<view class="movie-item m-hairline--bottom">
  <view class="movie-item__poster"></view>
  <view class="movie-item__content"></view>
<view>
/* components/movie/movie-item/index.scss */
.movie-item {
  display: flex;
  flex-shrink: 0;   /* 设置收缩规则 */
  margin: 0 30rpx;
  .movie-item__poster {
    width: 164rpx
  }
  .movie-item__content {
    flex: 1;
  }
}

3. 1px 边框的使用

mind-ui-weapp 组件库内置了 Retina 屏下的 1px 边框,之前的文章中已在 app.scss 文件中引入了该样式文件,信息卡的下边框只需要在外层元素添加 m-hairline--bottom 样式属性即可。

二、列表页面的实现

1. 效果预览

下拉刷新与上拉加载:

下拉刷新 上拉加载

1. computed 属性的使用

之前在 第04篇三方组件的使用中,我们介绍过 computed 组件的安装与使用,在这次的“即将上映”页面,我们将正式使用它来实现影视按日期分组的需求。
需求:“即将上映”的接口分页返回的数据,会按照上映时间排序,每个影视字段中会有一个 release_date (上映时间)字段,我们需要将日期相同的影视放在一个分组中,结构如下:

image.png image.png

如果没有 computed 属性,我们一般在获取到数据后,写个函数将数组转成需要的格式,有了 computed 后,处理变的便捷了很多,我们无需在数据返回后使用函数处理数据,把数据交给 computed 去自动处理:

// pages/movie/coming/index.js
Page({
  behaviors: [wx.computedBehavior], // 引入 computed 属性支持

  data: {
    list: [], // 影视信息列表
  }
  
  computed: {
    // 将影视按日期分组,注意 computed 中无法使用 this,需要把 data 作为参数传入
    groups(data) {
      let groups = [];
      data.list.map((movie) => {
        const gLen = groups.length;
        if (gLen && movie.release_date === groups[gLen - 1].date) {
          groups[gLen - 1].children.push(movie);
        } else {
          const group = {
            date: movie.release_date,
            children: [movie],
          };
          groups.push(group);
        }
      });

      return groups;
    }
  }
})
<!--pages/movie/coming/index.wxml -->
<view class="main">
  <view class="group m-hairline--bottom" wx:for="{{groups}}" wx:for-item="group" wx:key="date">
    <view class="group-title">{{ group.date }}</view>
    <view class="group-children">
      <movie-item wx:for="{{group.children}}" wx:key="id" movie="{{item}}" is-coming />
    </view>
  </view>
</view>

2. position: sticky 属性的使用

以上的 group-title 属性的元素,滚动到顶部时会有吸顶效果,我们日常项目中,也经常有这样的需求,早前我们需要用监听滚动距离的方式,给元素添加 position: fixed 属性使其固定在某个位置,现在我们只需要几行 css 属性就可以实现,他就是 position: sticky ,我们通过 caniuse 网站查看该属性的浏览器支持度已经很高了(在不支持的浏览器中,大多设备老旧,性能较低,js 实现的方式不会很流畅,算是优雅降级吧)。代码如下:

.group-title {
  z-index: 1;
  position: sticky;
  top: 0;
  padding: 0 30rpx;
  height: 80rpx;
  line-height: 80rpx;
  font-size: 28rpx;
  font-weight: bold;
  background-color: #fff;
}

三、上拉加载

1. 上拉加载的实现

上拉加载是移动端实现分页能力的常用方式,在 web 端我们一般通过监听页面滚动距离,如果滚动距离 - 页面高度的值在一定范围内,则调用接口获取下一页的数据。微信小程序替我们封装好了这一些判定条件与方法,它提供了一个名为 onReachBottom 方法用于监听用户上拉触底事件,我们只需要在事件触发后调用接口即可。
以“即将上映”实现为例:

// pages/movie/coming/index.js
import { getMovieComing } from "../../../api/api"; // 引入接口

Page({
  data: {
    loading: false, // 接口请求状态
    movies: [],     // 影视列表
    page: 1,        // 当前页
    per_page: 20,   // 每页条数
    total: 0,       // 总条数
  },
  
  onLoad() {
    this.getMovieComing();
  },
  
  // 获取即将上映影视列表
   async getMovieComing() {
    if(this.data.loading) return;  // 防止上拉加载重复请求
    this.loading = true;
    const params = {
      page: this.data.page,
      per_page: this.data.per_page,
    }
    // ES6 对象解构语法,未使用 try catch 是因为接口层做了处理,失败也会有返回值不中断执行
    const { code, data, total } = await getMovieComing(params);
    if(code === 200) {
      this.setData({
        movies: [...this.data.movies, ...data], // 使用 ES6 展开运算符将下一页数据添加到原有列表中
      })
      this.data.page++; // 页码+1
    }

    this.loading = false; // 关闭加载状态
  },
  // 页面触底
  onReachBottom() {
   this.getMovieComing();
  },
})

触底距离使用默认的50像素即可,无需在 .json 文件中设置

四、下拉刷新

小程序使用下拉刷新功能非常便捷,只需要一个配置 + 2个方法即可实现:

1. 开启下拉刷新配置:

// coming/index.json
{
  "enablePullDownRefresh": true
}

2. 下拉刷新的两个方法:

wx.startPullDownRefresh(); // 调用后将触发下拉刷新动画,效果与用户手动下拉刷新一致
wx.stopPullDownRefresh();  // 关闭下拉刷新动画

3. 使用

日常业务中,开启下拉刷新配置后,我们需要在接口获取到数据后关闭下拉刷新动画;开启下拉刷新方法很少使用,业务场景是被当做页面初始进入时的加载动画。
如果是列表页有分页的场景,wx.startPullDownRefresh() 方法建议放在 onLoad 方法中,并在调用接口前使用,如果放在接口中调用就需要判断当前分页是否为第一页了,增加代码冗余,示例如下:

Page({
  // data 代码同上
  onLoad() {
    wx.startPullDownRefresh(); // 开启下拉刷新动画
    this.getMovieComing();
  },

  // 下拉刷新事件,需要重置列表中的数据为初始值
  onPullDownRefresh() {
    this.setData({
      movie: [],
      page: 1,
      total: 0,
    })
    
    this.getMovieComing();
  },

  // 其它代码同上
  async getMovieComing() {
    // 省略部分代码
    const { code, data, total } = await getMovieComing(params);
    // 当接口请求完成后,无论是成功还是失败,都需要调用以下方法关闭下拉刷新动画
    wx.stopPullDownRefresh();
  }
})

总结

本篇文章主要介绍了 computed 的引入与使用,以及上拉加载与下拉刷新功能。因为这刷新与加载两个功能在项目中使用处特别多,下一篇我将会扩展 Page 属性,并将下拉刷新与上拉加载代码封装到 mixins 文件中。