在Meteor中实现无限滚动和实时更新的理想方法

12

我有一组大数据,希望通过Meteor实现无限滚动加载分块数据的方式,以避免一次性加载大型数据集;相反,需要在需要时加载数据集的块。如何在保留由infinite scroll加载的每个部分的实时页面更新的同时实现?

4个回答

8

这是我用于电影数据库的游乐场的代码。我尝试使用jQuery事件,但最终决定只需连续传输数据即可。我的测试集有680条记录,包含20个字段和一个缩略图字段,每个封面大小在10-20kb之间。以下是Coffeescript代码:

Movies = new Meteor.Collection("movies")
if Meteor.isClient
  # starting with 10 items that the user sees a page fast
  Session.set("queryLimit", 10)

  # getting the item count from the server collection
  Meteor.call 'getMoviesCount', (err, result) =>
    Session.set('itemsInQuery',result)
  # subscribe to a subset of the collection, and change the Session var on completion
  # automatically changing the limit of the publishing function with autorun
  Meteor.autorun ->
    Meteor.subscribe "MoviesList", Session.get("queryLimit"),  onComplete = ->
      if Session.get("queryLimit") < Session.get('itemsInQuery')
        queryLimit = Session.get("queryLimit") + 30 #change iterator depending on result size 
        Session.set("queryLimit", queryLimit )

  # Client helper to add more items to handlebars template
  Template.app.helpers movies: ->
    Movies.find({})

if Meteor.isServer
  Meteor.publish "MoviesList", (limit) ->
    # show whole collections once limit is reached, by setting limit to 0
    limit = 0 if limit > Movies.find().count() 
    console.log new Date(), limit
    Movies.find({}, { limit: limit, fields: {title:1 } } ) #use fields to test different result sizes  

  Meteor.methods
    getMoviesCount: (query) ->
      return Movies.find().count()

并且HTML:

<body>
  {{> app}}
</body>
<template name="app">
    {{#each movies}}
      <p>{{title}}</p>
    {{/each}}
</template>

我进行了一些快速的性能测试,结果表明对于每条记录只有几行文本的情况下,向客户端发送数据最快的方式是限制在约100条记录。我还尝试了包含在文件中的10-20KB缩略图。当使用更大的资源时,当限制大于30条记录时,Chrome变得相当不响应。

以下是在本地主机上进行的一些统计数据(每个运行了3次):

limit   seconds till last record rendered.
  0     79s 
  010   121s
  020   64s  
  030   45s
  050   34s
  100   16s
  200   16s

有趣的是,当 Meteor 服务器一次性发送整个页面(limit=0)时,需要大约79秒。我不确定这是如何可能的,因为连续传输应该是最快的。所以我的统计数据可能存在问题。有什么想法吗?


3

我的解决方案与Andrej的类似。因为我在数据库中有很多记录,我不想让服务器一次性发送它们,所以在服务器上我这样做:

Meteor.publish("items", function(page) {
    if(!page)
        page = 1;
    var limit = 30 * page;
    return Items.find({}, {limit: limit};
}

客户端:

Template.templateName.items = function () {
    Meteor.subscribe("items", Session.get("page"));
    return Items.find({});
}

以下是检测页面是否到达底部的jQuery函数:

$(window).scroll(function(){
    if ($(window).scrollTop() == $(document).height()-$(window).height()){
        Session.set("page", Session.get("page") + 1);
    }
});

此外,可以在模板创建回调函数中设置页面的初始页面:
Template.templateName.created = function() {
    Session.setDefault("page", 1);
};

在模板中,我使用{{#each}}来展示这些项目。另外,我需要检查是否还有更多的记录。

更好的解决方案是在创建模板时显示30个项目,并在滚动后获取30个项目,但我不知道如何展示它们。我可能有一个解决方案,但我懒得实现,因为我不确定它是否可行。我正在考虑将渲染的HTML附加到页面中。


1
您可以设置一个会话密钥,用于存储您当前所在的页面。例如:
Session.set("cur_page", 1);

然后将其输入到您的查询中,例如:

Template.list.items = function() {
  return Collection.find({}, {
    skip: Session.get("cur_page") * page_size,
    limit: page_size
  });
}

然后你只需使用 Session.set("cur_page", 2) 更新值,你的列表就会重新绘制,显示第二页的项目。大功告成!


需要注意的是,根据结果集大小,skip()并不总是理想的解决方案,另一种解决方案是使用范围查询来模拟skip(),而不会丢失索引使用以获取更多页面的记录。 - Sammaye
我刚意识到分页这个词是错误的。我想说的是无限滚动,因为每次你滑到页面底部时,会加载技术上的“页面”或数据部分,但你仍然需要更新以确定你是否在页面底部或顶部。我将编辑问题以澄清这一点。 - HGandhi

1

我还没有尝试过这个,但应该可以使用。因此,如果您有自己的集合:

var Posts = new Meteor.Collection("posts");

并且您的模板:

<template name="posts">
    {{#each posts}}
        {{> post}}
    {{/each}}
</template>

这可以是您的模板助手:

This could be your template-helper:

Template.posts.helpers({
    posts: function() {
        return Posts.find({}, {limit: Session.get("current_page")*PAGE_SIZE});
    }
});

PAGE_SIZE是每个“页面”上的帖子数量。每次用户滚动到页面底部时,您需要增加“current_page”会话变量。然后模板将被更新,您的无限滚动(希望如此)就可以工作了!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接