原文地址:medium.com/flutter-com…git
原文做者:medium.com/@jcocaramosgithub
发布时间:2018年10月31日 - 7分钟阅读shell
在"【第1部分】Dart中的代码生成:基础知识 "中,咱们介绍了代码生成背后的动机是什么,并列出了Dart中最重要的工具,让计算机为咱们作艰苦的工做。在这篇文章中,咱们将介绍如何建立和使用Dart注解,以及如何使用source_gen和build_runner开始处理这些注解。bash
注解是一种表示语法元数据的形式,它能够被添加到咱们的Dart代码中;换句话说,是一种向咱们的代码中的任何组件(如类或方法)添加额外信息的方式。注解在咱们的Dart代码中随处可见:咱们使用@required
来指定一个命名的参数不是可选的,因此若是被注解的字段不存在,咱们的代码就不会被编译,或者咱们使用@override
来识别那些由父类给出的API在子类中实现。咱们怎么知道 它们是注解呢?嗯,它们很容易找到,由于它们的前缀是@。app
尽管在咱们的类中拥有 "元数据 "的想法听起来很是异国情调和复杂,但事实上,注解是Dart中最简单的事情之一。在上面的段落中,我提到注解只是携带额外的信息。它们就像数据类同样。PODO...(Plain Old Dart Objects)。任何类均可以转化为一个注解,只要他们提供一个常量
构造函数。框架
class Todo {
final String name; final String todoUrl; final
final String todoUrl.Const Todo(this.name, {this.todoUrl} : assert(name !
const Todo(this.name, {this.todoUrl}) : assert(name != null);
}
@Todo('hello first annotation', todoUrl: 'https://www.google.com')
class HelloAnnotations {}
复制代码
正如你所看到的,注解是很是简单的。重要的是咱们如何使用这些注解;注解所包含的信息以及咱们如何使用这些信息才是它们的特别之处。而这正是source_gen
和build_runner
会帮助咱们的地方。async
build_runner
?build_runner是一个Dart包,它将帮助咱们使用Dart代码生成文件。咱们将经过build.yaml
来配置Builder
文件;一旦配置好了,一旦触发了build,或者文件发生了变化,咱们就会收到更新,咱们就能够解析那些发生了变化或者符合某个标准的代码。ide
在某种程度上,你能够把 build_runner
当作是回答何时须要生成代码的机制,而 source_gen
则回答了须要生成什么代码的问题。source_gen
提供了一个框架来构建 build_runner
期待的 Builders,同时暴露了一个友好的 API 来解析和生成代码。函数
在文章的其他部分,咱们将在一个名为todo_reporter.dart的宠物项目上工做,你能够在这个连接中找到它。工具
这是一个非书面规则,你能够在全部使用代码生成的项目中找到:你将为你的注解建立一个包,并为生成器建立一个不一样的包,为这些增长价值。在Dart/Flutter中建立一个库包所须要的全部信息均可以在这个连接中找到。
所以,咱们要作的是建立一个文件夹,我将命名为todo_reporter.dart
。在这个文件夹中,我将添加个人todo_reporter
,将包含注解,todo_reporter_generator
处理代码,最后是一个example
包,以演示个人库的功能。
我之因此把根文件夹后缀为.dart
,是为了清晰明了;虽然这不是强制性的,但我喜欢遵循这一点,以明确这个包能够在任何Dart项目中使用。相反,若是我想把这个包只标记为Flutter,好比ozzie.flutter,那么我会使用不一样的后缀。这不是必需要作的,只是我喜欢遵循的一个命名惯例。
todo_reporter
,咱们的注解包,也是最简单的一个包。咱们将在todo_reporter.dart
中建立咱们的todo_reporter
,添加pubspec.yaml
和lib
文件夹。pubspec很是简单。
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
复制代码
除了测试包以外,并无真正的依赖关系,只是用于开发目的。
在lib
文件夹中,咱们将作如下工做。
咱们将建立一个todo_reporter.dart
,而后咱们将在那里注册全部的类,这些类使用export
来暴露咱们包的公共API。 这是一个很好的作法,由于咱们包中的任何公共类均可以经过import "package:todo_reporter/todo_reporter.dart"
来导入。你能够在这里看到这个类的样子:github.com/jorgecoca/t… 。
在lib
文件夹内,咱们如今要建立一个src
文件夹,它将包含全部的代码,公共的或非公共的。
在咱们的例子中,咱们惟一须要包含的就是注释。让咱们在里面建立一个包含这些内容的todo.dart
文件。
class Todo {
final String name; final String todoUrl; final
final String todoUrl.Const Todo(this.name, {this.todoUrl} : assert(name !
const Todo(this.name, {this.todoUrl}) : assert(name != null);
}
复制代码
好了,这就是咱们须要的全部注释。我说过这很简单,对吧?好吧,咱们尚未完成。让咱们在测试包中添加一些单元测试。
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
的包。在它里面,你应该找到一个pubspec.yaml
,一个build.yaml
文件,一个lib
文件夹,在lib
文件夹里面,有一个src
文件夹和一个todo_reporter_generator.dart
文件,咱们将在其中包含咱们的export
语句。咱们的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
。这个文件将包含你的Builders
所需的配置。你能够在这里找到更多信息:github.com/dart-lang/b…
咱们的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
文件,在src
里面让咱们添加一个名为todo_reporter_generator.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');
复制代码
build.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!";
}
}
复制代码
todo_reporter_generator.dart
咱们能够看到,在builder.dart
中,咱们有一个todoReporter
方法,它将建立一个Builder
;这个Builder
是经过使用一个SharedPartBuilder
来提供的,这个SharedPartBuilder
接收了咱们的TodoReporterGenerator
。这就是build_runner
和source_gen
如何一块儿工做。
咱们的TodoReporterGenerator
是GeneratorForAnnotation
类型的;也就是说,它只有在找到一段被给定注释的代码时才会执行generateForAnnotatedElement
,在咱们的例子中就是Todo
。
generateForAnnotatedElement
的返回值是一个String值,将包含咱们生成的代码;若是生成的代码没有编译,咱们的构建阶段就会失败,这在避免bug时是很是整洁的。
在咱们的todo_repoter_generator
项目中使用这些文件,每次当尝试自动生成代码时,它将建立一个带有注释的part
文件,写着 // Hey! Annotation found!
. 咱们将在下一篇文章中学习如何处理注释😉。
开始使用咱们的todo_repoter.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
,内容以下。
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'todo.dart';
// *****************************************************************
// TodoReporterGenerator
// ********************************************************************
// Hey! Annotation found!
复制代码
成功了! 咱们已经完成了咱们的任务!如今咱们能够为每个在咱们的代码中找到的Todo
注释生成一个有效的Dart
文件。如今咱们能够为代码中发现的每个Todo注释生成一个有效的Dart文件。试试吧,你能够自由地建立你想要的任何数量的注释。
如今咱们已经有了生成文件的正确设置,在下一篇文章中,咱们将学习如何利用咱们的注释,让咱们的生成代码可以真正作一些很酷的事情,毕竟咱们如今生成的代码没有任何目的。
你能够关注我 twitter.com/jcocaramos ,也能够在个人公共Github上看到更多的代码 github.com/jorgecoca 。
经过www.DeepL.com/Translator(免费版)翻译