前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Meteor 分页包 alethes:pages 详解

Meteor 分页包 alethes:pages 详解

作者头像
我与梦想有个约会
发布2023-10-20 20:17:58
1920
发布2023-10-20 20:17:58
举报
文章被收录于专栏:jiajia_dengjiajia_deng

在做大部分真实应用的 web 项目过程中,都会有一样不可或缺的需求,那就是分页。Meteor 项目也不例外,同样会有这样的需求,本文给大家介绍的就是一个非常好用的分页包 alethes:pages。它可以实现简单的根据页数分页,也可以实现强大的滚动分页。内部还支持利用多个 collection 数据进行分页。下面我们就来详细的了解它。 注意:以下内容多是对官方包的一种简述和翻译,如果需要更详细的内容可以参考官方地址。

特性

官网上介绍了很多它的特性,由于我的英文不是很好,我只能翻译我们大家非常关注的点。

  • 仅 subscribe 当前页需要的数据,并不是一次性 sub 所有数据
  • 本地缓存,获取过的数据本地存储,避免返回时重新获取
  • 在加载当前页过程中,预取下一页的数据,确保下一页的时候无缝过度
  • 多个集合产生一个分页数据
  • 支持 bootstrap 2/3 的分页导航模版
  • 支持 iron-router
  • 页面无限滚动加载特效

安装

代码语言:javascript
复制
meteor add alethes:pages

官网

atomsphere - https://atmospherejs.com/alethes/pages/ github - https://github.com/alethes/meteor-pages/

Demo 演示

基本的分页 - http://pages.meteor.com/ 表格 (快速渲染) - http://pages-table.meteor.com/ 多个 collection 自动刷新 - http://pages-multi.meteor.com/ 滚动加载效果 - http://pages3.meteor.com/

使用

要使用这个包的功能非常简单,首先用你要实现分页的 collection 生成一个 PlayersPages 分页对象。 这里假设之前已经存在一个 Players 的 collection,你需要对它的数据进行分页显示,我们按如下方式对他下手即可。

代码语言:javascript
复制
// 原有存放数据的 collection
Players = new Mongo.Collection("players");

// 根据已经有数据的 collection 生成一个 Meteor.Pagination 类型的对象。
this.PlayersPages = new Meteor.Pagination(Players, {
  // 指定需要分页所使用的模版
  templateName: "playersTemplate"
});

上面的代码可以看到,我们指定了分页所使用的模版名字叫 playersTemplate,此时,我们只需要在增加一个这样的模版即可。

代码语言:javascript
复制
<template name="playersTemplate">
    {{> pages}}
    {{> pagesNav}}  <!--分页导航按钮-->
</template>

新建的模版中再导入另外两个模版 pagespagesNav,这个两个模版是分页包 alethes:pages 给我们创建的,用来显示数据用。如此这样添加后,playersTemplate 模版就可以分页显示 Players collection 的数据了。

个性化

但具体每页显示多少数据、显示数据的样式如何定义、分页导航按钮能不能换成滚动屏幕自动加载瀑布流的方式?等等类似的问题,这个包都提供了解决方案。我们先来看一下它比较重要的几个参数,用一个我们已经使用到项目中的代码片段+注释的方式,来给大家演示这个包的各种参数(更多参数可以参考 github 上的介绍)。

代码语言:javascript
复制
this.ProductPages = new Meteor.Pagination(Products, {
    // 调试模式
    debug: true,
    // 认证函数,内部可以写一些过滤,让一些不想被用户看到的数据被过滤掉
    auth: function (skip, sub) {

        // 判断是否有用户登陆
        if (!sub.userId) {
            return false;
        }

        // 清空过滤器内容
        this.filters = {};

        // userSettings 是每个不同用户本地不同的分页属性,如果有多个访问者,每个访问者可以设定自己的分页属性
        let userSettings = this.userSettings[sub._session.id]  {}
            , uFields = userSettings.fields  this.fields
            , uSort = userSettings.sort  this.sort
            , uPerPage = userSettings.perPage  this.perPage
            , _filters = userSettings.filters  this.filters
            , _options = {fields: uFields, sort: uSort, limit: uPerPage, skip: skip};

        // 将不想给用户看到的数据过滤掉,这里你可以写自己的过滤代码
        let shop = ReactionCore.getCurrentShop(this);
        if (shop) {
            _.extend(_filters, {shopId: shop._id});
            // products are always visible to owners
            if (!(Roles.userIsInRole(sub.userId, ["owner", "admin", "createProduct"], shop._id))) {
                _.extend(_filters, {isVisible: true});
            }
        }

        // 返回新的分页属性
        return [_filters, _options];
    },
    // 允许每个不同用户设定的分页属性有那些选项
    availableSettings: {
        limit: true,
        sort: true,
        filters: true
    },
    // 分页最外部的 div class 名
    divWrapper: 'row',
    // 是否启用滚动分页(瀑布流)
    infinite: true,
    // 滚动条加载到什么位置时加载下一组分页数据,这个参数问题比较多,后面再介绍
    infiniteTrigger: .8,
    // 滚动加载模式下,后续页面最小的加载时间间隔
    infiniteRateLimit: 1,
    // 暂时不用
    infiniteStep: 1,
    // 分页时每页数据中所有子项目所用的分页模版,你可以设定各种你已经美化过的模版
    itemTemplate: "productGridItems",
    // 分页所用的模版
    templateName: "infiniteProducts",
    // 最多显示多少数据
    pageSizeLimit: 1000,
    // 第一页加载多少数据
    perPage: 4,
    // 最大 subscribe 的数据两
    maxSubscriptions: 500,
    // 预加载页数,dataMargin * perPage + perPage = 页面首次打开显示的数据量
    dataMargin: 1,
    // 对数据进行排序
    sort: {
        order: 1, title: 1
    }
});

