目錄

巨集 (實驗性功能)

Dart 巨集系統是一個重要的全新語言功能,目前正在開發中,它為 Dart 語言增加了對靜態元程式設計的支援。

Dart 巨集是一段使用者可定義的程式碼,它將其他程式碼作為參數,並即時對其進行操作,以建立、修改或新增宣告。

您可以將巨集系統分為兩個部分:使用巨集和撰寫巨集。本頁涵蓋了每個部分 (在高層次上,因為該功能仍處於預覽階段),如下列各節所示

  • JsonCodable 巨集:一個現成的巨集,您今天可以試用 (在實驗性旗標後面),它為 Dart 中常見的繁瑣 JSON 序列化和反序列化問題提供了一個無縫的解決方案。

  • 一般的巨集功能:我們為何要將巨集新增到 Dart 中、激發使用案例、優於現有程式碼產生解決方案的優點,以及對未來功能完成後如何撰寫巨集的粗略概述。

JsonCodable 巨集

#

JsonCodable 巨集將使用者定義的 Dart 類別編碼和解碼為 Map<String, Object?> 類型的 JSON 對應。它會產生兩個成員,一個 toJson 序列化方法和一個 fromJson 反序列化建構函式。

設定實驗

#
  1. 切換至 Dart dev 通道Flutter master 通道

  2. 執行 dart --version 並確保您的 Dart 版本為 3.5.0-152 或更高版本。

  3. 編輯您的 pubspec 中的 SDK 約束,以要求 Dart 版本:sdk: ^3.5.0-152

  4. 將套件 json 新增至 dependenciesdart pub add json

  5. 在您的套件的 analysis_options.yaml 檔案中啟用實驗。檔案位於專案的根目錄

    yaml
    analyzer:
     enable-experiment:
       - macros
  6. 在您計劃使用的檔案中匯入套件

    dart
    import 'package:json/json.dart';
  7. 使用實驗旗標執行您的專案

    dart run --enable-experiment=macros bin/my_app.dart

使用巨集

#

若要使用 JsonCodable 巨集,請將註解附加到您想要序列化的類別

dart
import 'package:json/json.dart';

@JsonCodable() // Macro annotation.
class User {
 final int? age;
 final String name;
 final String username;
}

該巨集會檢視 User 類別,並使用 User 類別的欄位推導出 fromJsontoJson 的實作。

因此,無需自行定義它們,toJsonfromJson 現在可以在已註解類別的物件上使用

dart
void main() {
 // Given some arbitrary JSON:
 var userJson = {
   'age': 5,
   'name': 'Roger',
   'username': 'roger1337'
 };

 // Use the generated members:
 var user = User.fromJson(userJson);
 print(user);
 print(user.toJson());
}

檢視產生的程式碼

#

有時檢視產生的程式碼會很有用,以便更好地了解巨集如何運作,或檢查它提供的詳細資訊。

按一下 IDE 中註解下出現的「前往增強」連結 (VSCode 中支援),以查看巨集如何產生 toJsonfromJson

如果您變更已註解類別中的任何內容,您可以觀看產生的增強功能與您的應用程式程式碼一起即時調整

A side-by-side gif of the generated augmentation updating as the code it's augmenting is updated

觸發自訂診斷

#

JsonCodable 巨集具有內建診斷功能,其發出的方式與語言本身的診斷訊息相同。例如,如果您嘗試在應用巨集的位置手動宣告 toJson 方法,則分析器會發出錯誤

dart
@JsonCodable()
class HasToJson {
 void toJson() {}
 // Error: Cannot generate a toJson method due to this existing one.
}

您可以搜尋 JsonCodable 的定義中的「DiagnosticMessage」,以了解巨集會擲回的其他錯誤。例如,擴展一個不可序列化的類別,或者如果欄位名稱與給定 JSON 中的金鑰名稱不完全匹配。

巨集語言功能

#

