虚拟列表


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

虚拟列表的HTML结构

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

<!-- Virtual List -->
<div class="list-block virtual-list">
  <!-- 内容置空 -->
</div>

其中:

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

初始化虚拟列表

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

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

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

比如:

var myList = app.virtualList('.list-block', {
    items: [1,2,3,4],
    height: 44
});
注意,在初始化的时候虚拟列表的容器必须已经在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字符串:

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 参数是不兼容的!

API 方法

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

// 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 参数中指定的是一样的。