在第一部分中,我们了解了为什么需要代码生成,并列出了Dart中代码生成的必要工具。 在第二部分中,我们将学习如何在Dart中创建和使用注释,以及如何使用source_gen和build_runner开始代码生成。

飞镖注解
注释是可以添加到代码中的语法元数据。 换句话说,这是将附加信息添加到代码的任何组件(例如添加到类或方法)的机会。 注解在Dart代码中被广泛使用:我们使用@required
来表示需要一个命名参数,如果未指定注解参数,我们的代码将不会编译。 我们还使用@override
指示在父类中定义的给定API是在子类中实现的。 注释始终以@
符号开头。
如何创建自己的注释?
尽管将元数据添加到代码中的想法听起来有些奇怪和复杂,但是注释是Dart语言中最简单的事情之一。 以前曾说过, 注释只是携带其他信息 。 它们类似于PODO (普通的旧式Dart对象)。 如果在其中定义了const
构造函数,则任何类都可以用作注释 :
class { final String name; final String todoUrl; const Todo(this.name, {this.todoUrl}) : assert(name != null); } @Todo('hello first annotation', todoUrl: 'https://www.google.com') class HelloAnnotations {}
如您所见,注释非常简单。 重要的是我们将如何使用这些注释。 这将帮助我们source_gen
和build_runner
。
如何使用build_runner?
build_runner
是Dart软件包,它将帮助我们使用Dart代码生成文件。 我们将使用build.yaml
配置Builder
文件。 配置完成后,将在每个build
命令或更改文件时调用Builder
。 我们也有机会解析已修改或符合某些条件的代码。
source_gen用于理解Dart代码
从某种意义上说, build_runner
是一种回答“ 何时需要生成代码?”问题的机制。 同时, source_gen
回答问题“应生成什么代码?”。 source_gen
提供了一个框架,用于创建供build_runner
使用的Builders 。 此外, source_gen
还提供了一个方便的API,用于解析和生成代码。
放在一起:TODO报告
在本文的其余部分,我们将分解todo_reporter.dart项目,该项目可以在此处找到。
有一个不成文的规则,所有使用代码生成的项目都必须遵循:您需要创建一个包含批注的包 ,并为使用这些批注的生成器创建一个单独的包 。 有关如何在Dart / Flutter中创建软件包库的信息,请参见此处 。
首先,您需要创建todo_reporter.dart
目录。 在此目录中,您需要创建todo_reporter
目录,该目录将包含注释, todo_reporter_generator
目录以处理注释,最后是example
目录,其中包含正在创建的库功能的演示。
为了清楚起见, .dart
后缀.dart
添加到根目录名称。 当然,这不是必须的,但是我喜欢遵循此规则以准确表明该程序包可以在任何Dart项目中使用的事实。 相反,如果我想指出此软件包仅适用于Flutter(如ozzie.flutter ),我将使用其他后缀。 这不是必须的,这只是我尝试遵守的命名约定。
创建我们的简单注释包todo_reporter
我们将在todo_reporter.dart
创建todo_reporter.dart
。 为此,请创建pubspec.yaml
文件和lib
目录。
pubspec.yaml
非常简单:
name: todo_reporter description: Keep track of all your TODOs. version: 1.0.0 author: Jorge Coca <jcocaramos@gmail.com> homepage: https://github.com/jorgecoca/todo_reporter.dart environment: sdk: ">=2.0.0 <3.0.0" dependencies: dev_dependencies: test: 1.3.4
除了在开发过程中使用的test
包之外,没有其他依赖项。
在lib
目录中,执行以下操作:
- 您需要创建
todo_reporter.dart
文件,在其中使用export
,将指定所有具有公共API的类。 这是一个好习惯,因为可以使用import 'package:todo_reporter/todo_reporter.dart';
任何类import 'package:todo_reporter/todo_reporter.dart';
。 您可以在这里看到这堂课。 - 在
lib
目录中,我们将创建src
目录,其中包含所有代码-public和non-public。
在我们的例子中,我们需要添加的只是注释。 让我们用注释创建一个todo.dart
文件:
class Todo { final String name; final String todoUrl; const Todo(this.name, {this.todoUrl}) : assert(name != null); }
这样便足以进行注释。 我说过这很简单。 但这还不是全部。 让我们将单元测试添加到test
目录:
todo_test.dart import 'package:test/test.dart'; import 'package:todo_reporter/todo_reporter.dart'; void main() { group('Todo annotation', () { test('must have a non-null name', () { expect(() => Todo(null), throwsA(TypeMatcher<AssertionError>())); }); test('does not need to have a todoUrl', () { final todo = Todo('name'); expect(todo.todoUrl, null); }); test('if it is a given a todoUrl, it will be part of the model', () { final givenUrl = 'http://url.com'; final todo = Todo('name', todoUrl: givenUrl); expect(todo.todoUrl, givenUrl); }); }); }
这就是我们创建注释所需的全部。 您可以在此处找到代码。 现在我们可以去发电机了。
做一个很酷的工作:todo_reporter_generator
现在我们知道了如何创建包,让我们创建todo_reporter_generator
包。 该软件包中应该包含pubspec.yaml
和build.yaml
以及一个lib
目录。 lib
目录必须具有src
目录和builder.dart
文件。 我们的todo_reporter_generator
被视为一个单独的程序包,将作为dev_dependency
添加到其他项目中。 这是因为仅在开发阶段才需要代码生成,并且不需要将其添加到完成的应用程序中。
pubspec.yaml
如下:
name: todo_reporter_generator description: An annotation processor for @Todo annotations. version: 1.0.0 author: Jorge Coca <jcocaramos@gmail.com> homepage: https://github.com/jorgecoca/todo_reporter.dart environment: sdk: ">=2.0.0 <3.0.0" dependencies: build: '>=0.12.0 <2.0.0' source_gen: ^0.9.0 todo_reporter: path: ../todo_reporter/ dev_dependencies: build_test: ^0.10.0 build_runner: '>=0.9.0 <0.11.0' test: ^1.0.0
现在让我们创建build.yaml
。 该文件包含构建器所需的配置。 可以在这里找到更多详细信息。 build.yaml
如下:
targets: $default: builders: todo_reporter_generator|todo_reporter: enabled: true builders: todo_reporter: target: ":todo_reporter_generator" import: "package:todo_reporter_generator/builder.dart" builder_factories: ["todoReporter"] build_extensions: {".dart": [".todo_reporter.g.part"]} auto_apply: dependents build_to: cache applies_builders: ["source_gen|combining_builder"]
import
属性指向包含Builder
的文件, builder_factories
属性指向将生成代码的方法。
现在,我们可以在lib
目录中创建builder.dart
文件:
import 'package:build/build.dart'; import 'package:source_gen/source_gen.dart'; import 'package:todo_reporter_generator/src/todo_reporter_generator.dart'; Builder todoReporter(BuilderOptions options) => SharedPartBuilder([TodoReporterGenerator()], 'todo_reporter');
以及src
目录中的todo_reporter_generator.dart
文件:
import 'dart:async'; import 'package:analyzer/dart/element/element.dart'; import 'package:build/src/builder/build_step.dart'; import 'package:source_gen/source_gen.dart'; import 'package:todo_reporter/todo_reporter.dart'; class TodoReporterGenerator extends GeneratorForAnnotation<Todo> { @override FutureOr<String> generateForAnnotatedElement( Element element, ConstantReader annotation, BuildStep buildStep) { return "// Hey! Annotation found!"; } }
如您所见,在builder.dart
文件中,我们定义了Builder
创建的todoReporter
方法。 使用SharedPartBuilder
创建Builder
,该SharedPartBuilder
Builder
使用我们的TodoReporterGenerator
。 因此build_runner
和source_gen
可以一起工作。
我们的TodoReporterGenerator
是GeneratorForAnnotation
的子类,因此只有在代码中找到此注释(在本例中为@Todo
)时,才会执行generateForAnnotatedElement
方法。
generateForAnnotatedElement
方法返回一个包含我们生成的代码的字符串。 如果生成的代码未编译,则整个构建阶段将失败 。 这非常有用,因为它可以避免将来出现错误。
因此,对于每一代代码,我们的todo_repoter_generator
将创建一个带有注释的part
文件// Hey! Annotation found!
// Hey! Annotation found!
在下一篇文章中,我们将学习如何处理注释。
放在一起:使用todo_reporter
现在,您可以演示todo_reporter.dart
。 在使用软件包时,最好添加一个example
项目。 因此,其他开发人员将能够看到如何在实际项目中使用该API。
让我们创建一个项目,并将所需的依赖项添加到pubspec.yaml
。 在我们的例子中,我们将在example
目录中创建一个Flutter项目并添加依赖项:
dependencies: flutter: sdk: flutter todo_reporter: path: ../todo_reporter/ dev_dependencies: build_runner: 1.0.0 flutter_test: sdk: flutter todo_reporter_generator: path: ../todo_reporter_generator/
收到软件包( flutter packages get
)后,我们可以使用我们的注释:
import 'package:todo_reporter/todo_reporter.dart'; @Todo('Complete implementation of TestClass') class TestClass {}
现在一切就绪,运行我们的生成器:
$ flutter packages pub run build_runner build
命令完成后,您将在我们的项目中注意到一个新文件: todo.g.dart
。 它将包含以下内容:
我们实现了我们想要的! 现在,我们可以为代码中的每个@Todo
注释生成正确的Dart文件。 尝试创建所需数量的对象。
在下一篇文章中
现在我们有了用于生成文件的正确设置。 在下一篇文章中,我们将学习如何使用批注,以便所生成的代码可以做一些非常酷的事情。 毕竟,现在生成的代码没有多大意义。