非健全的空安全
A Dart program may contain some libraries that are null safe and some that aren’t. These mixed-version programs rely on unsound null safety.
一個 Dart 程式可以同時包含已經是 空安全 和未遷移至空安全的函式庫。這些 混合模式的程式 會執行在 非健全的空安全 下。
混合模式版本 的空安全,讓軟體套件的維護者可以遷移至空安全的同時,未遷移至空安全的使用者也可以享受新的問題修復和其他改進。然而,混合模式的程式無法擁有空安全帶來的所有優勢。
本文將描述健全和非健全的空安全之間的區別,讓你可以為何時進行空安全遷移下定論。
健全和非健全的空安全
Dart 透過一系列的靜態和執行時期檢查來提供健全的空安全。每一個使用了空安全的 Dart 庫都會擁有所有的 靜態 檢查和更嚴格的編譯期的錯誤提醒。對於包含了空安全庫的混合模式程式也是如此。程式碼一旦開始遷移,已遷移的部分就能立刻享有到它帶來的好處。
然而,混合模式的程式無法獲得與空安全的程式的 執行時 健全性一致的保證。
null
很可能從非空安全的函式庫汙染到空安全的程式碼,因為一旦它被阻止,就會對現有的程式碼行為造成破壞。
為了在保持與傳統庫執行時的相容性的同時,能為健全的空安全程式提供健全性, Dart 工具提供了以下兩種模式的支援:
-
以 非健全的空安全 執行的混合模式的程式。在執行時有可能出現
null
參考錯誤,但這只是因為一些null
值和可空型別在非空安全的函式庫中汙染了空安全的程式碼。 -
當程式完全遷移至空安全,且它所依賴的函式庫 全部 都遷移完成後,它就在以 健全的空安全 執行,擁有所有由健全性帶來的保證和編譯最佳化。
健全的空安全唾手可得。當你的程式入口的函式庫已經遷移至空安全,Dart 會自動以健全的空安全執行你的程式碼。如果你匯入了非空安全的函式庫,會有一條提示告訴你,你的程式只能以非健全的空安全執行。
逐步遷移
因為 Dart 支援混合模式的空安全,所以你可以一個個遷移你的函式庫(通常是一個檔案),同時能正常執行程式和測試。
我們推薦你 優先遷移最下層的函式庫——指的是沒有匯入其他套件的庫。接著遷移直接依賴了下層庫的依賴庫。最後再遷移依賴項最多的函式庫。
舉個例子,假設你的 lib/src/util.dart
匯入了其他(空安全)的軟體包和核心庫,但它沒有包含任何 import '<本地路徑>'
的參考。那麼你應當優先考慮遷移 util.dart
,然後遷移依賴了 util.dart
的檔案。如果有一些迴圈參考的函式庫(例如 A 參考了 B,B 參考了 C,C 參考了 A),建議同時對它們進行遷移。
使用遷移工具
你可以使用 遷移工具 進行漸進遷移。如果你需要排除部分檔案或資料夾,勾選綠色的勾選框。下方的截圖中,bin
資料夾的所有檔案都已被排除。
每個不遷移的檔案都會加上 2.9 語言版本的註釋。你可以之後再次執行 dart migrate
繼續遷移。已遷移的檔案將顯示為禁用的勾選框,它們無法撤銷遷移更改。
手動進行遷移
手動對 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 檔案頂部加上 語言版本註釋。
// @dart=2.9
在 2.12 的 package 中為庫指定 2.9 的語言版本可以減少一些未遷移的分析錯誤。然而,非健全的空安全減少了分析器中可用的資訊。例如,就算 2.9 版本的檔案中一個引數可能會傳入空值,分析器也可能會假定引數型別不為空,
-
利用分析器來辨析靜態錯誤,逐個遷移 Dart 檔案。
按需新增?
、!
、required
以及late
來消除靜態錯誤。
測試或執行混合版本的程式
想要測試或執行混合版本的程式碼,你需要禁用健全的空安全。有兩種方式可以進行操作:
-
在
dart
和flutter
命令裡,加入--no-sound-null-safety
標記禁用。例如:$ dart --no-sound-null-safety run $ flutter run --no-sound-null-safety
-
或者,設定包含
main()
函式的檔案程式入口的語言版本為 2.9。在 Flutter 應用中,一般是lib/main.dart
。在命令列應用中,一般是bin/<package 名稱>.dart
。同時你也可以設定test
下的檔案,因為它們也包含程式入口。例如:// @dart=2.9 import 'src/my_app.dart'; void main() { //... }
以上兩種方式的規避,對於 正在 增量遷移的過程非常有用,但這樣做意味著你並未在完全啟用空安全的情況下測試你的程式碼。當你完成增量遷移後,也請記得將測試程式碼 重新 遷移至空安全。