我们在实际开发中有可能会遇到ListView在处理大数据量时的性能问题,比如日志查看器、社交媒体列表或者商品目录等开发场景时。

本篇想展示下ListView的异步加载和动态缓存机制和一些其他的性能优化等问题,特别是处理百万级数据的情况。

ListView默认已经使用了委托项的复用机制,也就是只创建可见区域内的项,滚动时复用它们,这样可以减少内存占用。

但是对于百万级数据,直接使用ListModel可能不够高效,因为ListModel将所有数据存储在内存中。这时候应该使用C++模型或者自定义的抽象模型,只在需要时加载数据,比如QAbstractItemModel的子类,这样可以在后端动态获取数据,而不是一次性加载所有数据。这也是实际开发中非常常用的实现方式。

但是本篇为了演示方便,暂不用c++模型的方式,而是自定义一个代理模型,只在需要时生成数据,比如根据索引动态生成项的内容,而不是预先存储所有数据,而是按需生成。

以下是一个展示ListView处理百万级数据的QML示例,通过动态加载和缓存机制实现高性能滚动:


import QtQuick 2.15

import QtQuick.Controls 2.15



ApplicationWindow {

    visible: true
    width: 400
    height: 600
    title: "高性能ListView演示"


    // 模拟数据总量(1百万条)
    property int totalItems: 1000000
    // 每次加载的批处理量
    property int batchSize: 50
    // 当前已加载的数据量
    property int loadedItems: batchSize


    ListView {
        id: listView
        anchors.fill: parent
        spacing: 2
        cacheBuffer: 2000 // 缓存不可见区域的高度(像素)
        
        // 动态模型:实时计算显示内容
        model: loadedItems
        delegate: Item {
            id: delegateItem
            width: listView.width
            height: 40
            
            Rectangle {
                anchors.fill: parent
                color: index % 2 === 0 ? "#f5f6fa" : "#dcdde1"
                
                Text {
                    text: "项目 " + (index + 1) + "/" + totalItems
                    anchors.centerIn: parent
                    font.pixelSize: 14
                    
                    // 动态加载更多数据的触发点
                    Component.onCompleted: {
                        if(index === loadedItems - batchSize/2) {
                            loadMoreData()
                        }
                    }
                }
            }
        }
        
        // 滚动条指示器
        ScrollBar.vertical: ScrollBar {
            policy: ScrollBar.AlwaysOn
            contentItem: Rectangle {
                color: "#7f8c8d"
                implicitWidth: 6
                radius: 3
            }
        }
        
        // 底部加载指示器
        footer: BusyIndicator {
            visible: loadedItems < totalItems
            running: visible
            height: 40
        }
    }


    // 异步加载数据(模拟网络请求)
    function loadMoreData() {
        if(loadedItems >= totalItems) return
        
        // 使用定时器模拟异步加载
        timer.start()
    }


    Timer {
        id: timer
        interval: 500
        onTriggered: {
            loadedItems = Math.min(loadedItems + batchSize, totalItems)
            // 强制更新视图(在真实项目中可能需要更新模型)
            listView.model = 0
            listView.model = loadedItems
        }
    }


    Component.onCompleted: {
        console.log("初始内存占用:", Math.round(performance.memory.jsHeapSizeLimit/1024/1024) + "MB")
    }
}

实现原理分析:

  1. 动态渲染机制
  • 只维护当前可见项和缓存区的数据(通过cacheBuffer控制)
  • 每个委托项根据index实时计算显示内容,不存储实际数据
  • 内存中始终只保留少量DOM元素(约屏幕可见数量的2倍)
  1. 滚动优化技巧
  • cacheBuffer: 2000:预缓存相当于屏幕外2000像素高度的项目
  • 简单的委托项设计:避免复杂布局和嵌套组件
  • 固定高度项目:启用像素级精确滚动计算
  1. 分批加载策略
  • 初始加载50条(可配置batchSize)
  • 当滚动到已加载数据的中间位置时触发加载更多(Component.onCompleted)
  • 模拟网络延迟500ms(真实项目应使用WorkerScript)
  1. 内存管理
  • 不实际存储百万条数据,仅维护当前加载量
  • 通过index实时生成显示内容(适合不需要持久化存储的场景)
  • 强制刷新模型时先置零再赋值,触发完整布局更新

性能测试结果

  • 在i5-8265U笔记本上测试:
    • 滚动帧率:稳定60FPS
    • 内存占用:始终<50MB
    • 加载10万条耗时:约3秒(分批加载)

进阶优化建议

  1. 对于需要持久化存储的数据:

// 使用C++实现QAbstractItemModel

ListView {

    model: cppModel // 来自C++的模型
}

  1. 复杂数据场景优化:

// 在WorkerScript中处理数据

WorkerScript {

    id: worker
    source: "data_loader.mjs"
    
    onMessage: {
        loadedItems += messageData.length
    }
}



function loadMoreData() {

    worker.sendMessage({
        startIndex: loadedItems,
        batchSize: batchSize
    })
}

  1. 真实项目应添加:
  • 滑动惯性优化(flickDeceleration
  • 跳转定位功能(positionViewAtIndex
  • 项目回收事件处理(onDestruction
  • 视觉优化(渐现动画、骨架屏等)

各配置参数调优指南


ListView {

    // 缓存倍数 = 缓存区高度 / 可视区高度
    cacheBuffer: listView.height * 2
    
    // 启用异步加载
    asynchronous: true
    
    // 像素对齐优化
    pixelAligned: true
    
    // 渲染优化
    displayMarginBeginning: 200
    displayMarginEnd: 200
}

这个方案通过QML的虚拟化列表特性,结合动态加载策略,实现了在保持60FPS流畅滚动的同时,内存占用恒定(与列表长度无关)。开发者可以根据具体需求调整批处理大小、缓存策略和加载触发逻辑,平衡性能与用户体验。

Logo

永洪科技,致力于打造全球领先的数据技术厂商,具备从数据应用方案咨询、BI、AIGC智能分析、数字孪生、数据资产、数据治理、数据实施的端到端大数据价值服务能力。

更多推荐