以上时参考官方给出的无限滚动模式下所使用到的参数,其中 infiniteTrigger 参数我在使用过程中遇到了很多问题。第一个问题就是滚动条滚动到 0.8 的位置后,数据不会自动继续加载。修正了第一个问题后,随后出现的问题时滚动条并非到 0.8 的位置才加载数据,而是我滚动条只要一动,下一页的数据就自动加载出来了,这样明显不对。 我们通过分析处理 infiniteTrigger 参数的源代码来判断问题出在了哪里,请看代码和注释。

代码语言:javascript
复制
Pages.prototype.setInfiniteTrigger = function() {
  return $(window).scroll((_.throttle(function() {
    var l, oh, t;
    // 获取你设定的 infiniteTrigger 值
    t = this.infiniteTrigger;
    // 获取当前 body 的高度,应该是页面所有数据的高度之和
    oh = document.body.offsetHeight;
    // 判断你设定的 infiniteTrigger 值是否大于 1 做不同的操作
    if (t > 1) {
      l = oh - t;
    } else if (t > 0) {
      // l = body 的高度 * 你设定的 infiniteTrigger 的高度
      // 假设 body 已经有 2500 的高度了,2500 * 0.8 = 2000
      l = oh * t;
    } else {
      return;
    }
    // 如果当前可视的页面高度 + 滚动条的位置 >= 上面计算出来 l 的值
    if ((window.innerHeight + window.scrollY) >= l) {
      // 加载下一组数据
      if (this.lastPage < this.sess("totalPages")) {
        return this.sess("currentPage", this.lastPage + 1);
      }
    }
  }, this.infiniteRateLimit * 1000)).bind(this));
};

分析过代码后,我得出判断,第一个问题时由于我们页面中有一个很大的 div 当作 body 来用,滚动的时候实际时 div 的滚动条在滚动,而 body 的滚动条一直在 0 的位置,所以无论你看到的 div 的滚动条滚动到了哪里,下一组数据都不会继续加载。 在第一个问题解决完以后,再继续分析第二个问题,首先清楚两个概念。 document.body.offsetHeight - body 整个页面的高度,一般是页面中所有元素加起来的高度之和。 window.innerHeight - 可视的高度,当前浏览器显示了多少内容,这些内容的高度之和。 我分别在页面中打印了一下 window.innerHeight 的值和 document.body.offsetHeight 的值,赫然发现两个值时相等的,所以导致我滚动条刚刚开始滚动的时候,window.innerHeight + window.scrollY 一定大于 document.body.offsetHeight * infiniteTrigger 的值。知道原因了,如何解决呢?为什么 document.body.offsetHeight 的值与 window.innerHeight 的值一样大呢?不应该是页面所有元素的高度吗?在分析别人的代码对比后发现,原来我们的 body 被设定了一个 css 样式为 height: 100vh;,该属性的意思就是将 body 的高度设定为可视的高度,所以 body 的高度与 window.innerHeight 就没什么区别了,最终导致了如上问题,将这个 css 修改为 height: 100% 即可解决问题。

总结

这个包需要研究的地方还有很多,希望我介绍的内容能帮助大家在后期开发 Meteor 项目减少一些麻烦,一起努力共勉。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2015-12-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 特性
  • 安装
  • 官网
  • Demo 演示
  • 使用
  • 个性化
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档