Dart 巨集是一種靜態元程式設計或程式碼產生解決方案。與執行階段程式碼產生解決方案 (如 build_runner) 不同,巨集完全整合到 Dart 語言中,並由 Dart 工具在背景自動執行。這使得巨集比依賴次要工具有效率得多

  • 無需額外執行任何操作;巨集會在您撰寫程式碼時即時建置。
  • 沒有重複的工作或不斷重新編譯而影響效能;所有建置和程式碼產生都會直接在編譯器中自動發生。
  • 不會寫入磁碟,因此沒有部分檔案或指向產生的參考;巨集會直接增強現有類別。
  • 沒有令人困惑/混淆的測試;自訂診斷會像分析器中的任何其他訊息一樣,直接在 IDE 中發出。

而且比手動撰寫這些類型問題的解決方案更有效率,也更不容易出錯。

使用案例

#

巨集提供了可重複使用的機制來解決以繁瑣樣板為特徵的模式,並且通常需要迭代類別的欄位。我們希望將來透過巨集解決的一些常見範例包括

  • Json 序列化。 序列化 JSON 所需的額外工具,例如 json_serializable 套件,其效率不如預期。JsonCodable 巨集提供了一種更簡潔的方式來產生序列化程式碼;今天就試試看

  • 資料類別。 Dart 最多人要求的功能是資料類別,它可以自動為每個欄位提供建構子,以及 ==hashCodecopyWith() 方法的實作。使用巨集實作這個解決方案表示使用者可以根據自己的需求自訂資料類別。

  • 冗長的 Flutter 模式。 一個例子是將複雜的 build 方法分解為較小的 Widget 類別的集合。這對效能更好,也讓程式碼更容易維護。不幸的是,編寫所有這些較小的類別需要大量的樣板程式碼,這讓使用者卻步。巨集可能會提供一種解決方案,它會迭代複雜的 build 方法來產生較小的 Widget 類別,從而大幅提高 Flutter 程式碼的生產力和品質。您可以在 Flutter 團隊的這份提案中查看對這個主題的探索。

巨集如何運作

#

要建立巨集,您可以使用 macro 關鍵字編寫類似於類別的巨集宣告。巨集宣告也必須包含 implements 子句,以定義巨集可應用於哪個介面。

例如,適用於類別並向類別新增宣告的巨集,將實作 ClassDeclarationsMacro 介面。

dart
macro class MyMacro implements ClassDeclarationsMacro {
   const MyMacro();

   // ...
}

雖然此功能仍在開發中,您可以在原始程式碼中找到巨集介面的完整清單。

上述範例中的 MyMacro 建構子對應於您用於將巨集套用到宣告的註解。語法與 Dart 現有的中繼資料註解語法相同。

dart
@MyMacro()
class A {}

在巨集宣告的主體內,您可以定義您希望巨集產生的程式碼,以及您希望巨集發出的任何診斷

從非常高的層級來看,編寫巨集基本上是透過使用建構器方法將宣告的屬性與這些屬性的識別符組合在一起。巨集透過對程式進行深入的內省來收集此資訊。

巨集仍在開發中,所以我們目前只能深入探討到這個程度。如果您好奇,或者想在實驗性標記後方親自試試看,最好的指南是查看現有巨集的實作。

  • 查看 JsonCodable 巨集的定義
  • 或者語言儲存庫中提供的任何範例

時程

#

巨集的穩定發布日期目前未知。這是因為其實作的複雜性。

巨集的工作原理是對它們所應用的程式進行深入的內省。巨集最終可能會遍歷程式的遠端部分,以收集宣告中屬性和類型註解的必要資訊。

考慮到它們在大型程式碼庫中的應用,其中多個巨集可以在不同位置不斷地內省和擴增基礎,排序執行階段的設計尤其具有挑戰性,需要仔細考慮。

我們正朝著今年稍晚 (2024 年) 穩定發布 JsonCodable 巨集,以及明年年初 (2025 年) 穩定發布完整語言功能 (即,編寫您自己的巨集) 的目標邁進。