在 QML 文档中的 JavaScript 表达式#

描述 JavaScript 表达式在 QML 文档中的有效位置

QML 提供的 JavaScript 主环境可以运行有效的标准 JavaScript 结构,例如条件运算符、数组、变量设置和循环。除了标准的 JavaScript 属性,QML 全局对象还包括一些辅助方法,这些方法可以简化构建 UI 和与 QML 环境的交互。

QML 提供的 JavaScript 环境比网页浏览器中的更严格。例如,在 QML 中,你不能修改 JavaScript 全局对象成员。在常规 JavaScript 中,可能会意外地通过使用未声明的变量来修改这些成员。在 QML 中,这将抛出异常,因此所有局部变量都必须显式声明。有关 QML 中执行的 JavaScript 代码的限制的完整描述,请参阅 JavaScript 环境限制

QML 文档的各个部分可以包含 JavaScript 代码

  1. 属性绑定体。这些 JavaScript 表达式描述了 QML 对象属性之间的关系。当属性的依赖项发生变化时,根据指定的关系自动更新该属性。

  2. 信号处理程序体。每当 QML 对象发射相关的信号时,这些 JavaScript 语句将被自动评估。

  3. 自定义方法的定义。在 QML 对象体内部定义的 JavaScript 函数成为该对象的方法。

  4. 独立的 JavaScript 资源 (.js) 文件。这些文件实际上与 QML 文档分开,但可以导入到 QML 文档中。导入文件中定义的函数和变量可以在属性绑定、信号处理程序和自定义方法中使用。

属性绑定中的 JavaScript#

在下面的示例中,Rectangle 的颜色属性依赖于 TapHandler 的 pressed 属性。这个关系使用条件表达式来描述。

实际上,在属性绑定定义中可以使用任何 JavaScript 表达式(无论多么复杂),只要表达式的结果是属性可以赋值的值类型。这包括副作用。然而,不建议使用复杂的绑定和副作用,因为它们可能会降低代码的性能、可读性和可维护性。

定义属性绑定的方法有两种:最常用的方法在之前示例中已有展示,即在属性初始化中。第二种(相对较少见)方法是,在命令式JavaScript代码中,将属性赋值为从binding()函数返回的函数,如下所示。

有关如何定义属性绑定的更多信息,请参阅属性绑定文档,以及有关绑定与值赋值区别的文档,请参阅属性赋值与属性绑定对比

信号处理器中的JavaScript#

在QML中,对象类型可以针对某些事件发生发出信号。这些信号可以通过信号处理器函数进行处理,这些函数由客户端定义以实现自定义程序逻辑。

假设一个以矩形类型表示的按钮有一个TapHandler和一个Text标签。当用户按下按钮时,TapHandler会发出其tapped信号。客户端可以在onTapped处理器中使用JavaScript表达式来响应这个信号。QML引擎会根据需要执行处理器中定义的这些JavaScript表达式。通常,信号处理器绑定到JavaScript表达式以启动其他事件或分配属性值。

有关信号和信号处理器的更多详细信息,请参考以下主题:

独立函数中的JavaScript#

程序逻辑也可以在JavaScript函数中定义。这些函数可以定义在QML文档中(作为自定义方法)或在外部导入的JavaScript文件中。

自定义方法中的JavaScript#

可以在QML文档中定义自定义方法,也可以从信号处理器或QML对象的函数中调用它们。这些方法通常被称为内联JavaScript函数,因为它们的实现包含在QML对象类型定义(QML文档)中,而不是外部JavaScript文件中。

以下是一个内联自定义方法的示例:

每当TapHandler发出tapped信号时,将运行fibonacci函数。

注意:

在QML文档中内联定义的自定义方法将暴露给其他对象,因此QML组件根对象上的内联函数可以被组件外部的调用者调用。如果这不是期望的行为,则可以将方法添加到非根对象中,或者最好在外部JavaScript文件中编写。

有关使用JavaScript在QML中定义自定义方法的更多信息,请参阅QML对象属性文档。

JavaScript文件中定义的函数#

较复杂的程序逻辑最好分离到单独的JavaScript文件中。该文件可以使用import语句导入QML,就像QML 模块一样。

例如,在前面示例中的 fibonacci() 方法可以被移动到名为 fib.js 的外部文件中,并可以这样访问

有关在 QML 中加载外部 JavaScript 文件的更多信息,请阅读关于 在 QML 中导入 JavaScript 资源 的部分。

将信号连接到 JavaScript 函数#

发出信号的 QML 对象类型还为其信号提供默认信号处理器,如前所述的 上一节 中所述。然而,有时客户端想要在另一个 QML 对象发出信号时触发在 QML 对象中定义的函数。此类情况可以通过信号连接来处理。

可以通过调用信号的 connect() 方法并将 JavaScript 函数作为参数传递来将 QML 对象发出的信号连接到 JavaScript 函数。例如,以下代码将 TapHandler 的 tapped 信号连接到 script.js 中的 jsFunction()

import QtQuick
import "script.js" as MyScript

Item {
    id: item
    width: 200; height: 200

    TapHandler {
        id: inputHandler
    }

    Component.onCompleted: {
        inputHandler.tapped.connect(MyScript.jsFunction)
    }
}
// script.js

function jsFunction() {
    console.log("Called JavaScript function!")
}

每次 TapHandler 的 tapped 信号发出时,都会调用 jsFunction()

有关更多信息,请参见 将信号连接到方法和信号

在应用程序启动代码中使用 JavaScript#

有时在应用程序(或组件实例)启动时运行一些命令式代码是必要的。虽然将启动脚本直接包含为外部脚本文件中的 全局代码 很诱人,但这可能有严重的限制,因为 QML 环境可能尚未完全建立。例如,某些对象可能尚未被创建,或者一些 属性绑定 可能尚未建立。请参阅 JavaScript 环境限制 了解全局脚本代码的确切限制。

当 QML 对象的实例化完成时,它会发出 Component.completed 附加信号。在对象实例化后运行相应的 Component.onCompleted 处理器中的 JavaScript 代码。因此,将应用程序启动代码写入顶级对象的 Component.onCompleted 处理器是最佳位置,因为这个对象在 QML 环境完全建立时会发出 Component.completed

例如

QML 文件中的任何对象——包括嵌套对象和嵌套 QML 组件实例——都可以使用这个附加属性。如果有多个 onCompleted() 处理器在启动时执行,它们将按未定义的顺序顺序执行。

类似地,每个 Component 在被销毁之前都会发出一个 destruction() 信号。