目录

包布局约定

当你构建一个 pub 包时,我们鼓励你遵循此页面描述的约定。它们描述了你如何在包中组织文件和目录,以及如何命名。

以下是一个完整的包(名为 enchilada),它使用了这些指南的每个方面,可能看起来像这样

enchilada/
  .dart_tool/ *
  pubspec.yaml
  pubspec.lock **
  LICENSE
  README.md
  CHANGELOG.md
  benchmark/
    make_lunch.dart
  bin/
    enchilada
  doc/
    api/ ***
    getting_started.md
  example/
    main.dart
  hook/
    build.dart
  integration_test/
    app_test.dart
  lib/
    enchilada.dart
    tortilla.dart
    guacamole.css
    src/
      beans.dart
      queso.dart
  test/
    enchilada_test.dart
    tortilla_test.dart
  tool/
    generate_docs.dart
  web/
    index.html
    main.dart
    style.css

* 运行 dart pub get 后,.dart_tool/ 目录存在。不要将其检入源代码控制。要了解更多信息,请参阅 工具的特定于项目的缓存

** 运行 dart pub get 后,pubspec.lock 文件存在。除非你的包是应用程序包,否则请将其从源代码控制中排除。

*** 运行 dart doc 后,doc/api 目录在本地存在。不要将 api 目录检入源代码控制。

pubspec

#
enchilada/
  pubspec.yaml
  pubspec.lock

每个包在包的根目录中都有一个名为 pubspec.yamlpubspec 文件。这正是构成包的原因。

在包上运行 dart pub getdart pub upgradedart pub downgrade,会创建一个名为 pubspec.lock锁文件。如果你的包是应用程序包,请将锁文件检入源代码控制。否则,请不要这样做。

有关更多信息,请参阅 pubspec 页面

LICENSE

#
enchilada/
  LICENSE

如果要发布你的包,请包含一个名为 LICENSE 的许可文件。我们建议使用 OSI 批准的许可证,例如 BSD-3-Clause,以便其他人可以重用你的工作。

README.md

#
enchilada/
  README.md

在开源中,一个非常常见的文件是描述项目的 README 文件。这在 pub 中尤其重要。当你上传到 pub.dev 站点时,你的 README.md 文件会在你包的页面上显示(呈现为 Markdown)。这是向人们介绍你的代码的绝佳场所。

有关如何编写出色的 README 的指南,请参阅编写包页面

CHANGELOG.md

#
enchilada/
  CHANGELOG.md

包含一个 CHANGELOG.md 文件,该文件具有你包的每个发行版的部分,并带有注释以帮助你包的用户升级。你包的用户通常会查看变更日志,以发现错误修复和新功能,或确定升级到最新版本的包需要多少工作量。

为了支持解析 CHANGELOG.md 的工具,请使用以下格式

  • 每个版本都有自己的带有标题的部分。
  • 版本标题要么都是 1 级,要么都是 2 级。
  • 版本标题文本包含一个包版本号,可以选择以“v”作为前缀。

当你将包上传到 pub.dev 站点时,你包的 CHANGELOG.md 文件(如果有)将显示在 变更日志 选项卡中,呈现为 Markdown

以下是 CHANGELOG.md 文件的示例。如示例所示,你可以添加小节。

markdown
# 1.0.1

* Fixed missing exclamation mark in `sayHi()` method.

# 1.0.0

* **Breaking change:** Removed deprecated `sayHello()` method.
* Initial stable release.

## Upgrading from 0.1.x

Change all calls to `sayHello()` to instead be to `sayHi()`.

# 0.1.1

* Deprecated the `sayHello()` method; use `sayHi()` instead.

# 0.1.0

* Initial development release.

公共目录

#

你包中的两个目录对其他包是公开的:libbin。你将 公共库 放在 lib 中,将 公共工具 放在 bin 中。

公共库

#

以下目录结构显示了 enchilada 的 lib 部分

enchilada/
  lib/
    enchilada.dart
    tortilla.dart

许多定义了其他包可以导入和使用的 Dart 库。这些公共 Dart 库文件位于名为 lib 的目录中。

大多数包都定义了一个用户可以导入的库。在这种情况下,它的名称通常应与包的名称相同,例如此示例中的 enchilada.dart。但是你也可以使用对你的包有意义的任何名称定义其他库。

