原文地址: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(免費版)翻譯