QML Loader:从文件加载组件、加载内联组件

QML 做"按需加载"基本绕不开 Loader 这个组件。页面一开始不存在的组件,等用户点击时才创建;用不到了再销毁,省掉不必要的初始化开销。这篇用两个最小示例介绍 Loader 的两种加载入口:

  • source 从独立 QML 文件加载 --- 适合把每个页面/组件拆成单独文件管理
  • sourceComponent 加载内联 Component --- 组件定义在同一个文件里,随文档一起编译

两种方式都支持运行时反复挂载、卸载,区别只在于组件模板放在哪。

从文件加载组件

一个空白面板加两个按钮:点「加载组件」,面板里出现一块从 component/MyComponent.qml 实例化的内容;点「卸载组件」,内容消失,回到提示文字。

演示代码

qml 复制代码
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

FadeInAnimation {
    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 20
        spacing: 15

        // ... 省略标题组件 TitleSeparator ...

        RowLayout {
            Layout.fillWidth: true
            spacing: 10

            Button {
                text: "加载组件"
                onClicked: loader.source = "component/MyComponent.qml"
            }

            Button {
                text: "卸载组件"
                onClicked: loader.source = ""
            }
        }

        Rectangle {
            Layout.fillWidth: true
            Layout.fillHeight: true
            color: Qt.rgba(0.95, 0.95, 0.95, 1)
            radius: 4

            Loader {
                id: loader
                anchors.fill: parent
                anchors.margins: 10
            }

            Text {
                anchors.centerIn: parent
                text: loader.status === Loader.Ready ? "" : "点击「加载组件」"
                font.pointSize: 11
                color: "#999"
                visible: loader.status !== Loader.Ready
            }
        }
    }
}

被加载的 component/MyComponent.qml 是一个普通组件文件,根元素不需要写尺寸:

qml 复制代码
import QtQuick

Rectangle {
    color: "#d5e8f0"
    radius: 6

    Text {
        anchors.centerIn: parent
        text: "This is MyComponent.qml"
        font.pixelSize: 16
        color: "#333"
    }
}

关键逻辑解析

source 的相对路径从哪算起 。这里写 "component/MyComponent.qml",基准是当前 QML 文件所在目录。工程通过 CMake 把 QML 打进了资源系统,实际解析成 qrc:/component/MyComponent.qml,目录结构与源码一致,所以路径照源码相对关系写就行。

根元素不设尺寸也能铺满 。Loader 用 anchors.fill 显式定好了尺寸,这种情况下被加载的 item 会被自动拉伸到 Loader 的大小,组件文件里只需用 anchors.centerIn: parent 放内容,不用关心具体宽高。

用 status 判断内容就绪 。Loader 有三个阶段:Loader.Null(没加载)、Loader.Loading(加载中)、Loader.Ready(完成);加载出错则是 Loader.Error。这里占位提示文字绑定 loader.status !== Loader.Ready,组件加载完成后自动让位。

卸载就是清空 source 。loader.source = "" 会销毁当前实例并回到 Null,占位文字随之重新出现。反复点两个按钮,就是反复执行"创建 → 销毁",不会累积实例。

加载内联 Component 组件

效果和上一个几乎一样,但组件不是单独文件,而是直接写在当前文档里的 Component。

演示代码

qml 复制代码
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

FadeInAnimation {
    Component {
        id: com1
        Rectangle {
            width: 100
            height: 100
            color: "#3498db"
            radius: 6

            Text {
                anchors.centerIn: parent
                text: "Component"
                color: "#fff"
                font.pointSize: 12
            }
        }
    }

    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 20
        spacing: 15

        // ... 省略标题组件 TitleSeparator ...

        RowLayout {
            Layout.fillWidth: true
            spacing: 10

            Button {
                text: "加载"
                onClicked: loader.sourceComponent = com1
            }

            Button {
                text: "卸载"
                onClicked: loader.sourceComponent = null
            }
        }

        Rectangle {
            Layout.fillWidth: true
            Layout.fillHeight: true
            color: Qt.rgba(0.95, 0.95, 0.95, 1)
            radius: 4

            Loader {
                id: loader
                anchors.fill: parent
                anchors.margins: 10
            }

            Text {
                anchors.centerIn: parent
                text: loader.item ? "" : "点击「加载」"
                font.pointSize: 11
                color: "#999"
                visible: !loader.item
            }
        }
    }
}

关键逻辑解析

Component 本身不实例化,只是个模板 。文件顶部的 com1 只是把一段元素树登记成可复用模板,真正的实例由 loader.sourceComponent = com1 创建。这是 Loader 两个属性里更省事的一个:不用多开一个文件,组件和触发它的按钮在同一个文件里,改起来不用两头跑。

占位判断改用 loader.item 。上一例用 status,这里用 loader.item 是否为空判断------item 有值就代表实例存在,语义更直接。

卸载传 null 而不是空字符串 。source 清空用 "",sourceComponent 清空用 null,两者可以同时设置,但一般只用一个入口,另一个保持默认即可。

source 还是 sourceComponent:按组件放哪选

source sourceComponent
模板位置 独立 QML 文件 当前文档内联
适用 页面较大、需要单独维护,或要被多处 Loader 复用 组件短小、只在本页用
编译时机 加载时才解析文件 随宿主文档一起编译
写法 loader.source = "xxx.qml" loader.sourceComponent = com1

组件要单独成文件、可能被多个页面复用,用 source;组件只有几行、只属于当前页面,用 sourceComponent。实际项目里两种经常混用:页面框架用 source 从文件加载路由页面,页内的小部件用 sourceComponent 直接内联。

运行验证

  1. Qt Creator 打开 qml_loader/CMakeLists.txt,按 Ctrl+R 运行;
  2. 左侧列表点「从文件加载」,点「加载组件 / 卸载组件」看面板变化;
  3. 再点「加载 Component」试内联组件版本。

扩展复用方向

  • 给 Loader 加 asynchronous: true,组件较复杂时后台线程编译,避免界面卡顿;
  • 用 loader.active = false 挂起加载,等真正需要时再 active = true 实例化;
  • Loader.setSource(url, {prop: value}) 可以在加载的同时把初始属性传进去,配合下一类的属性传递玩法。

已验证环境:

相关推荐
实心儿儿3 小时前
Qt — Qt 事件
开发语言·qt
bkspiderx9 小时前
Qt 的消息机制:事件驱动与信号槽的底层逻辑
开发语言·qt·信号槽·事件驱动·qt 的消息机制
江湖人称菠萝包11 小时前
【Qt】《Qt 5.9 C++开发指南》笔记-Chapter11-数据库
笔记·qt·qt5
励志不掉头发的内向程序员11 小时前
【从零写一个CAD 02】画完第一条线之后:实体容器、Esc 取消,和按了没反应的键盘
开发语言·c++·qt·学习·系统架构
Quz16 小时前
QML TreeView:排序与搜索过滤
qt
Quz17 小时前
QML TreeView:基础树视图与自定义委托
qt
Junfeng的技术笔记1 天前
基于 C++/Qt 的雷达回波信号检测与测距仿真系统
qt
Quz1 天前
QML TableView:自定义样式与排序表格
qt
Quz1 天前
QML TableView:可编辑表格与 SQLite 数据持久化
qt·sqlite
Quz1 天前
QML ListView:C++ 数据模型与 ProxyModel 实时过滤
qt