当你这样做时,用户可以使用包的名称和库文件来导入这些库,如下所示

dart
import 'package:enchilada/enchilada.dart';
import 'package:enchilada/tortilla.dart';

如果要组织你的公共库,你也可以在 lib 内创建子目录。如果你这样做,用户将在导入时指定该路径。假设你有以下文件层次结构

enchilada/
  lib/
    some/
      path/
        olives.dart

用户按如下方式导入 olives.dart

dart
import 'package:enchilada/some/path/olives.dart';

请注意,只有应位于 lib 中。入口点 — 带有 main() 函数的 Dart 脚本 — 不能放在 lib 中。如果你将 Dart 脚本放在 lib 中,你将发现它包含的任何 package: 导入都无法解析。相反,你的入口点应放在适当的 入口点目录 中。

有关包的更多信息,请参阅 创建包

公共工具

#

放置在 bin 目录中的 Dart 脚本是公开的。如果你在包的目录中,则可以使用 dart run 从包依赖的任何其他包的 bin 目录中运行脚本。从任何目录中,你可以使用 dart pub global activate 激活的包中运行脚本

如果你希望你的包被依赖,并且希望你的脚本对你的包是私有的,请将它们放在顶层的 tool 目录中。如果你不希望你的包被依赖,则可以将你的脚本保留在 bin 中。

公共资源

#
enchilada/
  lib/
    guacamole.css

虽然大多数包的存在是为了让你重用 Dart 代码,但你也可以重用其他类型的内容。例如,Bootstrap 的包可能包含许多 CSS 文件,供包的消费者使用。

这些文件放在顶层的 lib 目录中。你可以在其中放置任何类型的文件,并使用子目录进行组织,方式随你喜欢。

实现文件

#
enchilada/
  lib/
    src/
      beans.dart
      queso.dart

lib 目录中的库是公开可见的:其他包可以自由地导入它们。但是,一个包的大部分代码是内部实现库,应该只由包本身导入和使用。这些库放在 lib 的一个名为 src 的子目录中。如果这样有助于组织,你可以在其中创建子目录。

你可以自由地从同一包中的其他 Dart 代码(例如 lib 中的其他库、bin 中的脚本和测试)导入 lib/src 中的库,但你绝不应该从另一个包的 lib/src 目录导入。这些文件不是包的公共 API 的一部分,它们可能会以破坏你的代码的方式进行更改。

如何从你自己的包中导入库取决于库的位置。

例如

lib/beans.dart
dart
// When importing from within lib:
import 'src/beans.dart';
test/beans_test.dart
dart
// When importing from outside lib:
import 'package:enchilada/src/beans.dart';

你在此处使用的名称(在本例中为 enchilada)是在其 pubspec 中为你的包指定的名称。

Web 文件

#
enchilada/
  web/
    index.html
    main.dart
    style.css

对于 Web 包,请将入口点代码(包括 main() 和支持文件(例如 CSS 或 HTML)的 Dart 脚本)放在 web 下。你可以根据需要将 web 目录组织成子目录。

库代码放在 lib 下。如果库不是由 web 下的代码或其他包直接导入的,请将其放在 lib/src 下。将基于 Web 的示例放在 example 下。有关放置资源(例如图像)的提示,请参阅公共资源

命令行应用

#
enchilada/
  bin/
    enchilada

有些包定义了可以直接从命令行运行的程序。这些可以是 Shell 脚本或任何其他脚本语言,包括 Dart。

如果你的包定义了这样的代码,请将其放在名为 bin 的目录中。如果使用 dart pub global 设置,你可以从命令行上的任何位置运行该脚本。

测试和基准

#
enchilada/
  test/
    enchilada_test.dart
    tortilla_test.dart

每个包都应该有测试。对于 pub,约定是将大部分测试放在 test 目录(或其中的某个目录,如果你喜欢的话)中,并在其文件名末尾添加 _test

通常,这些测试使用 test 包。

enchilada/
  integration_test/
    app_test.dart

Flutter 应用程序包也可能具有特殊的集成测试,这些测试使用 integration_test 包。这些测试放在它们自己的 integration_test 目录中。

