虚拟列表


虚拟列表可以用来渲染有大量数据的列表,并且不有有任何的性能问题。并且,它和所有的Framework7组件都是兼容的,包括搜索栏、无限滚动、下拉刷新、滑动删除和可排序列表。

虚拟列表的HTML结构

虚拟列表打HTML结构非常简单,他和常见的 列表 或者 多媒体列表只有一个区别,你必须把它的内容设置为空的。

  1. <!-- Virtual List -->
  2. <div class="list-block virtual-list">
  3.   <!-- 内容置空 -->
  4. </div>

其中:

  • virtual-list - 在使用虚拟列表的容器上增加这个类

初始化虚拟列表

现在,当我们有了列表的HTML,我们下一步是初始化它。我们需要用到这些App方法:

myApp.virtualList(listBlockContainer, parameters) - 使用parameters参数初始化虚拟列表

  • listBlockContainer - HTMLElement or string (with CSS Selector) 虚拟列表的容器. 必填.
  • parameters - object - 初始化参数,必填.
  • 这个方法会返回一个虚拟列表的实例

比如:

  1. var myList = app.virtualList('.list-block', {
  2.     items: [1,2,3,4],
  3.     height: 44
  4. });
注意,在初始化的时候虚拟列表的容器必须已经在DOM中。所以,如果你的虚拟列表不是用在首页,那么你必须在 pageInit (或者 pageBeforeInit) 事件中初始化。

虚拟列表的初始化参数

下面是所有的初始化参数:

参数类型默认值说明
itemsarray
包含列表条目的数组
rowsBeforenumber
在当前屏幕滚动位置之前需要渲染的条目数。默认是两倍当前屏幕大小需要的数量。(译者注:也就是说,假设你的屏幕是320px高,每一个条目是40px,那么当前屏幕上方会有(320/40*2) == 16条),加上当前屏幕中的8条和屏幕下方的8条,总共在DOM中存在32个条目)。
rowsAfternumber
在当前屏幕滚动位置之后需要渲染的条目数。默认是当前屏幕大小需要的数量。(译者注:参加上面的说明)。
colsnumber1每一行需要的条目数。如果你用了动态的高度,这个参数是不兼容的。
heightnumber or function(item)44如果是 数字,那么它是每一个条目的px高度。如果是 函数,那么它应该返回条目的高度。
templatestring or function
Template7 字符串模板,或者是Template7 编译后的模板,用来渲染单个条目。这个模板应该有一个条目的完整HTML结构,包括外面的 <li></li> 容器。
renderItemfunction(index, item)
这个可选方法用来自定义渲染条目的HTML。它是用来代替 tamplate 参数的。
dynamicHeightBufferSizenumber1This parameter allows to control buffer size on Virtual Lists with dynamic height (when height parameter is function) as a buffer size multiplier
cachebooleantrue启用/禁用已经渲染的条目的DOM缓存。启用的情况下,每一个条目都只会被渲染一次,后续都是对DOM的操作。如果你的条目中需要保存一些用户的操作(比如表单或者滑动删除)或者修改,那么这个参数是很有用的。
updatableScrollboolean
当前是否在滚动的时候会更新滚动条并触发滚动事件。默认情况下对所有iOS 8 以下的设备都是false。
搜索
searchByItemfunction(query, index, item)
搜索栏用的搜索方法,它接收三个参数:搜索词,条目的序号和条目自身。如果一个条目符合结果,你应该返回 true,否则返回false。
searchAllfunction(query, items)
搜索栏用到的搜索方法,它接受两个参数:搜索词,和包含所有条目的数组。你需要自己遍历并返回匹配的条目。
回调
onItemBeforeInsertfunction(list, item)
回调函数,当条目被插入到虚拟列表的 document fragment 之前执行。
onBeforeClearfunction(list, fragment)
回调函数,当前列表的DOM元素被删除并替换成新的 document fragment 之前执行。
onItemsBeforeInsertfunction(list, fragment)
回调函数,当前列表的DOM元素被删除之后,准备替换成新的 document fragment 之前执行。
onItemsAfterInsertfunction(list, fragment)
回调函数,新的 document fragment 插入之后执行。

虚拟列表方法和属性

当我们初始化完成之后,我们就有一个存储了虚拟列表实例的变量(比如上面例子中的 myList变量),这个实例有很有有用的方法和属性:

属性
myList.items包含所有条目的数组
myList.filteredItems所有过滤之后的条目(在执行了 ".filterItems" 方法之后)
myList.domCache条目的DOM缓存
myList.params初始化时传递的参数
myList.listBlock虚拟列表的容器的 Dom7 实例
myList.pageContent当前页面的 "page-content" 容器的 Dom7 实例
myList.currentFromIndex当前被渲染的第一个条目的序号
myList.currentToIndex当前被渲染的最后一个条目的序号
myList.reachEnd布尔属性,如果当前的最后条目是否是所有条目中的最后一个。
方法
myList.filterItems(indexes);过滤当前列表,只显示传入的序号指定的条目。
myList.resetFilter();关闭过滤,并再次显示所有的条目
myList.appendItem(item);增加条目
myList.appendItems(items);增加多个条目(数组)
myList.prependItem(item);在前面增加条目
myList.prependItems(items);在前面增加多个条目(数组)
myList.replaceItem(index, items);把指定序号的条目换成一个新的条目
myList.replaceAllItems(items);把所有的条目都换成新的
myList.moveItem(oldIndex, newIndex);把一个条目从 oldIndex 换到 newIndex
myList.insertItemBefore(index, item);在指定的序号前插入一个条目
myList.deleteItem(index);删除指定序号的条目
myList.deleteItems(indexes);删除指定的序号数组的条目
myList.deleteAllItems();删除所有条目
myList.clearCache();删除DOM缓存
myList.destroy();销毁实例,解除所有事件绑定
myList.update();更新列表。会重新计算列表的大小,并重新渲染
myList.scrollToItem(index);滚动到指定位置

示例

简单的条目

最简单的一种情况,我们不需要用模板来渲染条目(没有 templaterenderItem 参数),并且我们也不需要复杂的逻辑,我们直接在 items 参数中传入了HTML字符串:

  1. var myList = myApp.virtualList('.list-block.virtual-list', {
  2.     // Array with plain HTML items
  3.     items: [
  4.         '<li class="item-content"><div clas="item-inner"><div class="item-title">Item 1</div></div></li>',
  5.         '<li class="item-content"><div clas="item-inner"><div class="item-title">Item 2</div></div></li>',
  6.         '<li class="item-content"><div clas="item-inner"><div class="item-title">Item 3</div></div></li>',
  7.         //...
  8.         '<li class="item-content"><div clas="item-inner"><div class="item-title">Item 1000</div></div></li>'
  9.     ]
  10. });

使用模板

但是大部分情况下,我们需要有一些过滤和渲染的逻辑,或者我们会从一个JSON字符串中加载数据。这种情况下,我们在 items 参数中传入数据,然后使用 Template7 template 参数或者使用 renderItem 自定义一个渲染函数。

使用Template7模板:

  1. var myList = myApp.virtualList('.list-block.virtual-list', {
  2.     // Array with items data
  3.     items: [
  4.         {
  5.             title: 'Item 1',
  6.             picture: 'path/to/picture1.jpg'
  7.         },
  8.         {
  9.             title: 'Item 2',
  10.             picture: 'path/to/picture2.jpg'
  11.         },
  12.         // ...
  13.         {
  14.             title: 'Item 1000',
  15.             picture: 'path/to/picture1000.jpg'
  16.         },
  17.     ],
  18.     // Template 7 template to render each item
  19.     template: '<li class="item-content">' +
  20.                   '<div class="item-media"><img src="{{picture}}"></div>' +
  21.                   '<div class="item-inner">' +
  22.                       '<div class="item-title">{{title}}</div>' +
  23.                   '</div>' +
  24.                '</li>'
  25. });

使用自定义的渲染函数:

  1. var myList = myApp.virtualList('.list-block.virtual-list', {
  2.     // Array with items data
  3.     items: [
  4.         {
  5.             title: 'Item 1',
  6.             picture: 'path/to/picture1.jpg'
  7.         },
  8.         {
  9.             title: 'Item 2',
  10.             picture: 'path/to/picture2.jpg'
  11.         },
  12.         // ...
  13.         {
  14.             title: 'Item 1000',
  15.             picture: 'path/to/picture1000.jpg'
  16.         },
  17.     ],
  18.     // Custom render function to render item's HTML
  19.     renderItem: function (index, item) {
  20.         return '<li class="item-content">' +
  21.                   '<div class="item-media"><img src="' + item.picture + '"></div>' +
  22.                   '<div class="item-inner">' +
  23.                       '<div class="item-title">' + item.title + '</div>' +
  24.                   '</div>' +
  25.                '</li>';
  26.     }
  27. });

使用搜索栏

如果我们想使用搜索栏,我们必须在初始化的事后传入一个 searchAll 或者 searchByItem 参数:

  1. var myList = myApp.virtualList('.list-block.virtual-list', {
  2.     // Array with items data
  3.     items: [
  4.         {
  5.             title: 'Item 1',
  6.             picture: 'path/to/picture1.jpg'
  7.         },
  8.         {
  9.             title: 'Item 2',
  10.             picture: 'path/to/picture2.jpg'
  11.         },
  12.         // ...
  13.         {
  14.             title: 'Item 1000',
  15.             picture: 'path/to/picture1000.jpg'
  16.         },
  17.     ],
  18.     // search all items, we need to return array with indexes of matched items
  19.     searchAll: function (query, items) {
  20.         var foundItems = [];
  21.         for (var i = 0; i = 0) foundItems.push(i);
  22.         }
  23.         // Return array with indexes of matched items
  24.         return foundItems; 
  25.     }
  26. });

和上面功能相同,但是使用 searchByItem 参数:

  1. var myList = myApp.virtualList('.list-block.virtual-list', {
  2.     // Array with items data
  3.     items: [
  4.         {
  5.             title: 'Item 1',
  6.             picture: 'path/to/picture1.jpg'
  7.         },
  8.         {
  9.             title: 'Item 2',
  10.             picture: 'path/to/picture2.jpg'
  11.         },
  12.         // ...
  13.         {
  14.             title: 'Item 1000',
  15.             picture: 'path/to/picture1000.jpg'
  16.         },
  17.     ],
  18.     // search item by item
  19.     searchByItem: function (query, index, item) {
  20.         // Check if title contains query string
  21.         if (item.title.indexOf(query.trim()) >= 0) {
  22.             return true; //item matches query
  23.         }
  24.         else {
  25.             return false; //item doesn't match
  26.         }
  27.     }
  28. });

动态高度

如果我们的条目并不是都一样高的,那么我们可以通过 height 参数传入一个函数而不是数字来制定高度:

  1. var myList = myApp.virtualList('.list-block.virtual-list', {
  2.     // Array with items data
  3.     items: [
  4.         {
  5.             title: 'Item 1',
  6.             picture: 'path/to/picture1.jpg'
  7.         },
  8.         {
  9.             title: 'Item 2'
  10.         },
  11.         {
  12.             title: 'Item 3',
  13.             picture: 'path/to/picture3.jpg'
  14.         },
  15.         // ...
  16.         {
  17.             title: 'Item 1000'
  18.         },
  19.     ],
  20.     // Item template
  21.     template: '...',
  22.  
  23.     // Height function
  24.     height: function (item) {
  25.         if (item.picture) return 100; //item with picture is 100px height
  26.         else return 44; //item without picture is 44px height
  27.     }
  28. });

注意,JS并不是直接设定了你的条目的高度,所以请确保你的条目确实有指定的高度。你可能依然需要在CSS中设置,或者在 template 或者 renderItem 参数中通过 设置"style"属性来设置高度。

注意动态高度和 cols 参数是不兼容的!

API 方法

如果我们需要增加、删除、替换或者移动条目,我们需要使用虚拟列表的相关方法:

  1. // Initialize List
  2. var myList = myApp.virtualList('.list-block.virtual-list', {
  3.     // Array with items data
  4.     items: [
  5.         {
  6.             title: 'Item 1'
  7.         },
  8.         {
  9.             title: 'Item 2'
  10.         },
  11.         // ...
  12.         {
  13.             title: 'Item 1000'
  14.         }
  15.     ],
  16.  
  17.     // Item template
  18.     template: '...',
  19. });
  20.  
  21. // Append Item
  22. myList.appendItem({
  23.     title: 'Item 1001'
  24. });
  25.  
  26. // Append multiple items when clicking on some button
  27. $('.button.append-items').on('click', function () {
  28.     // Append multiple items by passing array with items
  29.     myList.appendItem([
  30.         {
  31.             title: 'Item 1002'
  32.         },
  33.         {
  34.             title: 'Item 1003'
  35.         },
  36.         {
  37.             title: 'Item 1004'
  38.         }
  39.     ]);    
  40. });
  41.  
  42. // Replace First Item
  43. myList.replaceItem(0, {
  44.     title: 'New Item 1'
  45. });
  46.  
  47. // Show only first 10 items when clicking on button
  48. $('.button.show-first-10').on('click', function () {
  49.     // We need to pass array with indexes of items to show
  50.     myList.filter([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
  51. });
  52.  
  53. // Reset filter
  54. $('.button.reset-filter').on('click', function () {
  55.     myList.resetFilter();
  56. });
  57.  
  58. // Insert new item before 1st and 2nd:
  59. myList.insertItemBefore(1, {
  60.     title: 'Item 1.5'
  61. });

注意,如果你使用上面这些方法来操作虚拟列表,你传入的每一个条目的格式必须和你在 items 参数中指定的是一样的。