目錄

在 Dart 生態系統中使用 packages 實現共享軟體,比如一些函式庫和工具。本章將透過最常見的 Package 來介紹如何建立一個 Package。

建立一個新的 package

若要為 package 建立一個初始化的目錄和結構,使用 dart create 命令,並加入 package 作為命令引數來建立:

$ dart create -t package <PACKAGE_NAME>

Package 的組成

下圖展示了最簡單的 Package 的結構:

root directory contains pubspec.yaml and lib/file.dart

Package 的最基本要求包括:

pubspec 檔案
Package 的 pubspec.yaml 檔案與應用程式的 pubspec.yaml 檔案相同— pubspec.yaml 檔案中並沒有特別的指出這個 Package 是一個函式庫。

lib 目錄
如你所料,庫的程式碼位於 lib 目錄下,且對於其他 Package 是公開的。你可以根據需要在 lib 下任意建立組織檔案結構。按照慣例,實現程式碼會放在 lib/src 目錄下。 lib/src 目錄下的程式碼被認為是私有的。其他 Package 應該永遠不需要匯入 src/... 目錄下程式碼。透過匯出 lib/src 目錄的檔案到一個 lib 目錄的檔案,實現對 lib/src 目錄中 API 的公開。

組織 Package 的程式碼結構

在建立一個小的,獨立的 Package 時(稱之為 Mini Library),它非常容易維護,擴充和測試。大多數情況下,除非存在兩個類緊密耦合的情況,否則每個類都應該將自己視為一個 Mini Library 。

直接在 lib 目錄下建立“主” Library 檔案,lib/<package-name>.dart,該檔案匯出所有的公開的 API 。這樣就可以允許使用者匯入單個檔案就能夠獲得 Library 的所有功能。

lib 目錄還可能包含其他可匯入的非 src 程式碼。例如,主 Library 可能是跨平臺的,但建立的獨立 Library 依賴於 dart:io 或 dart:html 。 Some packages have separate libraries that are meant to be imported with a prefix, when the main library is not.(無法確切理解含義,暫未翻譯)

這裡讓我們來看下一個真實 Library Package 的組織結構:shelf 。 shelf Package 提供了一種使用 Dart 建立 Web 伺服器的簡便方法,它是一種 Dart Package 的常用結構:

shelf root directory contains example, lib, test, and tool subdirectories

主 Library 檔案 shelf.dart 在 lib 目錄下,透過 shelf.dart 檔案匯出 lib/src 目錄下的若干檔案。為了不匯出過多的 API,並且為開發者提供公開的 API 的概覽, shelf.dart 使用了 show 來指定哪些內容需要匯出:

export 'src/cascade.dart' show Cascade;
export 'src/handler.dart' show Handler;
export 'src/hijack_exception.dart' show HijackException;
export 'src/middleware.dart' show Middleware, createMiddleware;
export 'src/middleware/add_chunked_encoding.dart' show addChunkedEncoding;
export 'src/middleware/logger.dart' show logRequests;
export 'src/middleware_extensions.dart' show MiddlewareExtensions;
export 'src/pipeline.dart' show Pipeline;
export 'src/request.dart' show Request;
export 'src/response.dart' show Response;
export 'src/server.dart' show Server;
export 'src/server_handler.dart' show ServerHandler;

在 shelf Package 中同樣包含了 Mini Library:shelf_io 。介面卡用來處理來自 dart:io 的 HttpRequest 物件。

匯入庫檔案

在從其他 package 匯入庫檔案時,使用 package: 命令來指定檔案的 URI 。

import 'package:utilities/utilities.dart';

在兩個檔案都在 lib 目錄中,或兩個檔案都在 lib 目錄外,我們都可以使用相對路徑的方式匯入 Library 。但是,如果兩個檔案不都在 lib 目錄中,需要對 lib 內或者 lib 外進行查詢,那麼此時必須要使用 package: 匯入。如果對當前使用存在疑惑,那麼直接 package:package: 滿足所有情況。

下面圖片展示分別從 lib 和 web 目錄中匯入 lib/foo/a.dart

lib/bar/b.dart uses a relative import; web/main.dart uses a package import

