迁移至空安全
本文将介绍如何将你的代码迁移至 空安全。以下是对你的 package 逐个迁移的基本步骤:
-
等待 你依赖的 package 迁移完成。
-
迁移 你的 package 的代码,最好使用交互式的迁移工具。
-
静态分析 package 的代码。
-
测试 你的代码,确保可用。
-
如果你已经在 pub.dev 上发布了你的 package,可以将迁移完成的空安全版本以 预发布 版本进行 发布。
如果你想预览迁移工具的体验,可以查看以下视频:
可交互的迁移工具让你可以简化迁移至空安全的过程。
1. 等待迁移
我们强烈建议你按顺序迁移代码,先迁移依赖关系中的处于最末端的依赖。例如,如果 C 依赖了 B,B 依赖了 A,那么应该按照 A -> B -> C 的顺序进行迁移。
虽然你在你的所有依赖迁移完成前就 可以 进行迁移,但在它们迁移完成后,你可能需要再对你的代码进行调整。例如,如果你推测一个函数可以接受一个可空的参数,但依赖的 package 迁移后变为了非空,在传递可空的参数时便会出现编译错误。
该节会讲述如何在空安全模式下,使用 dart pub outdated
检查并更新你的依赖。如果你的代码应用了 版本管理,你可以随时回滚所有的改动。
切换至 Dart 2.19.6 版本
切换到 Dart SDK 的 2.19.6 稳定版,它包含在 Flutter 3.7.12 SDK 中。
执行下面代码查看是否使用了 Dart 2.19.6 版本:
$ dart --version
Dart SDK version: 2.19.6
检查所有依赖的迁移状态
通过以下命令检查你的 package 的迁移状态:
$ dart pub outdated --mode=null-safety
如果你看到所有依赖都已支持空安全,就意味着你可以开始迁移了。否则请使用 Resolvable 列内列举的已迁移至空安全的版本。
这是一个简单的 package 的输入示例。每个 package 的绿色对勾代表着对应版本已支持空安全:
上面的输出说明了所有依赖的 package 都有可使用的已支持空安全的预发布版本。
如果你的 package 的依赖中,有一些 尚未 支持空安全,我们推荐你联系对应依赖的作者。你可以在 pub.flutter-io.cn 对应 package 的页面,找到作者的联系信息。
升级依赖
在迁移你的 package 的代码之前,请将它的依赖项升级至空安全版本。
-
运行命令
dart pub upgrade --null-safety
将依赖升级至支持空安全的最新版本。 注意: 该命令会更改你的pubspec.yaml
文件。 -
运行命令
dart pub get
。
2. 迁移
你的代码里大部分需要更改的代码,都是可以轻易推导的。例如,如果一个变量可以为空,它的类型需要 ?
后缀。一个不可以为空的命名参数,需要使用 required
标记,或者给定其一个 默认值。
针对迁移,你有两个选项可以选择:
-
使用迁移工具,它可以帮你处理大多数可推导的变更。
使用迁移工具
迁移工具会带上一个非空安全的 package ,将它转换至空安全。你可以先在代码中添加 提示标记 来引导迁移工具的转换。
开始转换前,请做好如下的准备:
-
使用 Dart SDK 的 2.19.6 版本。
-
运行
dart pub outdated --mode=null-safety
以确保所有依赖为最新且空安全。
在包含 pubspec.yaml
的目录下,执行 dart migrate
命令,启动迁移工具。
$ dart migrate
如果你的 package 可以进行迁移,工具会输出类似以下的内容:
View the migration suggestions by visiting:
http://127.0.0.1:60278/Users/you/project/mypkg.console-simple?authToken=Xfz0jvpyeMI%3D
使用 Chrome 浏览器访问 URL,你可以看到一个交互式的界面,引导你进行迁移:
你可以在工具中看到其推断的所有变量和类型注解。例如,在上面的截图中,工具推断第一行的 ints
列表元素可能为空,所以应该变为 int?
(先前为 int
)。
理解迁移的结果
若要了解每个变化(或者未变化)的原因,点击 Proposed Edits 窗口中的行数,原因会出现在 Edit Details 窗口中。
举个例子,假设我们有如下的非空安全的代码:
var ints = const <int>[0, null];
var zero = ints[0];
var one = zero + 1;
var zeroOne = <int>[zero, one];
当这些代码处在函数外时,默认的迁移改动是向后兼容的,但并不理想(在函数内时会稍有不同):
var ints = const <int?>[0, null];
var zero = ints[0];
var one = zero! + 1;
var zeroOne = <int?>[zero, one];
点击 line 3 链接,你可以看到迁移工具添加 !
的原因。而因为你知道 zero
不会为空,所以你可以改进迁移结果。
改进迁移的结果
当分析结果推导了错误的可空性时,你可以添加临时的提示标记来改变建议的编辑:
-
在迁移工具的 Edit Details 窗格中,你可以通过 Add
/*?*/
hint 和 Add/*!*/
hint 按钮来添加提示标记。按下这些按钮,相应的标记会立刻添加到代码中,并且 无法撤销。如果你想删除标记,可以和平常一样使用代码编辑器删除它。
-
就算迁移工具正在运行,你也可以使用编辑器添加提示标记。由于你的代码还未迁移到空安全,所以无法使用空安全的新特性。但是你可以进行与空安全无关的改动,例如重构。
当你完成编辑后,点击 Rerun from sources 进行更改。
下方的表格展示了可以使用的提示标记。
|
|
---|---|
expression /*!*/ |
|
type /*!*/ |
|
/*?*/ |
|
/*late*/ |
|
/*late final*/ |
|
/*required*/ |
|
一个提示也可能产生蝴蝶效应,影响其他的代码。在先前的例子中,如果在 zero
被赋值的位置(第二行)添加一个 /*!*/
标记,迁移工具就会推断 zero
的类型是 int
而非 int?
。这就会影响到直接或间接使用了 zero
的代码。
var zero = ints[0]/*!*/;
通过添加了以上的提示,迁移工具将调整建议的更改,如下面的代码所示。第三行的 zero
后面不再有 !
,第四行的 zeroOne
也被推断为 int
列表而不是 int?
。
|
|
---|---|
|
|
只迁移部分文件
尽管我们希望你能一次性完成迁移工作,但对于大体量的应用或 package 而言并不是简单的事。如果你想只迁移部分文件,请将暂时不迁移的文件前方的绿色勾选框取消勾选。稍后应用迁移更改时,这些文件会加上 Dart 2.9 版本注释,其他内容保持不变。
更多有关渐进迁移空安全的内容,请阅读 非健全的空安全。
请注意,从 Dart 3 开始,只支持完全迁移到空安全的应用和 package。
应用更改
当你觉得迁移工具提示的更改部分可以应用了,点击 Apply migration。迁移工具会删除所有的提示标记,保存迁移后的代码。同时,迁移工具也会更改 pubspec 的 SDK 限制,将 package 迁移至空安全。
下一步就是对代码进行 静态分析。如果一切正常,下一步就是 测试你的代码。最后,如果你已经将 package 发布至 pub.flutter-io.cn, 发布空安全的预览版本。
手动迁移
如果你不想使用迁移工具,你也可以手动进行迁移。
我们推荐你 优先迁移最下层的库 —— 指的是没有导入其他 package 的库。接着迁移直接依赖了下层库的依赖库。最后再迁移依赖项最多的库。
举个例子,假设你的 lib/src/util.dart
导入了其他(空安全)的 package 和核心库,但它没有包含任何 import '<本地路径>'
的引用。那么你应当优先考虑迁移 util.dart
,然后迁移依赖了 util.dart
的文件。如果有一些循环引用的库(例如 A 引用了 B,B 引用了 C,C 引用了 A),建议同时对它们进行迁移。
手动对 package 进行迁移时,请参考以下步骤:
-
编辑 package 的
pubspec.yaml
文件,将最低 SDK 版本设置到至少为2.12.0
:environment: sdk: '>=2.12.0 <3.0.0'
-
重新生成 package 的配置文件:
$ dart pub get
在版本最低是
2.12.0
的 SDK 上运行dart pub get
时,会将每个 package 的默认 SDK 最低版本设定为 2.12,并且默认它们已经迁移至空安全。 -
在你的 IDE 上打开package 。
你也许会看到很多错误,没关系,让我们继续。 -
利用分析器来辨析静态错误,逐个迁移 Dart 文件。
按需添加?
、!
、required
以及late
来消除静态错误。
想获得更多手动迁移的帮助,请前往 非健全的空安全。
3. 分析
更新你的 package(在 IDE 或命令行工具中使用 dart pub get
)后在 IDE 或命令行工具中对你的代码进行 静态分析:
$ dart pub get
$ dart analyze # or `flutter analyze`
4. 测试
如果你的代码通过了分析,接下来可以开始测试:
$ dart test # or `flutter test`
你可能需要更新使用了空值作为预期用例的测试代码。
如果你需要对代码作出大量的更改,那么你可能需要重新对代码进行迁移。这时请先回滚代码更改,再运行迁移工具进行迁移。
5. 发布
我们希望你完成迁移后尽快将其发布,可以作为预览版:
-
发布 package。如果你不想发布稳定版本,你可以 发布为预发布版本。
更新 package 的版本
Package 的版本
你可以修改版本以表示该版本包含了破坏性的改动:
-
如果你的 package 版本已经大于或等于
1.0.0
,请提升主版本。例如,上一个版本为2.3.2
,那么新版本应该为3.0.0
。 -
如果你的 package 的版本还未高于
1.0.0
,你可以 提升次版本,也可以 提升至1.0.0
。例如,上一个版本为0.3.2
,那么新版本可以是0.4.0
或1.0.0
。
检查你的 pubspec
在你发布稳定版本的空安全 package 前,我们强烈建议你遵循以下 pubspec 的规则:
-
将 SDK 的最低限制设置为你测试过的最低稳定版本(至少是
2.12.0
)。 -
所有的直接依赖都使用稳定版本。
欢迎使用空安全
If you made it this far, you should have a fully migrated, null-safe Dart package.
If all of the packages you depend on are migrated too, then your program is sound with respect to null-reference errors. You should see output like this when running or compiling your code:
Compiling with sound null safety
如果你走到了这一步,你应该已经完全将你的 Dart package 迁移至空安全了。当你的所有依赖也都完成了迁移,那么你的程序就是健全的,同时可以正确处理空引用的错误。
谨代表 Dart 团队的所有成员,感谢你 迁移你的代码。