關於程式碼生成
程式碼生成是使用工具為我們生成程式碼的想法。 在 Dart 中,它的缺點是需要額外的步驟來“編譯”應用程式。 儘管這個問題可能在不久的將來得到解決, 但 Dart 團隊正在研究這個問題的潛在解決方案。
在 Riverpod 的上下文中,程式碼生成是稍微改變定義 "provider" 的語法。例如,代替:
final fetchUserProvider = FutureProvider.autoDispose.family<User, int>((ref, userId) async {
final json = await http.get('api/user/$userId');
return User.fromJson(json);
});
使用程式碼生成,我們可以編寫:
Future<User> fetchUser(Ref ref, {required int userId}) async {
final json = await http.get('api/user/$userId');
return User.fromJson(json);
}
使用 Riverpod 時,程式碼生成是完全可選的。 沒有的話完全可以使用 Riverpod。 同時,Riverpod 支援程式碼生成並推薦使用它。
有關如何安裝和使用 Riverpod 程式碼生成器的資訊, 請參閱入門指南頁面。 確保在文件的側欄中啟用程式碼生成。
我應該使用程式碼生成嗎?
Riverpod 中的程式碼生成是可選的。 考慮到這一點,您可能會想是否應該使用它。
答案是:很可能是的。
使用程式碼生成是使用 Riverpod 的推薦方式。
這是一種更面向未來的方法,可以讓您充分發揮 Riverpod 的潛力。
與此同時,許多應用程式已經使用
Freezed 或 json_serializable
等包來生成程式碼。在這種情況下,您的專案可能已經設定為程式碼生成,
並且使用 Riverpod 應該很簡單。
目前,程式碼生成是可選的,因為許多人不喜歡 build_runner
。
但是,一旦 Dart 中提供了靜態超程式設計,
build_runner
將不再是問題。
屆時,程式碼生成語法將是 Riverpod 中唯一可用的語法。
如果使用 build_runner
對您來說是一個破壞性的事情,
那麼只有那時您才應該考慮不使用程式碼生成。
但請記住,您將錯過一些功能,並且將來您將不得不遷移到程式碼生成。
儘管如此,當這種情況發生時,
Riverpod 將提供一個遷移工具,以使過渡儘可能順利。
使用程式碼生成有什麼好處?
您可能想知道:“如果 Riverpod 中程式碼生成是可選的,為什麼要使用它?”
這和其他包的目的一樣:讓您的生活更輕鬆。這包括但不限於:
- 更好的語法,更具可讀性/靈活性,並且學習曲線更短。
- 無需擔心提供者程式的型別。寫下您的邏輯,Riverpod 將為您選擇最合適的提供者程式。
- 語法看起來不再像我們定義了“骯髒的全域性變數”。相反,我們定義了一個自定義函式/類。
- 向提供者程式傳遞引數現在不受限制。
您現在可以傳遞任何引數,而不是僅限於使用
.family
並傳遞單個位置引數。 這包括命名引數、可選引數,甚至預設值。
- 用 Riverpod 編寫的程式碼的有狀態熱過載。
- 透過生成偵錯程式隨後拾取的額外元資料來更好地進行除錯。
- 某些 Riverpod 功能僅在程式碼生成時可用。
語法
定義提供者程式:
使用程式碼生成定義提供者程式時,記住以下幾點會很有幫助:
- 提供者程式可以定義為帶註釋的函式或
帶註釋的類。它們幾乎相同,
但基於類的提供者程式的優點是包含公共方法,使
外部物件能夠修改提供者程式的狀態(副作用)。
函式提供者程式是用於編寫基於類的提供者程式的語法糖,只有
build
方法, 因此不能由 UI 修改。 - 支援所有 Dart 非同步原語(Future、FutureOr 和 Stream)。
- 當函式被標記為async時, 提供者程式會自動處理錯誤/載入狀態並公開 AsyncValue。
函式式的 (不能使用公共方法執行副作用) | 基於類的 (可以使用公共方法執行副作用) | |
---|---|---|
同步的 |
|
|
非同步的 - Future |
|
|
非同步的 - Stream |
|
|
啟用/停用自動處置 autoDispose:
使用程式碼生成時,提供者程式預設為 autoDispose。
這意味著當沒有監聽器附加到它們(ref.watch/ref.listen)時,它們會自動處理掉自己。
此預設設定更符合 Riverpod 的理念。
最初沒有使用程式碼生成變體時,預設情況下 autoDispose 處於關閉狀態,
以適應從 package:provider
遷移的使用者。
如果您想停用 autoDispose,可以透過將 keepAlive: true
傳遞給註釋來實現。
// AutoDispose provider (keepAlive is false by default)
String example1(Ref ref) => 'foo';
// Non autoDispose provider
(keepAlive: true)
String example2(Ref ref) => 'foo';
將引數傳遞給提供者程式(family):
使用程式碼生成時,我們不再需要依賴 family
修飾符將引數傳遞給提供者程式。
相反,我們的提供者程式的主函式可以接受任意數量的引數,包括命名、可選或預設值。
但請注意,這些引數應該仍然具有 == 的一致性。
這意味著要麼應該快取值,要麼應該覆蓋 == 引數。
函式式的 | 基於類的 |
---|---|
|
|
從非程式碼生成變體遷移:
使用非程式碼生成變體時,需要手動確定提供者程式的型別。 以下是轉換為程式碼生成變體的相應選項:
Provider | |
之前 |
|
之後 |
|
NotifierProvider | |
之前 |
|
之後 |
|
FutureProvider | |
之前 |
|
之後 |
|
StreamProvider | |
之前 |
|
之後 |
|
AsyncNotifierProvider | |
之前 |
|
之後 |
|
StreamNotifierProvider | |
之前 |
|
之後 |
|