创建包
— 焉知非鱼Creating packages
Dart 生态系统使用包来共享软件,如库和工具。本页告诉你如何创建一个包,重点是最常见的一种包,库包。
是什么造就了一个库包 #
下图是最简单的库包的布局:
一个库的最低要求是
pubspec 文件
库的 pubspec.yaml
文件和应用程序包的文件是一样的-没有特别的名称来表示这个包是一个库。
lib 目录
正如你所期望的那样,库代码存在于 lib
目录下,对其他包是公开的。你可以根据需要在 lib
下创建任何层次结构。按照惯例,实现代码被放在 lib/src
下。lib/src
下的代码被认为是私有的;其他包不应该需要导入 src/...
。要使 lib/src
下的 API 公开,您可以从直接位于 lib
下的文件导出 lib/src
文件。
注意:当没有指定 library
指令时,会根据每个库的路径和文件名为其生成一个唯一的标签。因此,我们建议您从代码中省略 library
指令,除非您计划生成库级文档。
组织一个库包 #
当你创建小的、单独的库(称为迷你库)时,库包的维护、扩展和测试是最容易的。在大多数情况下,每个类都应该在自己的迷你库中,除非你有两个类是紧密耦合的情况。
注意:你可能听说过 part
指令,它允许你将一个库分割成多个 Dart 文件。我们建议你避免使用 part
指令,而是创建迷你库。
直接在 lib
下创建一个"主"库文件,lib/<package-name>.dart
,导出所有的公共 API。这样用户就可以通过导入一个文件来获得一个库的所有功能。
lib
目录也可能包含其他可导入的、非src的库。例如,也许你的主库可以跨平台使用,但是你创建了单独的库,这些库依赖于 dart:io
或者 dart:html
。有些包有单独的库,这些库是要用前缀导入的,而主库不是。
让我们来看看一个现实世界中的库包的组织: shelf。shelf 包提供了一种使用 Dart 创建 web 服务器的简单方法,它的布局结构是 Dart 库包常用的:
直接在 lib
下,主库文件 shelf.dart
从 lib/src
导出几个文件:
export 'src/cascade.dart';
export 'src/handler.dart';
export 'src/handlers/logger.dart';
export 'src/hijack_exception.dart';
export 'src/middleware.dart';
export 'src/pipeline.dart';
export 'src/request.dart';
export 'src/response.dart';
export 'src/server.dart';
export 'src/server_handler.dart';
shelf 包还包含一个迷你库: shelf_io
。这个适配器处理来自 dart:io
的 HttpRequest
对象。
对网络应用的提示: 为了在使用 dartdevc 开发时获得最佳性能,请将实现文件放在 /lib/src
下,而不是放在 /lib
下的其他地方。同时,避免导入 package:package_name/src/...
的文件。
导入库文件 #
当从其他包中导入一个库文件时,使用 package:
指令来指定该文件的 URI。
import 'package:utilities/utilities.dart';
当从自己的包中导入一个库文件时,当两个文件都在 lib 内,或者两个文件都在 lib 外时,使用相对路径。使用 :package
当导入的文件在 lib 内,而导入者在 lib 外时。
下图显示了如何从 lib 和 web 中导入 lib/foo/a.dart
。
有条件地导入和导出库文件 #
如果你的库支持多个平台,那么你可能需要有条件地导入或导出库文件。一个常见的用例是一个同时支持 web 和原生平台的库。
要有条件的导入或导出,你需要检查 dart:*
库的存在。下面是一个有条件导出代码的例子,它检查 dart:io
和 dart:html
的存在:
export 'src/hw_none.dart' // Stub implementation
if (dart.library.io) 'src/hw_io.dart' // dart:io implementation
if (dart.library.html) 'src/hw_html.dart'; // dart:html implementation
下面是这段代码的作用。
- 在一个可以使用
dart:io
的应用程序中(例如,一个命令行应用程序),导出src/hw_io.dart
- 在一个可以使用
dart:html
的应用程序中(一个 web 应用程序),导出src/hw_html.dart
- 否则,导出
src/hw_none.dart
要有条件地导入一个文件,使用与上面相同的代码,但将 exporrt
改为 import
。
注意:有条件的导入或导出只检查库在当前平台上是否可用,而不是检查是否实际导入或使用。
所有有条件导出的库都必须实现相同的 API。例如,这里是 dart:io
的实现:
import 'dart:io';
void alarm([String text]) {
stderr.writeln(text ?? message);
}
String get message => 'Hello World from the VM!';
这里是默认的实现,它是一个抛出 UnsupportedErrors 的 stub。
void alarm([String text]) => throw UnsupportedError('hw_none alarm');
String get message => throw UnsupportedError('hw_none message');
在任何平台上,你都可以导入有条件导出代码的库。
import 'package:hw_mp/hw_mp.dart';
void main() {
print(message);
}
提供补充文件 #
一个设计良好的库包是很容易测试的。我们建议你使用 test 包来编写测试,将测试代码放在测试包顶部的 test
目录中。
如果你创建了任何旨在供公众使用的命令行工具,请将这些工具放在 bin
目录下,这是公共的。启用从命令行运行工具,使用 pub global activate。将工具列在 pubspec 的可执行文件部分,允许用户直接运行它,而无需调用 pub global run。
如果你包含了一个如何使用你的库的例子,这将会很有帮助。这将被放入软件包顶部的 example
目录中。
你在开发过程中创建的任何工具或可执行文件,如果不是公开使用的,都会进入 tool
目录。
如果你把你的库发布到 pub.dev 站点,其他需要的文件,如 README.md
和 CHANGELOG.md
,将在发布软件包中描述。有关如何组织包目录的更多信息,请参见 pub 包布局惯例。
编写库文档 #
你可以使用 dartdoc 工具为你的库生成 API 文档。Dartdoc 解析源码寻找文档注释,其中使用了 ///
语法:
/// The event handler responsible for updating the badge in the UI.
void updateBadge() {
...
}
关于生成文档的例子,请看 shelf 文档。
注意:要在生成的文档中包含任何库级文档,你必须指定 library
指令。请参阅 问题 1082。
分发一个开源库 #
如果你的库是开源的,我们建议在 pub.dev 站点上分享它。要发布或更新库,请使用 pub publish,它可以上传您的包并创建或更新其页面。例如,请看 shelf 包的页面。有关如何准备发布软件包的详细信息,请参见发布包。
pub.dev 站点不仅托管您的软件包,而且还生成和托管您软件包的 API 参考文档。最新生成的文档的链接在软件包的 About 框中;例如,请看 shelf 包的 API 文档。到以前版本的文档的链接在软件包页面的版本选项卡中。
要确保你的软件包的 API 文档在 pub.dev 网站上看起来不错,请按照以下步骤进行。
- 在发布你的软件包之前,运行 dartdoc 工具,以确保你的 docs 成功生成,并且看起来符合预期。
- 发布软件包后,检查 Versions 选项卡以确保文档成功生成。
- 如果文档根本没有生成,点击 Verrsions 选项卡中的 failed,查看 dartdoc 的输出。
资源 #
使用以下资源了解更多关于库包的信息。