虚拟列表可以用来渲染有大量数据的列表,并且不有有任何的性能问题。并且,它和所有的Framework7组件都是兼容的,包括搜索栏、无限滚动、下拉刷新、滑动删除和可排序列表。
虚拟列表打HTML结构非常简单,他和常见的 列表 或者 多媒体列表只有一个区别,你必须把它的内容设置为空的。
<!-- Virtual List --> <div class="list-block virtual-list"> <!-- 内容置空 --> </div>
其中:
virtual-list
- 在使用虚拟列表的容器上增加这个类
现在,当我们有了列表的HTML,我们下一步是初始化它。我们需要用到这些App方法:
myApp.virtualList(listBlockContainer, parameters) - 使用parameters参数初始化虚拟列表
比如:
var myList = app.virtualList('.list-block', { items: [1,2,3,4], height: 44 });
下面是所有的初始化参数:
参数 | 类型 | 默认值 | 说明 |
---|---|---|---|
items | array | 包含列表条目的数组 | |
rowsBefore | number | 在当前屏幕滚动位置之前需要渲染的条目数。默认是两倍当前屏幕大小需要的数量。(译者注:也就是说,假设你的屏幕是320px高,每一个条目是40px,那么当前屏幕上方会有(320/40*2) == 16条),加上当前屏幕中的8条和屏幕下方的8条,总共在DOM中存在32个条目)。 | |
rowsAfter | number | 在当前屏幕滚动位置之后需要渲染的条目数。默认是当前屏幕大小需要的数量。(译者注:参加上面的说明)。 | |
cols | number | 1 | 每一行需要的条目数。如果你用了动态的高度,这个参数是不兼容的。 |
height | number or function(item) | 44 | 如果是 数字,那么它是每一个条目的px高度。如果是 函数,那么它应该返回条目的高度。 |
template | string or function | Template7 字符串模板,或者是Template7 编译后的模板,用来渲染单个条目。这个模板应该有一个条目的完整HTML结构,包括外面的 <li></li> 容器。 | |
renderItem | function(index, item) | 这个可选方法用来自定义渲染条目的HTML。它是用来代替 tamplate 参数的。 | |
dynamicHeightBufferSize | number | 1 | This parameter allows to control buffer size on Virtual Lists with dynamic height (when height parameter is function) as a buffer size multiplier |
cache | boolean | true | 启用/禁用已经渲染的条目的DOM缓存。启用的情况下,每一个条目都只会被渲染一次,后续都是对DOM的操作。如果你的条目中需要保存一些用户的操作(比如表单或者滑动删除)或者修改,那么这个参数是很有用的。 |
updatableScroll | boolean | 当前是否在滚动的时候会更新滚动条并触发滚动事件。默认情况下对所有iOS 8 以下的设备都是false。 | |
搜索 | |||
searchByItem | function(query, index, item) | 搜索栏用的搜索方法,它接收三个参数:搜索词,条目的序号和条目自身。如果一个条目符合结果,你应该返回 true ,否则返回false。 | |
searchAll | function(query, items) | 搜索栏用到的搜索方法,它接受两个参数:搜索词,和包含所有条目的数组。你需要自己遍历并返回匹配的条目。 | |
回调 | |||
onItemBeforeInsert | function(list, item) | 回调函数,当条目被插入到虚拟列表的 document fragment 之前执行。 | |
onBeforeClear | function(list, fragment) | 回调函数,当前列表的DOM元素被删除并替换成新的 document fragment 之前执行。 | |
onItemsBeforeInsert | function(list, fragment) | 回调函数,当前列表的DOM元素被删除之后,准备替换成新的 document fragment 之前执行。 | |
onItemsAfterInsert | function(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); | 滚动到指定位置 |
最简单的一种情况,我们不需要用模板来渲染条目(没有 template
和 renderItem
参数),并且我们也不需要复杂的逻辑,我们直接在 items
参数中传入了HTML字符串:
var myList = myApp.virtualList('.list-block.virtual-list', { // Array with plain HTML items items: [ '<li class="item-content"><div clas="item-inner"><div class="item-title">Item 1</div></div></li>', '<li class="item-content"><div clas="item-inner"><div class="item-title">Item 2</div></div></li>', '<li class="item-content"><div clas="item-inner"><div class="item-title">Item 3</div></div></li>', //... '<li class="item-content"><div clas="item-inner"><div class="item-title">Item 1000</div></div></li>' ] });
但是大部分情况下,我们需要有一些过滤和渲染的逻辑,或者我们会从一个JSON字符串中加载数据。这种情况下,我们在 items
参数中传入数据,然后使用 Template7 template
参数或者使用 renderItem
自定义一个渲染函数。
使用Template7模板:
var myList = myApp.virtualList('.list-block.virtual-list', { // Array with items data items: [ { title: 'Item 1', picture: 'path/to/picture1.jpg' }, { title: 'Item 2', picture: 'path/to/picture2.jpg' }, // ... { title: 'Item 1000', picture: 'path/to/picture1000.jpg' }, ], // Template 7 template to render each item template: '<li class="item-content">' + '<div class="item-media"><img src="{{picture}}"></div>' + '<div class="item-inner">' + '<div class="item-title">{{title}}</div>' + '</div>' + '</li>' });
使用自定义的渲染函数:
var myList = myApp.virtualList('.list-block.virtual-list', { // Array with items data items: [ { title: 'Item 1', picture: 'path/to/picture1.jpg' }, { title: 'Item 2', picture: 'path/to/picture2.jpg' }, // ... { title: 'Item 1000', picture: 'path/to/picture1000.jpg' }, ], // Custom render function to render item's HTML renderItem: function (index, item) { return '<li class="item-content">' + '<div class="item-media"><img src="' + item.picture + '"></div>' + '<div class="item-inner">' + '<div class="item-title">' + item.title + '</div>' + '</div>' + '</li>'; } });
如果我们想使用搜索栏,我们必须在初始化的事后传入一个 searchAll
或者 searchByItem
参数:
var myList = myApp.virtualList('.list-block.virtual-list', { // Array with items data items: [ { title: 'Item 1', picture: 'path/to/picture1.jpg' }, { title: 'Item 2', picture: 'path/to/picture2.jpg' }, // ... { title: 'Item 1000', picture: 'path/to/picture1000.jpg' }, ], // search all items, we need to return array with indexes of matched items searchAll: function (query, items) { var foundItems = []; for (var i = 0; i = 0) foundItems.push(i); } // Return array with indexes of matched items return foundItems; } });
和上面功能相同,但是使用 searchByItem
参数:
var myList = myApp.virtualList('.list-block.virtual-list', { // Array with items data items: [ { title: 'Item 1', picture: 'path/to/picture1.jpg' }, { title: 'Item 2', picture: 'path/to/picture2.jpg' }, // ... { title: 'Item 1000', picture: 'path/to/picture1000.jpg' }, ], // search item by item searchByItem: function (query, index, item) { // Check if title contains query string if (item.title.indexOf(query.trim()) >= 0) { return true; //item matches query } else { return false; //item doesn't match } } });
如果我们的条目并不是都一样高的,那么我们可以通过 height
参数传入一个函数而不是数字来制定高度:
var myList = myApp.virtualList('.list-block.virtual-list', { // Array with items data items: [ { title: 'Item 1', picture: 'path/to/picture1.jpg' }, { title: 'Item 2' }, { title: 'Item 3', picture: 'path/to/picture3.jpg' }, // ... { title: 'Item 1000' }, ], // Item template template: '...', // Height function height: function (item) { if (item.picture) return 100; //item with picture is 100px height else return 44; //item without picture is 44px height } });
注意,JS并不是直接设定了你的条目的高度,所以请确保你的条目确实有指定的高度。你可能依然需要在CSS中设置,或者在 template
或者 renderItem
参数中通过 设置"style"属性来设置高度。
注意动态高度和 cols
参数是不兼容的!
如果我们需要增加、删除、替换或者移动条目,我们需要使用虚拟列表的相关方法:
// Initialize List var myList = myApp.virtualList('.list-block.virtual-list', { // Array with items data items: [ { title: 'Item 1' }, { title: 'Item 2' }, // ... { title: 'Item 1000' } ], // Item template template: '...', }); // Append Item myList.appendItem({ title: 'Item 1001' }); // Append multiple items when clicking on some button $('.button.append-items').on('click', function () { // Append multiple items by passing array with items myList.appendItem([ { title: 'Item 1002' }, { title: 'Item 1003' }, { title: 'Item 1004' } ]); }); // Replace First Item myList.replaceItem(0, { title: 'New Item 1' }); // Show only first 10 items when clicking on button $('.button.show-first-10').on('click', function () { // We need to pass array with indexes of items to show myList.filter([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); }); // Reset filter $('.button.reset-filter').on('click', function () { myList.resetFilter(); }); // Insert new item before 1st and 2nd: myList.insertItemBefore(1, { title: 'Item 1.5' });
注意,如果你使用上面这些方法来操作虚拟列表,你传入的每一个条目的格式必须和你在 items 参数中指定的是一样的。