加载只是第一步,真正用起来还要解决两件事:组件根据条件来回切,以及把外层数据送进加载出来的实例里。这篇两个示例分别演示声明式切换和实例传参。
- 切换组件 --- 一个
Switch开关,在两个内联组件之间条件切换 - 属性传递 --- 把输入框文字和选中的颜色写进已加载实例的自定义属性,实例即时刷新
切换组件:开关决定显示哪个

拨动开关,面板在红色「组件 1」和蓝色「组件 2」之间切换,底部文字跟着变。
演示代码
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
FadeInAnimation {
ColumnLayout {
anchors.fill: parent
anchors.margins: 20
spacing: 15
// ... 省略标题组件 TitleSeparator ...
Switch {
id: controlSwitch
text: "切换组件"
}
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: Qt.rgba(0.95, 0.95, 0.95, 1)
radius: 4
Loader {
anchors.fill: parent
anchors.margins: 10
sourceComponent: controlSwitch.checked ? component1 : component2
}
}
Text {
text: "当前: " + (controlSwitch.checked ? "组件 1(红色)" : "组件 2(蓝色)")
font.pointSize: 11
color: "#666"
}
}
Component {
id: component1
Rectangle {
color: "#e74c3c"
radius: 6
Text {
anchors.centerIn: parent
text: "组件 1"
color: "#fff"
font.pointSize: 16
font.bold: true
}
}
}
Component {
id: component2
Rectangle {
color: "#3498db"
radius: 6
Text {
anchors.centerIn: parent
text: "组件 2"
color: "#fff"
font.pointSize: 16
font.bold: true
}
}
}
}
关键逻辑解析
sourceComponent 可以绑表达式,这是声明式切换的核心 。controlSwitch.checked ? component1 : component2 是一个普通绑定,开关状态一变,Loader 立刻换用另一个模板------不需要 onToggled 里手动赋值,状态和界面始终同步。
切换的代价是旧实例被销毁 。和上一类换 source 一样,切到组件 2 时组件 1 的实例销毁,组件 1 里如果有输入框、滚动位置这类内部状态,会全部丢失。需要保留状态就不能用这种"销毁重建"的切换,得让两个组件常驻再用 visible 控制。
两个组件模板都写在文件底部 。Component 不是界面元素,放根元素下当"模板库"用即可。页面里只出现 Loader,同一时刻只有当前选中的模板被实例化。
相对路径方案对比 :如果两个组件是独立文件,用 loader.source = checked ? "a.qml" : "b.qml" 效果等价。内联 Component 适合两个模板都很短、只在本页用的场景;文件版适合组件较大、需要单独维护的场景。
属性传递:把数据写进加载的实例

输入框填文字、下拉框选颜色,点「应用」,面板里的实例文字和底色立刻更新;点「重置」恢复默认。
演示代码
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
TextField {
id: textInput
placeholderText: "输入文字"
Layout.fillWidth: true
maximumLength: 10
}
ComboBox {
id: colorPicker
Layout.fillWidth: true
model: [
{ text: "红色", color: "#e74c3c" },
{ text: "蓝色", color: "#3498db" },
{ text: "绿色", color: "#2ecc71" },
{ text: "紫色", color: "#9b59b6" }
]
textRole: "text"
currentIndex: 0
}
Button {
text: "应用"
onClicked: {
if (loader.item) {
loader.item.displayText = textInput.text || "Hello"
loader.item.displayBgColor = colorPicker.model[colorPicker.currentIndex].color
}
}
}
Button {
text: "重置"
onClicked: {
textInput.text = ""
colorPicker.currentIndex = 0
if (loader.item) {
loader.item.displayText = "Hello"
loader.item.displayBgColor = colorPicker.model[colorPicker.currentIndex].color
}
}
}
}
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
sourceComponent: displayComponent
onLoaded: {
item.displayText = textInput.text || "Hello"
item.displayBgColor = colorPicker.model[colorPicker.currentIndex].color
}
}
}
}
Component {
id: displayComponent
Rectangle {
id: inner
property string displayText: "Hello"
property color displayBgColor: "#e74c3c"
color: inner.displayBgColor
radius: 6
opacity: 0.9
Text {
anchors.centerIn: parent
text: inner.displayText
color: "#fff"
font.pixelSize: 24
font.bold: true
}
}
}
}
关键逻辑解析
被加载的组件要定义自己的"入参" 。displayComponent 里声明了 property string displayText 和 property color displayBgColor,这两个自定义属性就是组件对外的接口。外层要传什么,组件就暴露什么属性,耦合只存在于这层约定上。
访问实例统一走 loader.item 。加载进来的实例挂在 Loader.item 上,loader.item.displayText = ... 就是给实例的属性赋值,实例内部的绑定(color: inner.displayBgColor、text: inner.displayText)自动刷新,不用手动通知。
onLoaded 负责初始化,按钮负责更新 。sourceComponent 一开始就非空,Loader 加载完成立刻触发 onLoaded,在这里用当前输入框的值做一次初始化;之后点「应用」再改。两处写的是同一段赋值逻辑,保证"初始态"和"改后态"走同一条路。
赋值前先判空 。loader.item 在实例未加载时是 null,直接访问会报错,所以 if (loader.item) 包一层。这里 sourceComponent 恒定非空,其实实例常驻,判空是防御写法------换成按需 source 加载时,这个判空就是必需的了。
传值途径不止一种 。loader.item.xxx = value 适合"加载完成后随时改";如果要在加载的一瞬间 带上初始属性,用 Loader.setSource("file.qml", {prop: value}) 更直接,属性会作为初始值注入实例,比加载完再补一次赋值干净。
sourceComponent 条件切换和属性传递怎么配合用
| 手段 | 解决的问题 | 写法 |
|---|---|---|
sourceComponent: a ? comp1 : comp2 |
两个组件按条件二选一 | 声明式绑定,免手动赋值 |
loader.source = checked ? "a.qml" : "b.qml" |
两个文件按条件二选一 | 同上,模板放文件 |
loader.item.prop = value |
实例已存在,随时改数据 | 赋值即刷新 |
Loader.setSource(url, {prop: value}) |
加载瞬间注入初始值 | 一次性传参 |
组件内 property 声明 |
定义组件对外的接口 | 组件自己暴露 |
单页面多形态用 sourceComponent 条件切换;需要把外部数据灌进实例,用 loader.item 赋值或 setSource 初值。数据流方向要固定:外层通过属性单向送数据进组件,组件内部不反向改外层状态,逻辑才不会绕。
运行验证
- Qt Creator 打开
qml_loader/CMakeLists.txt,按Ctrl+R运行; - 左侧点「切换组件」,拨动开关看红蓝组件切换;
- 点「属性传递」,输入文字、选个颜色点「应用」,看面板实例更新;点「重置」恢复 Hello。
扩展复用方向
- 把多个内联
Component放进一个Repeater或数组,用sourceComponent按当前模式取值,可以扩展成"主题切换"; - 属性传递配合
Connections反向收信号:实例里发信号,外层Connections { target: loader.item }接收,实现双向通信; - 组件接口属性多了以后,把
displayText/displayBgColor这类收拢成一组property并加注释,等于一份轻量文档,团队协作时不易传错参数。
已验证环境:
- Qt 版本:Qt 6.8.2 / Qt 6.11.1
- 操作系统:Windows 11
- GitHub:QML-Minimal-Demos/qml_loader