QML提供的JavaScript 的运行宿主环境可以运行有效的标准JavaScript 功能,如条件运算符、数组、变量设置和循环。除了标准的JavaScript属性之外,QML Global对象还包含一些辅助方法,用于简化构建ui以及与QML环境交互的过程。
QML提供的JavaScript环境比web浏览器中严格。例如,在QML中不能添加或修改JavaScript全局对象的成员。在常规的JavaScript中,有可能不小心使用了变量而没有声明它。在QML中,这将抛出异常,因此所有的局部变量都必须显式声明。有关从QML执行JavaScript代码的限制的完整描述,请参阅JavaScript环境限制。
QML文档的不同部分可以包含JavaScript代码,下面就是这些代码可能存在的情况:
在下面的例子中,Rectangle的color属性依赖于 TapHandler 的 pressed 属性。这种关系可以用一个条件表达式来描述:
import QtQuick 2.12Rectangle {id: colorbuttonwidth: 200; height: 80;color: inputHandler.pressed ? "steelblue" : "lightsteelblue"TapHandler {id: inputHandler}}
运行的效果如下:按下之后这个颜色更深
事实上,任何JavaScript表达式 (无论多么复杂) 都可以用于属性绑定定义,只要表达式的结果是一个类型可以赋给属性的值。这包括副作用。然而,不鼓励使用复杂的绑定和副作用,因为它们会降低代码的性能、可读性和可维护性。
定义属性绑定有两种方法:
第一种是最常见的方法如上面的示例所示
第二种(更少见的)方式是在命令式JavaScript代码中,将Qt.binding()函数返回的函数赋值给属性,如下所示:
import QtQuick 2.12Rectangle {id: colorbuttonwidth: 200; height: 80;color: "red"TapHandler {id: inputHandler}Component.onCompleted: {color = Qt.binding(function() { return inputHandler.pressed ? "steelblue" : "lightsteelblue" });}
}
有关如何定义属性绑定的更多信息,请参阅属性绑定文档,有关绑定与值赋值的区别,请参阅属性赋值与属性绑定的文档。
QML对象类型可以在某些事件发生时发出信号。这些信号可以通过信号处理函数来处理,这些函数可以由客户端定义来实现自定义的程序逻辑。
假设用Rectangle类型表示的按钮有一个TapHandler和一个Text标签。当用户按下按钮时,TapHandler会发出tap信号。客户端可以使用JavaScript表达式对onTapped处理程序中的信号做出反应。
QML引擎根据需要执行处理程序中定义的这些JavaScript表达式。通常,将信号处理程序绑定到JavaScript表达式以初始化其他事件或为属性赋值。
import QtQuick 2.12Rectangle {id: buttonwidth: 200; height: 80; color: "lightsteelblue"TapHandler {id: inputHandleronTapped: {// arbitrary JavaScript expressionconsole.log("Tapped!")}}Text {id: labelanchors.centerIn: parenttext: inputHandler.pressed ? "Pressed!" : "Press here!"}}
程序逻辑也可以在JavaScript函数中定义。这些函数可以在QML文档中内联定义(作为自定义方法),也可以在导入的JavaScript文件中外部定义。
自定义方法可以在QML文档中定义,也可以从信号处理程序、属性绑定或其他QML对象中的函数中调用。这些方法通常被称为内联JavaScript函数,因为它们的实现包含在QML对象类型定义(QML文档)中,而不是在外部的JavaScript文件中。
下面是一个内联自定义方法的例子:
import QtQuick 2.12Item {function fibonacci(n){var arr = [0, 1];for (var i = 2; i < n + 1; i++)arr.push(arr[i - 2] + arr[i -1]);return arr;}TapHandler {onTapped: console.log(fibonacci(10))}}
每当TapHandler发出tap信号时,fibonacci函数就会运行。
注意: 在QML文档中内联定义的自定义方法将暴露给其他对象,因此QML组件中根对象上的内联函数可以由组件外部的调用者调用。如果不希望这样做,可以将该方法添加到非根对象中,或者最好写在外部JavaScript文件中。
其实就是这个内联方法是有作用区域的。
工程实际中更推荐使用这个写法
重要的程序逻辑最好分离到一个单独的JavaScript文件中。这个文件可以使用import语句导入到QML中,就像QML模块一样。
例如,我们可以把前面例子中的fibonacci()方法移到一个名为fib.js的外部文件中,然后像下面这样访问它:
import QtQuick 2.12import "fib.js" as MathFunctionsItem {TapHandler {onTapped: console.log(MathFunctions.fibonacci(10))}}
有关将外部JavaScript文件加载到QML的更多信息,请阅读有关在QML中导入JavaScript资源的部分。
发出信号的QML对象类型还为其信号提供默认信号处理程序,如前一节所述。然而,有时客户端希望在另一个QML对象发出信号时触发QML对象中定义的函数。这种情况可以通过信号连接来处理。
通过调用信号的connect()方法并将JavaScript函数作为参数传递,QML对象发出的信号可以连接到JavaScript函数。例如,下面的代码将TapHandler的tap信号连接到script.js中的jsFunction():
import QtQuick 2.12
import "script.js" as MyScriptItem {id: itemwidth: 200; height: 200TapHandler {id: inputHandler}Component.onCompleted: {inputHandler.tapped.connect(MyScript.jsFunction)}
}
// script.jsfunction jsFunction() {console.log("Called JavaScript function!")
}
只要发出TapHandler的tap信号,jsFunction()就会被调用。
有时需要在应用程序(或组件实例)启动时运行一些命令式代码。虽然将启动脚本作为全局代码包含在外部脚本文件中很诱人,但这可能有严重的限制,因为QML环境可能尚未完全建立。例如,一些对象可能还没有创建,或者一些属性绑定可能还没有建立。要了解全局脚本代码的确切限制,请参阅JavaScript环境限制。
当QML对象的实例化完成时,它会发出Component.completed附加信号。对应组件中的JavaScript代码。onCompleted处理程序在对象实例化后运行。因此,编写应用程序启动代码的最佳位置是在组件中。在顶级对象的onCompleted处理程序中,因为当QML环境完全建立时,该对象会发出Component.completed。
例如:
import QtQuick 2.0Rectangle {function startupFunction() {// ... startup code}Component.onCompleted: startupFunction();
}
QML文件中的任何对象——包括嵌套的对象和嵌套的QML组件实例——都可以使用这个附加属性。如果启动时要执行多个onCompleted()处理程序,它们将以未定义的顺序顺序运行。
同样,每个组件在被销毁之前都会发出一个destroy()信号。