QML 提供的 JavaScript Host Environment 可以运行标准 JavaScript。除了标准的 JavaScript 属性之外,QML Global Object 还包括许多帮助程序方法,这些方法简化了 UI 的构建和与 QML 环境的交互。

QML 提供的 JavaScript 环境比 Web 浏览器中的更严格。例如,在 QML 中,您不能添加或修改 JavaScript 全局对象的成员。在常规 JavaScript 中,可能会通过使用变量而不声明它来意外执行此操作。在 QML 中,这将引发异常,因此必须显式声明所有局部变量。有关 QML 中的 JavaScript 代码限制,请参阅 JavaScript Environment Restrictions

QML documents 的各个部分都可以包含 JavaScript 代码:

  • property bindings 的主体。这些 JavaScript 表达式描述了 QML 对象属性间的关系。
  • Signal handlers 的主体。每当 QML 对象发出相关信号时,就会自动评估这些 JavaScript 语句。
  • custom methods 的定义。在 QML 对象的主体内定义的 JavaScript 函数成为该对象的方法。
  • 独立的 JavaScript resource (.js) files。这些文件实际上与 QML 文档是分开的,但是它们可以导入到 QML 文档中。导入文件中定义的函数和变量可用于属性绑定、信号处理程序和自定义方法。

    5.1.1 属性绑定中的 JavaScript

    在下面的例子中,Rectangle 的 color 属性依赖于 TapHandler 的pressed 属性。这种关系使用条件表达式来描述: ```javascript import QtQuick 2.12

Rectangle { id: colorbutton width: 200; height: 80;

  1. color: inputHandler.pressed ? "steelblue" : "lightsteelblue"
  2. TapHandler {
  3. id: inputHandler
  4. }

}

  1. 事实上,任何 JavaScript 表达式都可以用在属性绑定定义中,只要表达式的结果是一个可以分配给属性类型的值。这包括副作用。但是,不鼓励复杂绑定和副作用,因为它们会降低代码的性能、可读性和可维护性。
  2. 有两种定义属性绑定的方法:最常见的一种是在 [property initialization](https://doc.qt.io/qt-6/qtqml-syntax-objectattributes.html#value-assignment-on-initialization) 中的示例。第二种(也是很少见的)方法是从命令式 JavaScript 代码中为 [Qt.binding()](https://doc.qt.io/qt-6/qml-qtqml-qt.html#binding-method) 函数返回的函数分配属性,如下所示:
  3. ```javascript
  4. import QtQuick 2.12
  5. Rectangle {
  6. id: colorbutton
  7. width: 200; height: 80;
  8. color: "red"
  9. TapHandler {
  10. id: inputHandler
  11. }
  12. Component.onCompleted: {
  13. color = Qt.binding(function() { return inputHandler.pressed ? "steelblue" : "lightsteelblue" });
  14. }
  15. }

有关如何定义属性绑定的更多信息,请参阅 property bindings。有关绑定与值分配有何不同的信息,请参阅 Property Assignment versus Property Binding

5.1.2 信号处理函数中的 JavaScript

QML 对象类型可以发出信号以响应发生的某些事件。这些信号可以由信号处理函数处理,客户端可以定义这些函数来实现自定义程序逻辑。

假设由 Rectangle 类型表示的按钮具有 TapHandler 和 Text 标签。当用户按下按钮时,TapHandler 会发出它的敲击信号。客户端可以使用 JavaScript 表达式对 onTapped 处理程序中的信号做出反应。QML 引擎根据需要执行处理程序中定义的这些 JavaScript 表达式。通常,信号处理程序绑定到 JavaScript 表达式以启动其他事件或分配属性值。

  1. import QtQuick 2.12
  2. Rectangle {
  3. id: button
  4. width: 200; height: 80; color: "lightsteelblue"
  5. TapHandler {
  6. id: inputHandler
  7. onTapped: {
  8. // arbitrary JavaScript expression
  9. console.log("Tapped!")
  10. }
  11. }
  12. Text {
  13. id: label
  14. anchors.centerIn: parent
  15. text: inputHandler.pressed ? "Pressed!" : "Press here!"
  16. }
  17. }

5.1.3 独立函数中的 JavaScript

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

5.1.3.1 自定义方法中的 JavaScript

自定义方法可以在 QML 文档中定义,并且可以从信号处理程序、属性绑定或其他 QML 对象中的函数调用。此类方法通常称为 inline JavaScript functions,因为它们的实现包含在 QML 对象类型定义(QML 文档)中,而不是包含在外部 JavaScript 文件中。

内联自定义方法的示例:

  1. import QtQuick 2.12
  2. Item {
  3. function fibonacci(n){
  4. var arr = [0, 1];
  5. for (var i = 2; i < n + 1; i++)
  6. arr.push(arr[i - 2] + arr[i -1]);
  7. return arr;
  8. }
  9. TapHandler {
  10. onTapped: console.log(fibonacci(10)) //每当TapHandler发出tapped信号时,就会运行fibonacci()函数
  11. }
  12. }

注意:在 QML 文档中内联定义的自定义方法公开给其他对象,因此 QML 组件中根对象上的内联函数可以被组件外部的调用者调用。 如果不需要,可以将该方法添加到非根对象中,或者最好在外部 JavaScript 文件中编写。

5.1.3.2 JavaScript 文件中的函数定义

非平凡的程序逻辑最好分离到一个单独的 JavaScript 文件中。例如,前面示例中的 fibonacci() 方法可以移动到名为 fib.js 的外部文件中,并像这样访问:

  1. import QtQuick 2.12
  2. import "fib.js" as MathFunctions
  3. Item {
  4. TapHandler {
  5. onTapped: console.log(MathFunctions.fibonacci(10))
  6. }
  7. }

有关将外部 JavaScript 文件加载到 QML 的更多信息,请阅读 Importing JavaScript Resources in QML

5.1.3.3 连接信号到 JavaScript 函数

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

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

  1. import QtQuick 2.12
  2. import "script.js" as MyScript
  3. Item {
  4. id: item
  5. width: 200; height: 200
  6. TapHandler {
  7. id: inputHandler
  8. }
  9. Component.onCompleted: {
  10. inputHandler.tapped.connect(MyScript.jsFunction)
  11. }
  12. }
  13. // script.js
  14. function jsFunction() {
  15. console.log("Called JavaScript function!")
  16. }

5.1.4 程序启动代码中的 JavaScript

偶尔需要在应用程序(或组件实例)启动时运行一些命令式代码。虽然将启动脚本作为全局代码包含在外部脚本文件中很诱人,但由于 QML 环境可能尚未完全建立,这可能会产生严重的限制。例如,某些对象可能尚未创建或某些属性绑定可能尚未建立。有关全局脚本代码的确切限制,请参阅 JavaScript Environment Restrictions

QML 对象在其实例化完成时发出 Component.completed attached signal。相应的 Component.onCompleted 中的 JavaScript 代码在对象实例化后运行。因此,编写程序启动代码的最佳位置是在顶级对象的 Component.onCompleted 中,因为当 QML 环境完全建立时,该对象会发出 Component.completed。例如:

  1. import QtQuick 2.0
  2. Rectangle {
  3. function startupFunction() {
  4. // ... startup code
  5. }
  6. Component.onCompleted: startupFunction();
  7. }

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

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