QML 编码规范

本文档包含我们在文档和示例中遵循的 QML 编码约定,并建议大家遵循。

QML 对象声明

在整个文档和示例中,QML 对象属性 总按照以下顺序结构化:

  • id
  • 属性声明
  • 信号声明
  • JavaScript 函数
  • 对象属性
  • 子对象

为了更好的可读性,我们用空行将他们分隔为不同的部分。

例如,有一个 QML 对象 photo,如下所示:

 Rectangle {
     id: photo                                               // id 在第一行,方便找到对象

     property bool thumbnail: false                          // 属性声明
     property alias image: photoImage.source

     signal clicked                                          // 信号声明

     function doSomething(x)                                 // JavaScript 函数
     {
         return x + photoImage.width
     }

     color: "gray"                                           // 对象属性
     x: 20                                                   // 尝试将相关的属性归为一组
     y: 20
     height: 150
     width: {                                                // large bindings
         if (photoImage.width > 200) {
             photoImage.width;
         } else {
             200;
         }
     }

     states: [
         State {
             name: "selected"
             PropertyChanges { target: border; color: "red" }
         }
     ]

     transitions: [
         Transition {
             from: ""
             to: "selected"
             ColorAnimation { target: border; duration: 200 }
         }
     ]

     Rectangle {                                             // 子对象
         id: border
         anchors.centerIn: parent; color: "white"

         Image {
             id: photoImage
             anchors.centerIn: parent
         }
     }
 }

分组属性

如果使用了一组属性中的多个属性,建议使用 组表示法,而不是 点表示法,这有助于提高可读性。

例如,下面这个例子:

 Rectangle {
     anchors.left: parent.left; anchors.top: parent.top; anchors.right: parent.right; anchors.leftMargin: 20
 }

 Text {
     text: "hello"
     font.bold: true; font.italic: true; font.pixelSize: 20; font.capitalization: Font.AllUppercase
 }

可以写成这样:

 Rectangle {
     anchors { left: parent.left; top: parent.top; right: parent.right; leftMargin: 20 }
 }

 Text {
     text: "hello"
     font { bold: true; italic: true; pixelSize: 20; capitalization: Font.AllUppercase }
 }

不合格的访问

为了提高可读性和性能,始终通过 id 显式引用父组件的属性:

 Item {
     id: root

     property int rectangleWidth: 50

     Rectangle {
         width: root.rectangleWidth
     }
 }

必需的属性

当需要定义在组件之外的数据时,请使用 Required Properties 明确这一点。必须设置 Required properties,否则组件的创建将失败。这些属性比无条件的查找更可取,因为它们的性能更高,并且允许用户和工具对外部属性的类型进行推理。此外,它们还消除了组件对其创建环境的假设。

信号处理

在信号处理程序中处理参数时,使用明确命名它们的函数:

 MouseArea {
     onClicked: (event) => { console.log(`${event.x},${event.y}`); }
 }

JavaScript 代码

如果脚本是单个表达式,建议将其写为内联的形式:

 Rectangle { color: "blue"; width: parent.width / 3 }

如果脚本只有几行,通常使用一个代码块:

 Rectangle {
     color: "blue"
     width: {
         var w = parent.width / 3
         console.debug(w)
         return w
     }
 }

如果脚本多于几行,或者需要被不同的对象使用,建议创建一个函数并像下面这样调用它:

 function calculateWidth(object : Item) : double
 {
     var w = object.width / 3
     // ...
     // more javascript code
     // ...
     console.debug(w)
     return w
 }

 Rectangle { color: "blue"; width: calculateWidth(parent) }

另请注意,为了更容易地推理和重构你的应用程序,建议给你的函数添加类型注释,因为参数和返回类型都可以从函数签名中立即看到。

对于很长的脚本,建议将函数放在自己的 JavaScript 文件中,并像下面这样导入它:

 import "myscript.js" as Script

 Rectangle { color: "blue"; width: Script.calculateWidth(parent) }

如果代码长度超过一行,并因此在一个代码块内,使用分号来表示每个语句的结尾:

 MouseArea {
     anchors.fill: parent
     onClicked: {
         var scenePos = mapToItem(null, mouseX, mouseY);
         console.log("MouseArea was clicked at scene pos " + scenePos);
     }
 }