一起看 I/O | Dart 2.13 版现已发布
作者 / Kevin Moore & Michael Thomsen
空安全更新
在今年 3 月份发布的 Dart 2.12 中,我们推出了健全的空安全功能。空安全可谓是 Dart 最近推出的一项重要功能,旨在帮助您避免空值错误 (这类错误经常难以发现),有效提升工作效率。我们希望发布 package 的开发者能够及时跟进这项发布,更新 pub.dev 上分享的 package 以支持空安全。
健全的空安全 https://dart.cn/null-safety
我们极其欣喜地看到,在发布后的短短几个月内,空安全就已被广泛采用,目前 pub.dev 上前 500 个最受欢迎的 package 中,93% 的 package 已经支持空安全。在此,谨向如此迅速跟进的所有 package 开发者致以最诚挚的谢意,感谢大家帮助推动整个生态系统不断向前!
有了这么多 package 支持空安全,您就可以开始考虑着手将自己的应用迁移到使用空安全的环境。要开始迁移,请首先使用 dart pub outdated 检查应用的依赖项。详细步骤,请参阅空安全迁移指南。我们还调整了 dart create 和 flutter create 模板,现在它们在新的应用程序和 package 中默认启用空安全。
空安全迁移指南
https://dart.cn/null-safety/migration-guide#step1-wait
推出类型别名功能
类型别名是 2.13 版中新增的语言功能,也是广大开发者翘首以盼的功能,曾在语言问题的反馈中高居第二位。有了这一功能,开发者就能够创建函数类型的别名,但不能创建其他任何类型。
第二位
https://github.com/dart-lang/language/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc
利用类型别名您可以为任何现有的类型创建新的名称,然后将新创建的名称用在原始类型可以出现的任何地方。创建新名称并不会真的定义一个新类型,只不过是引入一个简短的别名而已。该别名甚至能通过类型等同测试:
typedef Integer = int;
void main() {
print(int == Integer); // true
}
那么,类型别名可以怎么用?一种常见的用法是给某类型指定一个更短或更具描述性的名称,以便您的代码更易于理解和维护。
比如,给 JSON 类型指定别名就是种不错的用法 (此示例由 GitHub 用户 Levi-Lesches 提供,特此感谢)。在下列示例中,我们可以定义一个新的类型别名 Json,它将一个 JSON 文档描述为一个 map,其键为 String,值为任意值 (使用动态类型)。这样,当我们定义名为 fromJson 的构造函数和 json get 函数时,就能使用该 Json 类型别名。
Levi-Lesches https://github.com/Levi-Lesches
typedef Json = Map<String, dynamic>;
class User {
final String name;
final int age;
User.fromJson(Json json) :
name = json['name'],
age = json['age'];
Json get json => {
'name': name,
'age': age,
};
}
您也可以对指代某个类的类型别名调用构造函数,比如以下示例就非常合规:
main() {
var j = Json();
j['name'] = 'Michael';
}
通过使用类型别名来指代复杂类型,可以让读者更容易理解您代码的不变量。例如,以下代码定义了一个类型别名来描述键值为泛型类型 X、值为类型 List<X> 的映射。如果给该类型指定一个具有单一类型参数的名称,映射的常规结构在代码读者眼中会变得更为清晰。
typedef MapToList<X> = Map<X, List<X>>;
void main() {
MapToList<int> m = {};
m[7] = [7]; // OK
m[8] = [2, 2, 2]; // OK
for (var x in m.keys) {
print('$x --> ${m[x]}');
}
}
=>
7 --> [7]
8 --> [2, 2, 2]
如果您尝试使用不匹配的类型,将出现分析错误:
m[42] = ['The', 'meaning', 'of', 'life'];
=>
The element type 'String' can't be assigned to the list type 'int'.
您甚至可以使用类型别名来重命名公共库中的类。假设现在公共库中有一个 PoorlyNamedClass 类,您想要将它重命名为 BetterNamedClass。如果您只是重命名该类,那么您的 API 客户那边将会出现突发编译错误。而使用类型别名,则不会出现这一问题,您可以随意重命名,只不过要先为旧的类名称定义一个新的类型别名,再给旧名称添加几行 @Deprecated 注解。这样,使用 PoorlyNamedClass 的代码虽然会出现警告,但仍可继续编译并照旧正常运行,让用户有时间升级其代码。
mylibrary.dart:
class BetterNamedClass {}
@Deprecated('Use BetterNamedClass instead')
typedef PoorlyNamedClass = BetterNamedClass;
main.dart
import 'mylibrary.dart';
void main() {
PoorlyNamedClass p;
}
=>
'PoorlyNamedClass' is deprecated and shouldn't be used. Use BetterNamedClass instead.
下面介绍实现 BetterNamedClass 和弃用 PoorlyNamedClass 的方法 (在一个名为 mylibrary.dart 的文件中)。
class BetterNamedClass {...}
@Deprecated('Use BetterNamedClass instead')
typedef PoorlyNamedClass = BetterNamedClass;
下面是尝试使用 PoorlyNamedClass 时会发生的情况:
import 'mylibrary.dart';
void main() {
PoorlyNamedClass p;
}
=>
'PoorlyNamedClass' is deprecated and shouldn't be used. Use BetterNamedClass instead.
类型别名功能从 Dart 2.13 版开始即可使用,要启用此功能,需要将您 pubspec 中版本较低的 Dart SDK 约束设置为最低 2.13 版,如下所示:
environment:
sdk: ">=2.13.0 <3.0.0"
此功能支持向后兼容,这要归功于语言的版本管理。也就是说,SDK 约束版本低于 2.13 的 package 可以安全地引用 2.13 版 package 中定义的类型别名,尽管 2.13 版之前的 package 不能定义其自己的类型别名。
语言的版本管理 https://dart.cn/guides/language/evolution#language-versioning
Dart 2.13 FFI 的变化
#35763 https://github.com/dart-lang/sdk/issues/35763
struct MyStruct {
uint8_t arr[8];
}
class StructInlineArray extends Struct {
@Array(8)
external Array<Uint8> arr;
}
#38158 https://github.com/dart-lang/sdk/issues/38158 封装结构体 http://www.catb.org/esr/structure-packing/
@Packed(4)
class TASKDIALOGCONFIG extends Struct {
@Uint32()
external int cbSize;
@IntPtr()
external int hwndParent;
@IntPtr()
external int hInstance;
@Uint32()
external int dwFlags;
…
}
Dart 2.13 在性能方面的提升
我们一直在不断努力降低 Dart 代码的应用体量和内存占用量。在大型 Flutter 应用中,经过 AOT 编译 Dart 程序的元数据的内部结构可能要占用非常可观的内存。这些元数据的存在大多是为了实现热重载、交互式调试,以及格式化可读堆栈轨迹等功能,这些功能在需要部署的应用中从不会用到。过去几年来,我们一直在重构 Dart 原生运行时环境,以便尽可能多地消除这种开销。其中一些改进适用于所有以版本模式构建的 Flutter 应用,而有些则需要使用 --split-debug-info 标志将 AOT 编译应用中的调试信息拆分出来,从而放弃可读的堆栈轨迹。
--split-debug-info
https://dart.cn/docs/perf/app-size#reducing-app-size
--split-debug-info
https://dart.cn/docs/perf/app-size#reducing-app-size
symbolize 命令
https://dart.cn/docs/deployment/obfuscate#reading-an-obfuscated-stack-trace
Dart 官方 Docker 镜像发布以及 Cloud 支持
官方镜像
https://docs.docker.com/docker-hub/official_images/
新的 Dart 镜像
https://hub.docker.com/_/dart
Cloud Run
https://cloud.google.com/run
虽然 Dart 始终专注于使 Flutter 等应用框架在每个屏幕上构建出色的界面,但我们意识到,大多数用户体验的背后至少有一个托管服务。通过让 Dart 轻松构建后端服务来支持全栈体验,开发者可以使用与前端 widget 相同的语言和业务逻辑,将他们的应用扩展到云端。
通常来说,将 Dart 用于 Flutter 应用程序的后端,特别符合 Google 无服务器管理平台 Cloud Run 的简单性和可扩展性。这也包括零扩展,意味着当您的后端不处理任何请求时,就不会产生成本。我们与 Google Cloud 团队合作,提供 Dart 的函数框架,这是一个 packages、工具和实例的集合,使开发者们能够轻松地编写 Dart 函数,以取代处理 HTTP 请求和 CloudEvents 的完整服务器部署。
Dart 的函数框架
https://pub.dev/packages/functions_framework
后续更新预告
静态分析
https://dart.cn//guides/language/analysis-options
lints
https://pub.dev/packages/lints
flutter_lints
https://pub.dev/packages/flutter_lints
#45451
https://github.com/dart-lang/sdk/issues/45451
Dart 2.13 版现已发布
Dart 2.13
https://dart.cn/get-dart
Flutter 2.2
https://flutter.cn/docs/get-started/
dart pub outdated
https://dart.cn/null-safety/migration-guide
推荐阅读