包布局约定
在构建 pub 包时,我们鼓励你遵循本页描述的约定。它们描述了如何在包内组织文件和目录,以及如何命名事物。
以下是一个完整包(名为 enchilada
),它使用了本指南的所有方面,看起来可能像这样:
enchilada/
.dart_tool/ *
pubspec.yaml
pubspec_overrides.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/
目录。不要将其提交到版本控制。要了解更多信息,请参阅工具的项目特定缓存。
** 如果存在 pubspec_overrides.yaml
文件,它会覆盖 pubspec.yaml
的某些方面。通常你不会想把它提交到版本控制。
*** 在运行 dart pub get
后会生成 pubspec.lock
文件。除非你的包是应用包,否则不要将其提交到版本控制。
**** 在本地运行 dart doc
后会生成 doc/api
目录。不要将 api
目录提交到版本控制。
pubspec 文件
#enchilada/
pubspec.yaml
pubspec.lock
每个包在其根目录中都有一个pubspec文件,名为 pubspec.yaml
。正是这个文件使其成为一个包。
对包运行 dart pub get
、dart pub upgrade
或 dart 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
文件的示例。如示例所示,你可以添加子章节。
# 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.
公共目录
#包中有两个目录对其他包是公共的:lib
和 bin
。你将公共库放在 lib
中,将公共工具放在 bin
中。
公共库
#以下目录结构显示了 enchilada 包的 lib
部分:
enchilada/
lib/
enchilada.dart
tortilla.dart
许多包定义了其他包可以导入和使用的 Dart 库。这些公共 Dart 库文件放在一个名为 lib
的目录中。
大多数包定义了一个用户可以导入的单个库。在这种情况下,其名称通常应与包的名称相同,例如这里的示例中的 enchilada.dart
。但你也可以定义其他库,使用对你的包有意义的任何名称。
当你这样做时,用户可以使用包的名称和库文件来导入这些库,如下所示:
import 'package:enchilada/enchilada.dart';
import 'package:enchilada/tortilla.dart';
如果你想组织公共库,你也可以在 lib
内部创建子目录。如果你这样做,用户导入时将指定该路径。假设你有以下文件层级:
enchilada/
lib/
some/
path/
olives.dart
用户导入 olives.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/
内部或外部时(lint 规则:avoid_relative_lib_imports),使用package:
。 - 否则,优先使用相对导入。
例如:
// When importing from within lib:
import 'src/beans.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()
的 Dart 脚本和支持文件,如 CSS 或 HTML)放在 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
的目录中。
钩子 (Hooks)
#enchilada/
hook/
build.dart
包可以定义由 Dart 和 Flutter SDK 调用的钩子。这些钩子有一个预定义的 CLI 接口,如果存在,将被 SDK 工具调用。
由于这些钩子在运行和构建时被 dart
和 flutter
工具调用,因此这些钩子的依赖项必须是正常的依赖项(dependencies
),而不是开发依赖项(dev_dependencies
)。
工具的项目特定缓存
#.dart_tool/
目录在你运行 dart pub get
时创建,并可能随时被删除。各种工具使用此目录来缓存特定于你的项目和/或本地机器的文件。.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/
中。这有助于加快未来重新运行构建步骤的速度。