# 混入

# 介绍

微信小程序的 behaviors (opens new window) ,与 vuemixins 相似,为了与 vue 保持一致,文件命名为 mixins

# 目录结构

└─ mixins                   # 混入
  └─ getInfiniteData.js     # 无限滚动加载

# getInfiniteData.js 无限滚动加载

本项目中存在大量的列表类页面,如 正在热映Top 100影评列表 等,这些页面存着共性方法是 滚动触底加载更多下拉刷新方法;所以将这些页面的共性部分抽离出来共用,可以极大的减少重复代码。

# 小程序组件的 behaviors 方法:

  • 虽然小程序官方文档中 behaviors 只可在组件中使用,但实际使用发现在 页面级也是有效的
  • 由于组件与页面都有 ready 方法,所以 behaviors 中会在该生命周期中调用页面/组件中的接口请求。
// 引入计算属性
const computedBehavior = require('miniprogram-computed').behavior

module.exports = Behavior({

  behaviors: [computedBehavior],

  // 初始化一些数据
  data: {
    id: null,           // 详情页面的资源ID,可选值
    loading: false,     // 页面接口请求loading
    noData: false,      // 无数据
    noMoreData: false,  // 无更多数据
    form: {},           // 查询条件
    list: [],           // 接口返回 data 中的列表数据
    total: 0,           // 列表数据总条数
    page: 1,            // 当前页码
    per_page: 20,       // 每页返回数量
    _isError: false,    // 接口报错
  },

  computed: {
    //  是否显示加载骨架屏动画
    isShowSkeleton(data) {
      // 只有接口请求中并且当前为第一页时才显示骨架屏,否则显示页底的 <m-loadmore /> 组件
      return data.loading && data.page === 1;
    },
  },

  ready() {
    // 调用加载方法
    this.loadMore();
  },

  methods: {

    // 下拉刷新时重置data数据
    resetData() {
      // 已在下拉加载状态是禁止接口请求,防止重复请求导致数据重复
      if (this.data.loading) return;

      this.setData({
        loading: false,
        page: 1,
        list: [],
        total: 0,
        noData: false,
        noMoreData: false,
        _isError: false,
      })

      this.loadMore();
    },


    /**
     * @desc 分页获取更多数据
     * @param Function fn 接口请求方法,在 api 文件夹在定义的方法
     * @param Object ...args 接口请求参数,可以未多个,使用...运算符对参数进行分解
     * @return 无返回结果,相关数据已挂载在当前组件实例上
    */
    async getData(fn, ...args) {
      // 接口请求失败时阻止继续请求
      if (this.data._isError) return;

      // 设置无更多数据时标志,当前为第一页且无数据时,可以再次触发请求
      if (this.noMoreData && this.page === 1 && this.list.length === 0) {
        this.noMoreData = false;
      }

      // 接口请求中或者无更多数据时,拦截请求
      if (this.data.loading || this.data.noMoreData) return;

      // 接口请求标志位
      this.setData({ loading: true })

      // 请求参数
      let params = {
        page: this.data..page,
        per_page: this.data.per_page,
        ...this.data.form
      };

      /**
       * @desc 接口请求
       * @param ...args 多个请求参数,需要与 api 接口定义一致
       * @param params 请求query中携带参数
       * @return code 状态码 200 为成功
       * @return data 数据列表
       * @return total 数据总条数
      */
      const { code, data, total } = await fn(...args, params);

      // 接口请求结束时关闭下拉状态(刷拉刷新时有效)
      wx.stopPullDownRefresh();

      if (code === 200) {
        let noData = false;

        // 是否无数据,当前为第一页且返回数据为空
        if (this.data.page === 1 && data.length === 0) {
          noData = true;
        } else {
          noData = false;
        }

        this.data._isError = false;

        // 返回数据条数小于设置的每页请求条数时,则没有更多数据了
        if (data.length < this.data.per_page) {
          this.setData({
            noMoreData: true
          })
        }

        // 返回的数据追加到原有列表中
        this.data.list.push(...data)

        this.setData({
          noData,
          list: this.data.list,
          page: ++this.data.page, // 返回成功后当前页码+1
          total: total || 0,
        })

      } else {
        // 接口失败时设置失败标志位为 true,阻止滚动继续发起请求
        this.data._isError = true;
      }

      // 渲染完成后隐藏加载动画
      wx.nextTick(() => {
        this.setData({
          loading: false
        })
      });
    },
  },
})

# 使用示例

<view class="list">
  <!-- 列表内容 -->
  <list-item 
    wx:for="{{ list }}" 
    wx:key="id" 
    item="{{ item }}"
  />
</view>

<m-loadmore loading="{{loading}}" />
Page({
  // 注入混入内容,混入方法已在 app.js 中挂载在 wx 对象上
  behaviors: [wx.getInfiniteData],

  // 页面滚动触底会调用 loadMore 方法,getData 方法在上面的 mixins 里面定义了
  loadMore() {
    this.getData(request, ...params);
  },

  // 触底方法请看下面 Page 扩展
})

# Page 构造函数的扩展

该方法主要扩展了下拉刷新与滚动触底两个方法,如有其它需求可以自主扩展。

  1. onPullDownRefresh
    下拉刷新方法,需要在页面的 app.json 文件中开启下拉刷新配置。
{
  "enablePullDownRefresh": true
}
  1. onReachBottom
    页面 .json 文件中 onReachBottomDistance: 50 配置为默认值,如触底高度无需改变,可不写。

以下为 Page 扩展代码

const expandPage = () => {
  const originalPage = Page;

  Page = function (config) {
    // 获取 Page 下拉刷新与触底方法
    const { onPullDownRefresh, onReachBottom } = config;

    // 下拉刷新
    config.onPullDownRefresh = function () {
      // 页面存在下拉刷新方法时,则使用页面中定义的方法
      if (typeof onPullDownRefresh === 'function') {
        onPullDownRefresh.call(this);
      } else {
        // 页面存在 resetData 与 loadMore 方法时才会调用该方法
        if (typeof this.resetData === 'function' && typeof this.loadMore === 'function') {
          this.resetData();
        }
      }
    }

    // 重写触底方法
    config.onReachBottom = function () {
      // 页面存在触底方法时,则使用页面中定义的方法
      if (typeof onReachBottom === 'function') {
        onReachBottom.call(this);
      } else {
        // 页面存在 loadMore 方法,则触底时调用该方法
        if (typeof this.loadMore === 'function') {
          this.loadMore();
        }
      }
    }
    // 返回重写后的 Page 方法
    return originalPage(config)
  }
}

App({
  async onLaunch() {
    // 调用该方法将全局 Page 重写
    expandPage()
  }
})

上次更新: 2022-10-25 9:05:36 ├F10: PM┤