作者 / Michael Thomsen, Product Manager working on Dart and Flutter
我们在 Google I/O 大会上发布了全新的 2.17 版 Dart SDK。此版本构建于我们的核心主题之上,即领先的工作效率与平台可移植性。Dart 2.17 带来了新的语言特性,包括在枚举中支持成员变量、改进超类参数继承及提高命名参数的灵活性等。我们推出全新的主要版本 package:lints
来帮助大家根据最佳实践检查 Dart 代码。同时,我们还大幅更新了核心库的 API 文档,为其带来了丰富的代码示例。为了改善平台集成效果,我们在 Flutter 插件中提供了新的模版,以便使用 dart:ffi
与原生平台进行 C 语言的互操作,还开始为 RISC-V 处理器提供实验性支持,以及为 macOS 和 Windows 可执行文件提供签名。
新语言特性助力工作效率提升
为了帮助开发者提升工作效率,我们一直在改进 Dart 语言,为其添加新特性并持续改进现有功能。Dart 2.17 新增对枚举成员变量的支持,优化在构造函数中使用命名参数的方式,并使超类参数的继承更加便捷,减少了冗长和重复的代码。
在枚举中支持成员变量
枚举非常适合用来表示一组离散的状态。例如,我们可以将水描述为 enum Water { frozen, lukewarm, boiling }
。但是,如果我们想在 enum
上添加一些方法,例如,将每个状态转换为温度,以及支持将 enum
转换为 String
,该怎么办?或许我们可以使用扩展方法来添加一个 waterToTemp()
方法,但必须注意让其与 enum
保持同步。对于 String
转换,我们希望重写 toString()
,但这个做法在之前的版本不受支持。
Dart 2.17 中现已全面支持枚举成员变量。因此,我们可以添加保存状态的字段、设置状态的构造函数、具备功能的方法,甚至重写现有方法。许多开发者对此功能一直有需求,这是我们语言问题跟踪专页中 投票排名第三的问题。
再次以水为例,我们可以添加一个保存温度的 int 字段,并添加一个接收 int的默认构造函数:
enum Water {
…
final int tempInFahrenheit;
const Water(this.tempInFahrenheit);
}
为了确保在创建 enum
时构造函数被调用,我们需要为每一个 enum
值进行调用:
enum Water {
frozen(32),
lukewarm(100),
boiling(212);
…
}
要支持转换为 String
,我们只需重写 enums
继承自 Object
的 toString
方法:
@override
String toString() => "The $name water is $tempInFahrenheit F.";
这样即可获得一个可以轻松实例化的完整 enum
,您可以在其上调用方法:
void main() {
print(Water.frozen); // Prints “The frozen water is 32 F.”
}
以下为这两种方法的完整示例。不难看出,全新 Dart 2.17 版本的代码更加易于阅读和维护。
超类初始化构造
如果您的代码存在类继承层次结构,常见模式是将一些构造函数参数传递给超类的构造函数。为此,子类需要 1) 在其构造函数中列出每个参数;2) 使用这些参数调用超类的构造函数。这样会导致样板代码反复出现,从而增加代码的阅读和维护难度。
在几位 Dart 社区成员的帮助下,我们的改进目标得以实现。半年前,GitHub 用户 @roy-sianez 提交了一个相关的 语言问题。他的建议与 GitHub 用户 @apps-transround 之前提出的建议 类似: 也许我们可以这样解决问题,即引入新的构造方式,来表示超类中已指定一个参数。我们认为这个想法不错,所以将其添加在 Dart 2.17 中。从下面的示例中不难看出,这尤其与 Flutter widget 代码密切相关。实际上,在我们将这项新特性应用到 Flutter 框架后,代码总共 减少了近两千行!
在任意参数位置使用命名参数
最后,我们改进了调用某个方法时命名参数的使用方式。此前,命名参数只能出现在方法参数列表的最后。如果您希望让位置参数靠后,从而提升代码可读性,这种限制就会令人感到非常烦恼。以下面 List<T>.generate
构造函数的调用为例: 之前,growable 参数必须放在最后,因为它位于含生成器的大型位置参数之后,很容易在阅读时被错过。而现在,您可以根据喜好排列参数,先放置小型命名参数,最后再放置带生成器的参数。
更多有关这三个特性的示例,请参阅我们更新的 枚举、超类初始化构造 和 命名参数 示例代码。
工作效率工具
在工作效率主题方面,我们对核心工具进行了一些改进。
在 Dart 2.14 中,我们引入了 package:lints
,它可与 Dart 分析器一起工作,以帮助您避免编写出包含错误的 Dart 代码,并通过规范化的方式提升代码审核效率。后来分析器中又新增了许多 Lint,我们对其进行了仔细分类,并从中选择了 10 条新的 Lint 用于所有 Dart 代码,还有 2 条新的 Lint 专门用于 Flutter 代码。其中一些 Lint 可确保 pubspec 文件中包含导入内容,防止滥用类型参数空检查,以及保证子属性格式一致。您可以通过简单的命令升级到新的 Lint:
-
Dart package 可以使用:
dart pub upgrade —-major-versions lints
-
Flutter package 可以使用:
flutter pub upgrade —-major-versions flutter_lints
SecureSockets 通常用于启用受 TLS 和 SSL 保护的 TCP 套接字。Dart 2.17 发布之前,由于无法检查安全数据流量,开发过程中调试这些套接字非常困难。现在我们添加了指定 keyLog
文件的功能。指定日志文件后,与服务器交换新的 TLS 密钥时,一行 NSS 密钥日志格式 的文本就会附加到文件中。这样,网络流量分析工具 (如 Wireshark) 即可解密通过套接字发送的内容。更多详细信息,请参阅 SecureSocket.connect()
的 API 文档。
dart doc
工具生成的 API 文档是大多数 Dart 开发者学习新 API 的重要资源之一。尽管我们的 核心库 API 一直都有详实的文本描述,但许多开发者告诉我们,他们更喜欢通过阅读示例代码来学习 API。在 Dart 2.17 中,我们彻底检查了所有主要的核心库,为浏览量排在前 200 名的页面添加了详实的示例代码。您可以对比 Dart 2.16 和 Dart 2.17 中 dart:convert 的文档页面,希望这些更新有助于您更轻松地使用 API 文档。
为平台新增功能可以提升工作效率,清理堆积的既有问题,并删除弃用的功能同样也可以。这样做有助于保持精简的使用体验,对新上手的开发者而言,这一点尤为重要。为此,我们从 dart:io
库中删除了 231 行已弃用的代码。如果您仍在使用这些已弃用的 API,可以使用 dart fix 进行修复和替换。我们还在继续努力删除 已弃用的 Dart CLI 工具,此外,本次更新删除了 dartdoc
工具 (使用 dart doc
代替) 和 pub
工具 (使用 dart pub
或 flutter pub
代替)。
扩大平台集成与支持
平台集成与支持是我们的第二个核心主题。Dart 是一种真正的多平台语言。尽管我们现已支持 大量平台,但仍在不断拓展演进,确保您可以与每个受支持的平台深度集成,同时也支持新出现的平台。
Dart FFI 是我们 与 C 语言或原生代码互操作 的核心机制,广泛用于将 Dart 代码与现有原生平台代码集成。在 Flutter 上,Dart FFI 也是构建使用托管平台原生 API (例如 Windows win32 API) 的 插件 的理想方法。在 Dart 2.17 和 Flutter 3 中,我们为 flutter 工具添加了模板,现在您可以轻松地创建 FFI 插件,这些插件的 Dart API 通过 dart:ffi 调用原生代码。详细信息请参阅官方文档中的 "开发 package 和插件"。
为支持在具有 ABI (应用程序二进制接口) 特有类型的平台上使用 FFI,FFI 现已支持 ABI 特有类型。例如,现在您可以使用 Long (C 语言中的 long
) 正确表示 ABI 特有大小的长整数,由于 CPU 架构的区别,结果可能是 32 位或 64 位。有关支持类型的完整列表,请参阅 AbiSpecificInteger API 页面中的 "Implementers" 列表。
在使用 Dart FFI 与原生平台深度集成时,有时开发者需要调整适配 Dart 代码和原生代码的内存或其他资源 (端口、文件等) 的清理行为。长期以来,这个问题都十分棘手,因为 Dart 是一种会自动处理清理行为的垃圾回收语言。在 Dart 2.17 中,我们通过引入 Finalizer 的概念,解决了这个问题。Finalizer 中包括一个 Finalizable 标记接口,用于 "标记" 不应过早终结或丢弃的对象,以及一个可以附加到 Dart 对象的 NativeFinalizer 类,在对象即将被垃圾回收时提供回调运行。这样,在原生代码和 Dart 代码中都可以运行清理代码。更多详细信息,请参阅 NativeFinalizer API 文档,或参阅 WeakReference 和 Finalizer 文档中的描述和示例,以了解常规 Dart 代码中的类似做法。
支持将 Dart 编译为原生代码,是使 Flutter 应用具有出色启动性能和快速渲染能力的核心要素。除此之外,您还可以使用 dart compile 将 Dart 文件编译为可执行文件。这些可执行文件可以在任何机器上独立运行,无需安装 Dart SDK。Dart 2.17 中的另一个新功能是支持 对可执行文件进行签名,从而在往往需要签名的 Windows 和 macOS 上进行部署。
我们还在积极跟进新平台的发展,不断扩大可支持的平台范围。RISC-V 是一个全新的处理器指令集。RISC-V International 是一家全球非营利性组织,其拥有 RISC-V 规范并使该指令集保持自由开放的状态。尽管仍是新平台,但其潜力无限,因此我们的 2.17.0–266.1.beta Linux 版本 (以后可能进入我们的 beta 渠道) 中已经为其提供了实验性的支持。我们希望能够获得您的反馈,请大家不吝 提出问题 或 分享使用体验!
即刻使用 Dart 2.17!
我们希望 Dart 2.17 版本能打动您,并能助力您提高工作效率,把您的应用带去更多平台。您可以即刻下载 Dart 2.17 并开始使用,或者使用 Flutter 3 SDK 中包含的 Dart SDK。
别忘了观看 Google I/O 大会中 Flutter 的最新内容。