Wait the light to fall

创建包

焉知非鱼

Creating packages

Dart 生态系统使用来共享软件,如库和工具。本页告诉你如何创建一个包,重点是最常见的一种包,库包

是什么造就了一个库包 #

下图是最简单的库包的布局:

img

一个库的最低要求是

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 库包常用的:

img

直接在 lib 下,主库文件 shelf.dartlib/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:ioHttpRequest 对象。

对网络应用的提示: 为了在使用 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

img

有条件地导入和导出库文件 #

如果你的库支持多个平台,那么你可能需要有条件地导入或导出库文件。一个常见的用例是一个同时支持 web 和原生平台的库。

要有条件的导入或导出,你需要检查 dart:* 库的存在。下面是一个有条件导出代码的例子,它检查 dart:iodart: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.mdCHANGELOG.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 的输出。

资源 #

使用以下资源了解更多关于库包的信息。