Flutter Dart Macro 宏简化 JSON 序列化
视频
https://www.bilibili.com/video/BV1tT421a7ev/
前言
原文 https://ducafecat.com/blog/using-dart-macros-to-simplify-json-serialization
今天我们将会体验 dart 语言新特性 macro 宏,来实现对 json 的序列化,用到的包是官方实验室写的 json 包。
本文将会一步步的带你实现这个功能,那我们开始吧。
Dart, 宏, JSON 序列化, 代码生成, 效率优化, 语言特性
参考
https://dart.dev/language/macros#view-the-generated-code
https://github.com/dart-lang/language/blob/main/working/macros/feature-specification.md
macro 宏
好处
- 直接语言集成。由于宏直接内置于语言中,它们比外部库提供更多的一致性并兼容语言的本机功能。
- 减少外部依赖。通过使用宏来减少对外部库的依赖,您可以减小应用程序的大小并简化依赖管理。
- 编译时代码生成。与运行时生成代码的解决方案相比,宏在编译时生成代码可能会提供更快的结果。
- 增强定制化和自由度。开发者可以借助宏更好地根据项目独特需求定制代码生成,提供大量的定制自由度。
json_serializable 比较
为了在 Dart 中生成 JSON 序列化代码,这个库经常被使用,但它需要更多的依赖和设置。无需额外的工具,宏可以在源代码中自动生成 fromJson 和 toJson 方法。
freezed 比较
使用这个库,创建类型安全、不可变的 Dart 类变得非常简单。虽然 freezed 功能强大,但宏可能以更少的依赖和更直接的集成来完成类似的任务。
实现步骤
第一步:切到 dart 3.5 版本
https://docs.flutter.dev/release/archive?tab=macos
下载 beta channel , dart 3.5 版本。
我本机用的 fvm 管理多版本。
检查环境
❯ flutter --version
Flutter 3.23.0-0.1.pre • channel beta • https://github.com/flutter/flutter.git
Framework • revision 2feea7a407 (13 天前) • 2024-06-06 10:19:10 +0700
Engine • revision bb10c54666
Tools • Dart 3.5.0 (build 3.5.0-180.3.beta) • DevTools 2.36.0
第二步:配置 pubspec.yaml
pubspec.yaml
environment:
sdk: ">=3.5.0-180.3.beta <4.0.0"
dependencies:
json: ^0.20.2
官方实验室 json 包 https://pub.dev/packages/json
analysis_options.yaml
analyzer:
enable-experiment:
- macros
在编写代码时,除非你告诉分析器你正在试验此功能,否则它会发出警告。
第三步:编写测试代码
编写官方示例代码
lib/macros/user.dart
import 'package:json/json.dart';
@JsonCodable() // Macro annotation.
class User {
final int? age;
final String name;
final String username;
}
可以点击 Go to Augmentation
查看详细
augment library 'package:flutter_application_macro/macros/user.dart';
import 'dart:core' as prefix0;
augment class User {
external User.fromJson(prefix0.Map<prefix0.String, prefix0.Object?> json);
external prefix0.Map<prefix0.String, prefix0.Object?> toJson();
augment User.fromJson(prefix0.Map<prefix0.String, prefix0.Object?> json, )
: this.age = json[r'age'] as prefix0.int?,
this.name = json[r'name'] as prefix0.String,
this.username = json[r'username'] as prefix0.String;
augment prefix0.Map<prefix0.String, prefix0.Object?> toJson() {
final json = <prefix0.String, prefix0.Object?>{};
if (this.age != null) {
json[r'age'] = this.age!;
}
json[r'name'] = this.name;
json[r'username'] = this.username;
return json;
}
}
这段代码自动的,不需要你编写。
启动时加入测试 lib/main.dart
class _MyHomePageState extends State<MyHomePage> {
@override
void initState() {
super.initState();
// Given some arbitrary JSON:
final userJson = {
'age': 5,
'name': 'Roger',
'username': 'roger1337',
};
// Use the generated members:
final user = User.fromJson(userJson);
print(user);
print(user.toJson());
}
...
如果代码成功运行将会打印 json 字符串
最后:运行测试
加入参数 --enable-experiment=macros
执行
我是 vsc 所以加在了 .vscode/launch.json
"configurations": [
{
"name": "flutter_application_macro",
"request": "launch",
"type": "dart",
"flutterMode": "debug",
"program": "lib/main.dart",
"args": ["--enable-experiment=macros"]
},
主要是在 args 中加入
--enable-experiment=macros
参数
最后我们启动,查看输出信息。
Launching lib/main.dart on iPhone 15 in debug mode...
Xcode build done. 11.3s
Connecting to VM Service at ws://127.0.0.1:55696/aP-7aqbeIA4=/ws
Connected to the VM Service.
flutter: Instance of 'User'
flutter: {age: 5, name: Roger, username: roger1337}
官方 json 实现
代码
https://github.com/dart-lang/sdk/blob/main/pkg/json/lib/json.dart
macro class JsonCodable
with _Shared, _FromJson, _ToJson
implements ClassDeclarationsMacro, ClassDefinitionMacro {
const JsonCodable();
/// Declares the `fromJson` constructor and `toJson` method, but does not
/// implement them.
@override
Future<void> buildDeclarationsForClass(
ClassDeclaration clazz, MemberDeclarationBuilder builder) async {
final mapStringObject = await _setup(clazz, builder);
await (
_declareFromJson(clazz, builder, mapStringObject),
_declareToJson(clazz, builder, mapStringObject),
).wait;
}
/// Provides the actual definitions of the `fromJson` constructor and `toJson`
/// method, which were declared in the previous phase.
@override
Future<void> buildDefinitionForClass(
ClassDeclaration clazz, TypeDefinitionBuilder builder) async {
final introspectionData =
await _SharedIntrospectionData.build(builder, clazz);
await (
_buildFromJson(clazz, builder, introspectionData),
_buildToJson(clazz, builder, introspectionData),
).wait;
}
}
buildDeclarationsForClass
方法:
- 在类声明阶段,为该类生成
fromJson
构造函数和toJson
方法的声明。buildDefinitionForClass
方法:
- 在类定义阶段,为
fromJson
和toJson
提供具体的实现逻辑。- 该实现逻辑是根据类的属性信息自动生成的,通过反射获取类的结构信息。
代码
https://github.com/ducafecat/flutter_develop_tips/tree/main/flutter_application_macro_json
小结
这个 macro 实现的 json 目前也是试验产品,不建议你在项目中使用,本文的目的只是让你体验下宏的优势,和未来代码编写的一个趋势方向。
感谢阅读本文
如果有什么建议,请在评论中让我知道。我很乐意改进。
flutter 学习路径
- Flutter 优秀插件推荐 https://flutter.ducafecat.com
- Flutter 基础篇1 - Dart 语言学习 https://ducafecat.com/course/dart-learn
- Flutter 基础篇2 - 快速上手 https://ducafecat.com/course/flutter-quickstart-learn
- Flutter 实战1 - Getx Woo 电商APP https://ducafecat.com/course/flutter-woo
- Flutter 实战2 - 上架指南 Apple Store、Google Play https://ducafecat.com/course/flutter-upload-apple-google
- Flutter 基础篇3 - 仿微信朋友圈 https://ducafecat.com/course/flutter-wechat
- Flutter 实战3 - 腾讯即时通讯 第一篇 https://ducafecat.com/course/flutter-tim
- Flutter 实战4 - 腾讯即时通讯 第二篇 https://ducafecat.com/course/flutter-tim-s2
© 猫哥 ducafecat.com
end