查看原文
其他

Dart 健全的空安全: Beta 版

Flutter 谷歌开发者 2021-10-12

作者 / Michael Thomsen, Dart & Flutter Product Manager, Google


我们为 Dart 和 Flutter 带来了健全的空安全 Beta 版。空安全是提升生产力的最新功能,可帮助您避免空值错误,这类错误通常很难发现。想快速了解我们对空安全如此期待的原因,请观看这支视频:

  • 健全的空安全

    https://dart.cn/null-safety

  • 腾讯视频链接

    https://v.qq.com/x/page/n3208kis0wj.html

  • Bilibili 视频链接

    https://www.bilibili.com/video/BV1GV411Y7sW/


随着空安全 Beta 版的发布,是时候开始动员社区pub.dev 上的数千个 package 进行迁移了。我们已经迁移了 Dart 核心库、Flutter 框架以及超过 40 个 Dart 和 Flutter package。同时我们希望社区也能开始迁移 package,拥抱空安全。


  • pub.dev
    https://pub.flutter-io.cn/
  • 空安全 package
    https://pub.flutter-io.cn/packages?q=&prerelease-null-safe=1

在 Beta 版的基础上,我们也开始向空安全的稳定版发起冲刺。希望您能够开始使用空安全,并告诉我们任何可以改进之处,包括界面消息是否易于理解,或者文档是否清晰易读。我们非常期待您的反馈


  • 提交反馈
    https://github.com/dart-lang/sdk/issues/new?title=Null%20safety%20feedback:%20[issue%20summary]&labels=NNBD&body=Describe%20the%20issue%20or%20potential%20improvement%20in%20detail%20here



启用空安全


在讨论空安全迁移之前,我们需要重申 (如我们的空安全原则中所述),何时开始采用空安全完全由您来决定。如果应用和 package 的最低 Dart SDK 版本约束为 Dart 2.12 预发布版或更高版本,它们将默认以空安全状态运行:


  • 空安全原则
    https://dart.cn/null-safety#null-safety-principles
  • 启用空安全
    https://dart.cn/null-safety#enable-null-safety
environment: sdk: ">=2.12.0-0 <3.0.0"


如需体验空安全,请尝试创建 (例如,使用 dart create) 一个包含如下代码的小型空安全 hello 应用。然后,您可以尝试在更改 SDK 约束并运行 dart pub get 前后分别运行该应用,来体验程序行为的变化。(请确保使用 dart --version 命令,返回的是 2.12 的 SDK 版本)

bin/hello.dart:...void main() { var hello = 'Hello Dart developers'; if (someCondition) { hello = null; } print(hello);}Before changing the SDK constraint:$ dart runnullAfter changing the SDK constraint (and running dart pub get):$ dart runbin/hello.dart:6:13: Error: Null can't be assigned to a variable of type 'String' because 'String' is not nullable.hello = null; ^



迁移至空安全


要将 package (或简单应用) 迁移至空安全,请按照以下五个步骤进行操作,详细的内容说明请查看迁移指南:

https://dart.cn/null-safety/migration-guide

第 1 步: 检查以确保依赖项已就绪

我们强烈建议按顺序迁移代码,首先迁移依赖关系图中的子项。如下图所示,如果 C 依赖于 B,而 B 依赖于 A,那么应首先将 A 迁移到空安全,然后再依次迁移 B 和 C。无论 A、B 和 C 是库、package 还是应用,该顺序都适用。

迁移顺序为何如此重要?虽然您可以在迁移依赖项之前先处理一些代码迁移工作,但如果依赖项在迁移期间更改了其 API,那么您将面临必须重新执行迁移的风险。如果您的某些依赖项没有支持空安全,请考虑使用 pub.dev 上 package 所给出的联系方式与 package 发布者联系。


验证依赖项是否已就绪

要验证您的应用或 package 是否已准备好开始迁移,您可以在空安全模式下使用 dart pub outdated。比如下图所示应用,只需将其依赖项 path、process 和 pedantic 升级到 Resolvable 列中给出的预发布版本,就表示已做好了迁移的准备。

如果依赖项的小版本升级已经提供了空安全支持,则会显示在 Upgradable 列中。空安全支持通常由大版本更新来提供,这时 outdated 输出结果的 Resolvable 下方将列出相应的版本。要升级到这些版本,请编辑 pubspec.yaml 文件以允许获取这些大版本更新。例如,将 process: ^3.0.13 更改为 process: ^4.0.0-nullsafety


您还可以在 pub.dev 上的 package 页面使用新的 Null safety 标签查看支持空安全的 package (例如 collection 1.15),我们还在高级搜索中提供了新的空安全搜索选项 (如下图所示)。


  • collection 1.15
    https://pub.flutter-io.cn/packages/collection/versions/1.15.0-nullsafety.5
  • 空安全搜索选项
    https://pub.flutter-io.cn/packages?q=&prerelease-null-safe=1

第 2 步: 使用迁移工具进行迁移

依赖项准备就绪后,您就可以使用迁移工具 dart migration 来迁移应用或 package 了。


迁移工具是交互式的,您可以查看工具推断出的可空属性。如果您对工具给出的结论有异议,则可以添加可空性提示以更改推断。适当添加迁移提示有可能大幅提升迁移质量。

我们曾安排少量的 Dart package 作者使用空安全的早期预览版进行了测试性迁移,并取得了颇为积极的结果。迁移指南中给出了更多迁移工具的使用技巧。


  • 迁移指南
    https://dart.cn/null-safety/migration-guide


第 3 步: 静态分析迁移后的代码

在 IDE 或命令行中使用 pub get 更新 package。然后使用 IDE 或命令行对您的 Dart 代码执行静态分析:

$ dart pub get$ dart analyze

如果是 Flutter 代码,执行静态分析的命令如下:

$ flutter pub get$ flutter analyze

第 4 步: 确保测试通过
运行测试,并确保测试通过。如果您曾将 package 代码更改为不再允许为空,则可能需要更新可接受空值的测试。

第 5 步: 发布您的空安全 package

迁移完成且测试通过后,您便可以将 package 发布为预发布版。这里简单给出两种最佳做法供参考:

  • 将您的版本号升至下一个大版本 (例如,从 2.3.x 到 3.0.0)。此最佳做法可确保您的 package 用户不会在尚未做好使用空安全的准备时就升级至该版本,并且使您可以自由重构 API 以最充分地利用空安全。
  • 将 package 在 pub.dev 上以预发行的方式进行发布。(例如,使用 3.0.0-nullsafety.0,而非 3.0.0)


  • 以预发行的方式发布
    https://dart.cn/tools/pub/publishing#publishing-prereleases

有关迁移和发布版本的详细信息,请参阅迁移指南:

https://dart.cn/null-safety/migration-guide



健全空安全的优势


在之前 Dart 和 Flutter 空安全支持的技术预览版中,我们结合大量示例探讨了空安全的优势。随着空安全的逐步完成,我们将分享给大家一些真实案例中看到的空安全的优势。


代码安全性提升

就在最近,我们在 Flutter 的 master 渠道中发现了一个错误,多个 flutter 工具命令在特定计算机配置下会因为空值错误发生崩溃: The method '>=' was called on null。根本问题源于近期的一项拉取请求 (PR),用于添加对 Android Studio 4.1 的检测。该 PR 添加了如下代码:

final int major = version?.major;final int minor = version?.minor;if (globals.platform.isMacOS) { /// plugin path of Android Studio changed after version 4.1. if (major >= 4 && minor >= 1) {

您能发现里面的问题吗?因为 version 可能为空,major 和 minor 也都可能为空。如果单独检查这段代码,这个错误似乎并不难发现。但实际上,即使经过了严格的代码审查 (如 Flutter repo 所用代码审查流程),也总是难免有这样的漏网之鱼。借助空安全,静态分析能够立即捕捉到这一问题:

  • Dartpad 捕捉此问题的演示
    https://dartpad.cn/0e9797be7488d8ec6c3fca92b7f2740f


这只是一个非常简单的错误。在 Google 内部早期使用空安全代码时,捕捉并解决的复杂错误远不止于此。例如:

  • 一个内部团队发现,他们经常在代码中检查是否存在空值,但空安全知道那些值永远不会为空。这一状况常见于使用 protobuf 的代码,在这些代码中,可选字段在未经设置时会返回默认值,而永远不会为空。这会导致代码混淆默认值和空值,并错误地检查默认条件。

  • Google Pay 团队在他们的 Flutter 代码中发现了一些错误,在尝试访问 Widget 上下文之外的 Flutter State 对象时会出错。在引入空安全之前,这些代码会返回空值并掩盖错误;通过空安全健全的分析,确定这些属性永远不会为空,并抛出分析错误。

  • Flutter 团队发现了一个错误,如果向 Window.render() 中的 scene 参数传递空值,则 Flutter 引擎可能会崩溃。在进行空安全迁移期间,他们添加了一个提示以将 Scene 标记为不可空,然后便可轻松防止传递空值可能会引发的应用崩溃问题。


  • Protocol Buffers
    https://developers.google.cn/protocol-buffers
  • 将 Scene 标记为不可空
    https://github.com/cbracken/engine/blob/bad869e229a8a02cad6e63d12e80807b33b5c12f/lib/ui/window.dart#L1069

编译过程受益于健全的空安全
Dart 空安全的健全性还暗含了另一项广受好评的优势: 这意味着 Dart 编译器可以利用可空信息做出优化。这有可能使您的程序更加小巧而快速。完全迁移到健全的空安全的真实应用目前并不多 (因为我们现在才刚刚开始在生态系统内推进 package 的迁移,而很多应用依赖于这些 package),但从核心框架来看,我们得到了十分令人鼓舞的结果。

最近,我们对 hello_world 示例应用进行了重新编译测试,以衡量空安全对应用大小的影响。它是一个仅仅显示 "hello world" 的简单示例。对比编译后的代码总大小可以发现,在仅仅采用健全的空安全重新编译而无任何其他措施的情况下,未压缩 (安装在设备上) 的代码大小缩减了 3.5%。在只有 10 行代码的应用中实现这一成果,其原因是该应用所包含的所有库的代码大小都得到了缩减。例如 Flutter 框架本身 (package:flutter) 就缩减了 3.9%。

  • hello_world 示例应用
    https://github.com/flutter/flutter/blob/master/examples/hello_world/lib/main.dart
  • 应用大小比较
    https://gist.github.com/mit-mit/64e160f9dc3bf6c69c7ef2f81384594a


代码运行速度方面,如果必须强制执行健全类型系统,则可能会增加开销。但是,由于空值检查变少,因此也可能会提高代码运行的速度。我们初步的基准分析表明,性能与以前的版本处于同等水平,并且新的附加类型信息为我们将来实现新的性能提升带来了可能性。我们计划在未来更详细地和大家分享在性能提升方面所做的工作。


在某些情况下,我们已经看到空安全可以提高性能,特别是在向空安全迁移的过程中发现代码逻辑中的缺陷。例如,我们在 Flutter web 的文本布局缓存中发现了一个问题。此缓存使用了一个可空的键,然后某些逻辑会在出现空值时使用 TextAlign.start。这种逻辑在缓存中造成了一个缺陷: 元素虽保有默认值,但看起来却像发生了变化。其结果是频繁出现缓存未命中的情况。添加不可空的 textAlign getter 有助于修复缓存缺陷,在文本得到缓存的情况下,其渲染性能提高了 14 倍



即刻开始体验!


Dart 和 Flutter 空安全的 Beta 版已经发布,如果您使用 Flutter 进行开发,可以通过 flutter channel beta 切换到 Beta 版,然后执行 flutter upgrade。您也可以从 Dart SDK 归档页面中获取独立的 Dart SDK。

  • Dart SDK 归档

    https://dart.cn/tools/sdk/archive#beta-channel


如果您是 package 作者,建议您阅读我们的迁移指南并制定迁移计划。如果您有任何疑问或建议,请告诉我们


  • 迁移指南

    https://dart.cn/null-safety/migration-guide

  • 提供反馈

    https://github.com/dart-lang/sdk/issues/new?title=Null%20safety%20feedback:%20[issue%20summary]&labels=NNBD&body=Describe%20the%20issue%20or%20potential%20improvement%20in%20detail%20here


如果您是应用开发者,可以暂缓迁移并等待空安全进入稳定版。我们计划尽快解决在 Beta 版本中收集到的反馈,并修复所有遗留问题。现在很难为空安全的稳定版本设定一个具体的时间表,但我们正在考虑于明年年初发布。

感谢您一直以来的支持与反馈,帮助我们使 Dart 成为更加高效可靠的语言,使 Flutter 成为更加强大的框架!也欢迎大家继续在评论区和我们分享您的想法和建议!


推荐阅读






 点击屏末 | 阅读原文 | 访问 Flutter 开发者社区中文资源



: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存