其他包可以选择遵循类似的模式,将较慢的集成测试与单元测试分开,但请注意,默认情况下 dart test 不会运行这些测试。你必须使用 dart test integration_test 显式运行它们。

enchilada/
  benchmark/
    make_lunch.dart

具有性能关键代码的包也可能包含基准测试。这些测试 API 不是为了正确性,而是为了速度(或内存使用量,或者其他经验指标)。

文档

#
enchilada/
  doc/
    api/
    getting_started.md

如果你有代码和测试,你可能需要的下一个部分是好的文档。这放在名为 doc 的目录中。

当你运行 dart doc 工具时,它默认将 API 文档放在 doc/api 下。由于 API 文档是从源代码生成的,因此你不应将其置于源代码控制之下。

除了生成的 api 之外,我们没有关于你编写的文档的格式或组织的任何指导。使用你喜欢的任何标记格式。

示例

#
enchilada/
  example/
    main.dart

代码、测试、文档,你的用户还想要什么?当然,是使用你的包的独立示例程序!这些程序放在 example 目录中。如果示例很复杂并且使用多个文件,请考虑为每个示例创建一个目录。否则,你可以将每个示例直接放在 example 中。

在你的示例中,使用 package: 从你自己的包中导入文件。这确保了你包中的示例代码看起来与包外部的代码完全一样。

如果你可能会发布你的包,请考虑创建一个具有以下名称之一的示例文件

  • example/example[.md]
  • example[/lib]/main.dart
  • example[/lib]/package_name.dart
  • example[/lib]/package_name_example.dart
  • example[/lib]/example.dart
  • example/README[.md]

当你发布一个包含上述一个或多个文件的包时,pub.dev 站点会创建一个 示例 标签,以显示它找到的第一个文件(按上述列表中的顺序搜索)。例如,如果你的包在其 example 目录下有许多文件,包括名为 README.md 的文件,则你的包的“示例”标签将显示 example/README.md 的内容(解析为 Markdown。)

内部工具和脚本

#
enchilada/
  tool/
    generate_docs.dart

成熟的包通常具有一些小的辅助脚本和程序,人们在开发包本身时会运行这些脚本和程序。想想测试运行器、文档生成器或其他自动化位之类的东西。

bin 中的脚本不同,这些脚本不是为包的外部用户准备的。如果你有任何这些脚本,请将它们放在一个名为 tool 的目录中。

钩子

#
enchilada/
  hook/
    build.dart

包可以定义由 Dart 和 Flutter SDK 调用的钩子。这些钩子具有预定义的 CLI,如果存在,SDK 工具将调用它们。

由于这些钩子是在运行和构建时由 dartflutter 工具调用的,因此这些钩子的依赖项必须是正常的依赖项,而不是 dev_dependencies

工具的特定于项目的缓存

#

当你运行 dart pub get 时会创建 .dart_tool/ 目录,并且可能会随时删除它。各种工具使用此目录来缓存特定于你的项目和/或本地计算机的文件。.dart_tool/ 目录永远不应签入源代码控制,或在计算机之间复制。

删除 .dart_tool/ 目录通常也是安全的,尽管某些工具可能需要重新计算缓存的信息。

示例:dart pub get 工具会将依赖项下载并提取到全局 $PUB_CACHE 目录,然后写入一个 .dart_tool/package_config.json 文件,该文件将包名称映射到全局 $PUB_CACHE 目录中的目录。当其他工具(例如分析器和编译器)需要解析诸如 import 'package:foo/foo.dart' 之类的语句时,会使用 .dart_tool/package_config.json 文件。

在开发需要特定于项目的缓存的工具时,你可能会考虑使用 .dart_tool/ 目录,因为大多数用户已经使用 .gitignore 忽略了它。当在用户的 .dart_tool/ 目录中缓存工具的文件时,你应该使用唯一的子目录。为了避免冲突,.dart_tool/<tool_package_name>/ 形式的子目录保留给名为 <tool_package_name> 的包使用。如果你的工具不是通过 pub.dev 站点分发的,你可能会考虑发布一个占位符包以保留唯一的名称。

示例: package:build 提供了一个用于编写代码生成步骤的框架。当运行这些构建步骤时,文件会缓存在 .dart_tool/build/ 中。这有助于加快未来重新运行构建步骤的速度。