條件匯入或條件匯出 Library 檔案

如果你的 library 支援多平臺,那麼你應該會用到條件匯入或條件匯出 library 檔案。常見的使用案例是,一個函式庫同時支援 Web 和 Native 平台。

為了使用條件匯入或條件匯出,你需要檢查是否存在 dart:* 庫。下面是一個條件匯出程式碼的範例,它將檢查是否存在 dart:io and 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
lib/hw_mp.dart

該程式碼的作用如下:

  • 在一個可以使用 dart:io 的 app 中(例如一個命令列應用),匯出 src/hw_io.dart

  • 在一個 web 應用中可以使用 dart:html 匯出 src/hw_html.dart

  • 若是其他情況,則匯出 src/hw_none.dart

要條件匯入一個檔案可以使用和上面一樣的方式,僅需將 export 改為 import 即可。

所有條件匯出的函式庫必須實現相同的 API。下面是 dart:io 實現的一個例子:

import 'dart:io';

void alarm([String? text]) {
  stderr.writeln(text ?? message);
}

String get message => 'Hello World from the VM!';
lib/src/hw_io.dart

這是一個預設實現,它會導致丟擲 UnsupportedErrors

void alarm([String? text]) => throw UnsupportedError('hw_none alarm');

String get message => throw UnsupportedError('hw_none message');
lib/src/hw_none.dart

在任何平臺上,你都可以匯入具有條件匯出程式碼的函式庫:

import 'package:hw_mp/hw_mp.dart';

void main() {
  print(message);
}

提供額外檔案

一個設計良好的 Package 很容易被測試。我們建議使用 test Package 編寫測試使用案例,並將測試程式碼放到 Package 根目錄的 test 目錄中。

如果要建立一個公用的命令列工具,應該將這些工具放到公共目錄 bin 中。使用 dart pub global activate 命令列來執行工具。在 pubspec 的 executables 部分 列出的工具允許使用者直接執行它而無需呼叫 dart pub global run

在 Library 中包含一個 example 程式示範如何使用 Library 是非常有用的。 example 程式在 Package 根目錄的 example 目錄中。

在開發過程中任何非公開的工具或可執行程式,應該放到 tool 資料夾。

如果要將 Library 釋出到 Pub 網站還要求一些其他的檔案來描述 釋出的 Package ,例如:README.mdCHANGELOG.md 檔案。更多關於如何組織 Package 目錄的內容,參見 Pub Package 佈局約定

為 Library 製作文件

使用 [dartdoc][] 可以為 Library 產生 API 文件。 dartdoc 解析原始檔去查詢使用 /// 語法標註的 文件註釋

/// The event handler responsible for updating the badge in the UI.
void updateBadge() {
  ...
}

文件產生範例,參見 shelf 文件

若要自動產生任何庫級別的文件,請新增一個 library 指令並直接在其上方附加註釋。更多詳情,請參閱文件 Effective Dart: Documentation

分發開源 Library

如果 Library 是開源的,我們建議將他共享到 Pub 網站。使用 pub publish 來發布或者更新 Library,該命令將會上傳 Package 並建立或更新其頁面。範例參見 shelf Package 頁面。有關如何準備釋出 Package 的詳細內容,參見 釋出 Package

Pub.dev 網站不僅僅是 Dart Packages 的釋出網站, Pub 網站不僅用於託管 Package ,還能夠產生託管 Package 的 API 參考文件。最新產生的文件的連結位於 Package 的 About 選項卡中; 範例參見 shelf Package 的 API 文件。連結到以前版本的文件,位於 Package 頁面的 Versions 選項卡中。

為了確保 Package 的 API 文件在 Pub 網站上看起來更美觀,請遵循以下步驟:

  • 在釋出 Package 前,請透過執行 dart doc 工具確保文件能夠產生成功且符合預期。

  • 在釋出 Package 後,請檢查 Versions 選項卡,確保文件產生成功。

  • 如果文件沒有產生,點選 Versions 選項卡中的 failed 檢視 dartdoc 的輸出。

資源

透過運用以下資源來了解學習更多關於 Library Package 的內容: