GSYFlutterBook/Dart-300.md

266 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Google I/O 2023 - Dart 3 发布,快来看看有什么更新吧
> 核心原文链接: https://medium.com/dartlang/announcing-dart-3-53f065a10635
自从 Flutter Forword 发布了 [Dart 3α 预览](https://juejin.cn/post/7194741144482218045) 之后,大家对 Dart 3 的正式发布就一直翘首以待,这不仅仅是 Dart 版本号追上了 Flutter 版本号,更是 Dart 在 2.0 之后迎来的最大一次更新,主要包括了:
- 100% 空安全
- records
- patterns
- class modifiers
- Wasm 对 Web 的增加支持,可以预览 dart wasm native 了
# 100% 空安全支持
如下图所示Dart 的 null safety 历经三年的时间,如今 Dart 终于有用了完善的类型系统,现在的 Dart 3 下,如果一个类型说一个值不是 `null`,那么它永远不可能是 `null`
![](http://img.cdn.guoshuyu.cn/20230511_D3/image1.png)
> 说起来,还真有不少用户的项目没升级到 null safety ,这次就不能再等了。
另外,目前 pub.dev 上排名前 1000 的包中有 99% 支持空安全,所以官方预计升级到 Dart 3 的兼容问题并不大少数情况下Dart 3 中的对一些历史代码的相关清理可能会影响某些代码的运行,例如
- 一些旧的核心库 API 已被删除([#34233](https://github.com/dart-lang/sdk/issues/34233)、[#49529](https://github.com/dart-lang/sdk/issues/49529)
- 一些工具已被调整([#50707](https://github.com/dart-lang/sdk/issues/50707))。
> 如果你在迁移到到 Dart 3 时遇到问题,可以查阅 https://dart.dev/resources/dart-3-migration
# Record, patterns 和 class modifiers
关于万众期待的 record 和 patterns 其实在之前的 [Dart 3α 新特性 Record 和 Patterns 的提前预览讲解](https://juejin.cn/post/7194741144482218045)上已经有个详细解释,这里主要重新根据官方内容简诉一些这些变化。
## 使用 record 构建结构化数据
在此之前 Dart 函数只能返回一个值,如果需要返回多个值,必须将这些值打包成其他数据类型,例如 Map 或 List或者定义可以保存这些值的新类。
使用非类型化数据结构削弱了类型安全性,而定义新类来传输数据会增加编码过程中的工作量,但是现在,通过 record 就可以简洁明地构建结构化数据:
```dart
(String, int) userInfo(Map<String, dynamic> json) {
return (json['name'] as String, json['height'] as int);
}
```
在 Dart 中record 是一个通用功能,它们不仅可以用于函数返回值,还可以将它们存储在变量中,例如将它们放入 List 中或者它们用作 Map 中的键,或创建包含其他 record 的 record。
另外还可以添加未命名字段,就像我们在前面的示例中所做的那样,也可以添加命名字段,例如 `(42, description: Meaning of life)`
record 是值类型,没有标识,这让编译器能够在某些情况下完全擦除记录对象,记录还带有自动定义的 `==` 运算符和 `hashCode` 函数。
> 详细可以参考官方文档https://dart.dev/language/records 或者之前相关的中文资料: https://juejin.cn/post/7194741144482218045
## 使用具有 pattern 和 pattern 匹配的结构化数据
record 简化了构建结构化数据的方式,这不会取代使用类来构建正式的类型层次结构的方式,它只是提供了另一种选择。
在任何一种情况下,你可能希望将结构化数据分解为单独的元素,这就是 pattern 匹配发挥作用的地方。
考虑 pattern 的基本形式,以下记录 pattern 将 record 解构为两个新变量 `name``height` ,然后可以像任何其他变量一样使用这些变量:
```dart
var (String name, int height) = userInfo({'name': 'Michael', 'height': 180});
print('User $name is $height cm tall.');
```
List 和 Map 存在类似的 pattern ,都可以使用下划线模式跳过单个元素:
```dart
var (String name, _) = userInfo();
```
在 switch 语法中, Dart 3 扩展了语句 switch 的支持,现在支持在这些情况下进行 pattern 匹配:
```dart
switch (charCode) {
case slash when nextCharCode == slash:
skipComment();
case slash || star || plus || minus:
operator(charCode);
case >= digit0 && <= digit9:
number();
default:
invalid();
}
```
还可以通过新的表达式进行微调,以下示例函数返回 switch 表达式的值以计算今天工作日的描述:
```dart
String describeDate(DateTime dt) =>
switch (dt.weekday) {
1 => 'Feeling the Monday blues?',
6 || 7 => 'Enjoy the weekend!',
_ => 'Hang in there.'
};
```
**模式的一个强大功能是检查 “exhaustiveness” 的能力,此功能可确保 switch 处理所有可能的情况**
在前面的示例中,我们正在处理工作日的所有可能值,这是一个`int` ,所以我们通过针对特定值 `1``6 `/`7` 的匹配语句的组合来穷尽所有可能的值,然后通过 `_` 对其余情况使用默认情况。
要对用户定义的数据层次结构(例如类层次结构)启用该能力,请在类层次结构的顶部使用 `sealed` 修饰符,如下例所示:
```dart
sealed class Animal { }
class Cow extends Animal { }
class Sheep extends Animal { }
class Pig extends Animal { }
String whatDoesItSay(Animal a) =>
switch (a) { Cow c => ' $c says moo' , Sheep s => ' $s says baa' };
```
这将返回以下错误,提醒我们错过了最后一个可能的子类型 Pig 的处理:
```
line 6 • The type 'Animal' is not exhaustively matched by the switch cases
since it doesn't match 'Pig()'.
```
最后,`if ` 语句也可以使用 pattern ,在下面的例子里,我们使用 *if-case* 匹配映射模式来解构 JSON 映射,这里匹配常量值(字符串如 `'name'` and `'Michael' `)和类型测试模式 `int h` 以读出 JSON 值如果模式匹配失败Dart 将执行该 `else` 语句。
```dart
final json = {'name': 'Michael', 'height': 180};
// Find Michael's height.
if (json case {'name': 'Michael', 'height': int h}) {
print('Michael is $h cm tall.');
} else {
print('Error: json contains no height info for Michael!');
}
```
> 详细可以参考官方文档http://dart.dev/language/patterns 或者之前相关的中文资料: https://juejin.cn/post/7194741144482218045
# classes with class modifiers
Dart 3 的第三个语言特性是类修饰符,与前两个支持不同的是,这更像是一个高级用户功能,它主要是为了满足了 Dart 开发人员制作大型 API 或构建企业级应用时的需求。
> 目前是基于 *constructed*、 *extended* 和 *implemented* 来实现处理,关键词有
类修饰符使 API 作者能够仅支持一些特定的功能,而默认值保持不变,例如:`abstract`、`base` 、`final`、`interface`、`sealed`、`mixin` 。
> 只有`base `修饰符可以出现在 mixin 声明之前,修饰符不适用于其他声明如 `enum`、`typedef`或 `extension`。
```dart
class Vehicle {
String make; String model;
void moveForward(int meters) { }
}
// Construct.
var myCar = Vehicle(make: 'Ford', model: 'T',);
// Extend.
class Car extends Vehicle {
int passengers;
}
// Implement.
class MockVehicle implements Vehicle {
@override void moveForward
}
```
例如要强制继承类或 mixin 的实现,就可以使用 `base` 修饰符。 `base` 不允许在其自己的库之外实现,这保证:
- 每当创建类的子类型的实例时,都会调用基类构造函数
- 所有实现的私有成员都存在于子类型中
- 类中新实现的成 员`base `不会破坏子类型,因为所有子类型都继承了新成员
```dart
// Library a.dart
base class Vehicle {
void moveForward(int meters) { ... }
}
// Library b.dart
import 'a.dart';
var myCar = Vehicle(); // Can be constructed
base class Car extends Vehicle { // Can be extended
int passengers;
// ...
}
base class MockVehicle implements Vehicle { // ERROR: Cannot be implemented
@override
void moveForward { ... }
}
```
如果要创建一组已知的、可枚举的子类型,就可以使用修饰符 `sealed` [sealed 允许在那些静态](https://dart.dev/language/branches#exhaustiveness-checking)子类型上创建一个 switch 。
```dart
sealed class Vehicle { ... }
class Car extends Vehicle { }
class Truck implements Vehicle { }
class Bicycle extends Vehicle { }
// ...
var vehicle = Vehicle(); // ERROR: Cannot be instantiated
var vehicle = Car(); // Subclasses can be instantiated
// ...
// ERROR: The switch is missing the Bicycle subtype or a default case.
return switch (vehicle) {
Car() => 'vroom',
Truck() => 'VROOOOMM'
};
```
类修饰符存在一些添加限制,例如:
- 使用 `interface class` ,可以定义 contract 给其他人去实现,但不能扩展接口类。
- 使用 `base class`,可以确保类的所有子类型都继承自它,而不是实现它的接口,这确保私有方法在所有实例上都可用。
- 使用 `final class`,可以关闭类型层次结构,以防止自己的库之外的任何子类。这样的好处是允许 API 所有者添加新成员,而不会出现破坏 API 使用者更改的风险。
> 是不是没看明白?有关详细信息,可以参考 https://dart.dev/language/class-modifiers
# 展望未来
Dart 3 不仅仅是是在这些新功能上向前迈出了重要的一步,还为大家提供了下一步的预览。
## Dart language
Records, patterns 和 class modifiers 是非常庞大的新功能,因此它们的某些设计可能还需要改进,所以接下来还会有一些更小、更增量的功能更新,这些功能完全不会中断,并且专注于在没有迁移成本的情况下提高开发人员的工作效率。
目前正在探索的还有 [primary constructors](https://github.com/dart-lang/language/issues/2364) 和 [inline classes](https://github.com/dart-lang/language/issues/2727) 包装,另外之前讨论过的宏(也称为[元编程](https://github.com/dart-lang/language/blob/main/working/macros/feature-specification.md))也在进行探索,因为元编程的规模和固有风险,目前正在采取一种更有效和彻底的方法进行探索,因此没有具体的时间表可以分享,即使是最终确定的设计决策。
## native interop
移动和桌面上的应用通常依赖于 native 平台提供的大量 API无论是通知、支付还是获取手机位置等。
在之前 Flutter 中,这些是通过构建插件来访问的,这需要为 API 编写 Dart 代码和一堆特定于平台的代码来提供实现。
目前已经支持与使用 `dart:ffi` 直接和原生语言进行交互我们目前正在努力扩展它在Android 上的支持,再次之前可以看 [Java 和 Kotlin interop ](https://dart.dev/guides/libraries/java-interop) 以及 [Objective-C 和 Swift interop](https://juejin.cn/post/7137874832988831751) 。
> 请查看新的 Google I/O 23 的 [Android interop 视频](https://io.google/2023/program/2f02692d-9a41-49c0-8786-1a22b7155628/)。
## 编译为 WebAssembly——使用 native 代码定位 web
[WebAssembly (缩写为 Wasm作为跨](https://webassembly.org/)[所有浏览器的](https://caniuse.com/wasm)平台的二进制指令格式其可用性度一直在增长Flutter 框架使用 Wasm 有一段时间了,这就是我们如何通过 Wasm 编译模块将用 C++ 编写的 SKIA 图形渲染引擎交付给浏览器的实现。
Flutter 也一直对使用 Wasm 来部署 Dart 代码很感兴趣,但是在此之前该实现被阻止了,与许多其他面向对象的语言一样,因为 Dart 需要使用垃圾回收。
在过去的一年里Flutter 和 Wasm 生态系统中的多个团队合作,将新的 WasmGC 功能添加到 WebAssembly 标准中,目前在 Chromium 和 Firefox 浏览器中已经接近稳定。
将 Dart 编译为 Wasm 模块的工作有两个针对 Web 的高级目标:
- **加载时间:**我们希望我们可以使用 Wasm 交付部署有效负载,使浏览器可以更快地加载,从而缩短到达用户可以与 Web 交互的时间。
- **性能:**由 JavaScript 提供支持的 Web 应用需要即时编译才能获得良好的性能Wasm 模块更底层,更接近机器代码,因此我们认为它们可以提供更高的性能、更少的卡顿和更一致的帧率。
- **语义一致性**Dart 在我们支持的平台之间保持高度一致而自豪。但是,在 web 上有一些例外情况,例如 Dart web 目前在[数字表示](https://dart.dev/guides/language/numbers)方式上有所不同,而使用 Wasm 模块,我们可以将 web 视为具有与其他原生目标相似语义的“原生”平台。
**跟随 Dart3 的发布, Dart 到 Wasm 编译的第一个预览也一起发布**,这是最初的 Flutter Web 重点支持。虽然现在还早,后续还有很多工作要完成,但已经可以通过 https://flutter.dev/wasm 开始测试。