封裝
您可以使用 SvelteKit 建置應用程式以及元件庫,使用 @sveltejs/package
套件(npx sv create
有一個選項可以為您設定)。
當您建立一個應用程式時,src/routes
的內容是公開可見的;src/lib
包含您的應用程式的內部函式庫。
元件庫的結構與 SvelteKit 應用程式完全相同,除了 src/lib
是公開可見的部分,而您的根 package.json
用於發佈套件。src/routes
可能是隨附函式庫的文件或演示網站,或者它可能只是您在開發期間使用的沙箱。
從 @sveltejs/package
執行 svelte-package
命令將會取得 src/lib
的內容,並產生一個包含以下內容的 dist
目錄(可以設定)
src/lib
中的所有檔案。Svelte 元件將會預先處理,TypeScript 檔案將會轉譯為 JavaScript。- 類型定義(
d.ts
檔案)是為 Svelte、JavaScript 和 TypeScript 檔案產生。您需要安裝typescript >= 4.0.0
才能執行此操作。類型定義會放置在其實現旁邊,手寫的d.ts
檔案會原樣複製。您可以停用產生,但我們強烈建議不要這樣做 — 使用您的函式庫的人可能會使用 TypeScript,他們需要這些類型定義檔案。
@sveltejs/package
版本 1 產生了一個package.json
。現在不再是這種情況,它現在將使用您專案中的package.json
並驗證其是否正確。如果您仍然使用版本 1,請參閱此 PR 以取得遷移說明。
package.json 的剖析
由於您現在正在建置一個供公眾使用的函式庫,因此 package.json
的內容將變得更加重要。透過它,您可以設定套件的進入點、哪些檔案會發佈到 npm,以及您的函式庫有哪些相依性。讓我們逐一了解最重要的欄位。
name
這是您套件的名稱。其他人可以使用該名稱安裝它,並且可以在 https://npmjs.com/package/<name>
上看到它。
{
"name": "your-library"
}
在這裡閱讀更多關於它的資訊這裡。
license
每個套件都應該有一個 license 欄位,以便人們知道他們被允許如何使用它。一個非常受歡迎的許可證,在發行和重複使用方面也非常寬容且不提供保證,是 MIT
。
{
"license": "MIT"
}
在這裡閱讀更多關於它的資訊這裡。請注意,您還應該在套件中包含一個 LICENSE
檔案。
files
這會告知 npm 它將打包並上傳到 npm 的哪些檔案。它應該包含您的輸出資料夾(預設為 dist
)。您的 package.json
和 README
以及 LICENSE
將始終包含在內,因此您無需指定它們。
{
"files": ["dist"]
}
若要排除不必要的檔案(例如單元測試,或僅從 src/routes
匯入的模組等),您可以將它們新增至 .npmignore
檔案。這將產生更小的套件,安裝速度更快。
在這裡閱讀更多關於它的資訊這裡。
exports
"exports"
欄位包含套件的進入點。如果您透過 npx sv create
設定新的函式庫專案,它會設定為單一匯出,即套件根目錄
{
"exports": {
".": {
"types": "./dist/index.d.ts",
"svelte": "./dist/index.js"
}
}
}
這會告知捆綁器和工具,您的套件只有一個進入點,即根目錄,所有內容都應該透過它匯入,如下所示
import { import Something
Something } from 'your-library';
types
和 svelte
金鑰是匯出條件。它們會告知工具在尋找 your-library
匯入時要匯入哪個檔案
- TypeScript 會看到
types
條件並尋找類型定義檔案。如果您沒有發佈類型定義,請省略此條件。 - 具有 Svelte 感知的工具會看到
svelte
條件,並知道這是一個 Svelte 元件函式庫。如果您發佈的函式庫沒有匯出任何 Svelte 元件,並且也可以在非 Svelte 專案中運作(例如 Svelte 儲存函式庫),您可以將此條件替換為default
。
先前版本的
@sveltejs/package
也新增了一個package.json
匯出。這不再是範本的一部分,因為所有工具現在都可以處理未明確匯出的package.json
。
您可以根據自己的喜好調整 exports
,並提供更多進入點。例如,如果您不想使用重新匯出元件的 src/lib/index.js
檔案,而是想直接公開 src/lib/Foo.svelte
元件,您可以建立以下匯出對應...
{
"exports": {
"./Foo.svelte": {
"types": "./dist/Foo.svelte.d.ts",
"svelte": "./dist/Foo.svelte"
}
}
}
...而您函式庫的使用者可以像這樣匯入元件
import module "your-library/Foo.svelte"
Foo from 'your-library/Foo.svelte';
請注意,如果您提供類型定義,這樣做需要格外小心。在此處閱讀有關注意事項的更多資訊這裡
一般而言,匯出對應的每個金鑰都是使用者必須用來從您的套件匯入某些內容的路徑,而值是要匯入的檔案的路徑,或是依序包含這些檔案路徑的匯出條件對應。
在此處閱讀更多關於 exports
的資訊這裡。
svelte
這是一個舊版的欄位,可讓工具識別 Svelte 元件函式庫。當使用 svelte
匯出條件時,它不再是必要的,但為了向後相容尚未了解匯出條件的過時工具,最好保留它。它應該指向您的根進入點。
{
"svelte": "./dist/index.js"
}
sideEffects
package.json
中的 sideEffects
欄位由捆綁器用來判斷模組是否可能包含具有副作用的程式碼。如果模組在匯入時對模組外部的其他腳本產生可觀察的變更,則會將其視為具有副作用。例如,副作用包括修改全域變數或內建 JavaScript 物件的原型。由於副作用可能會影響應用程式其他部分的行為,因此這些檔案/模組將包含在最終捆綁包中,而不管其匯出是否在應用程式中使用。避免程式碼中的副作用是最佳實踐。
在 package.json
中設定 sideEffects
欄位可以幫助捆綁器更積極地消除最終捆綁包中未使用的匯出,這個過程稱為樹狀搖晃。這會產生更小、更有效率的捆綁包。不同的捆綁器以不同的方式處理 sideEffects
。雖然 Vite 不需要,但我們建議函式庫聲明所有 CSS 檔案都有副作用,這樣您的函式庫才會與 webpack 相容。這是新建立的專案隨附的組態設定
{
"sideEffects": ["**/*.css"]
}
如果您的函式庫中的腳本具有副作用,請確保更新
sideEffects
欄位。在新建立的專案中,所有腳本預設都會標記為無副作用。如果具有副作用的檔案被錯誤地標記為沒有副作用,則可能會導致功能損壞。
如果您的套件有具有副作用的檔案,您可以在陣列中指定它們
{
"sideEffects": [
"**/*.css",
"./dist/sideEffectfulFile.js"
]
}
這只會將指定的檔案視為具有副作用。
TypeScript
即使您自己不使用 TypeScript,您也應該發佈函式庫的類型定義,以便使用您的函式庫的人可以獲得適當的 intellisense。@sveltejs/package
使產生類型的過程對您來說幾乎是不透明的。預設情況下,封裝函式庫時,會自動為 JavaScript、TypeScript 和 Svelte 檔案產生類型定義。您需要確保的是 exports 對應中的 types
條件指向正確的檔案。透過 npx sv create
初始化函式庫專案時,這會自動為根匯出設定。
但是,如果您有根匯出以外的其他內容 — 例如提供 your-library/foo
匯入 — 您需要格外注意提供類型定義。不幸的是,TypeScript 預設情況下不會解析類似 { "./foo": { "types": "./dist/foo.d.ts", ... }}
的匯出條件的 types
。相反,它會在函式庫的根目錄中搜尋 foo.d.ts
(即 your-library/foo.d.ts
而不是 your-library/dist/foo.d.ts
)。若要修正此問題,您有兩個選項
第一種選擇是要求使用您函式庫的人員,在他們的 tsconfig.json
(或 jsconfig.json
) 中將 moduleResolution
選項設定為 bundler
(自 TypeScript 5 起可用,是未來最佳且推薦的選項)、node16
或 nodenext
。這會讓 TypeScript 實際查看 exports 對應表並正確解析類型。
第二種選擇是(濫用)TypeScript 的 typesVersions
功能來連接類型。這是 package.json
內的一個欄位,TypeScript 會使用它來檢查不同 TypeScript 版本的類型定義,並且也包含一個路徑映射功能。我們利用這個路徑映射功能來達到我們想要的目的。對於上面提到的 foo
export,對應的 typesVersions
看起來像這樣:
{
"exports": {
"./foo": {
"types": "./dist/foo.d.ts",
"svelte": "./dist/foo.js"
}
},
"typesVersions": {
">4.0": {
"foo": ["./dist/foo.d.ts"]
}
}
}
>4.0
告訴 TypeScript,如果使用的 TypeScript 版本大於 4(實際上應該總是如此),則檢查內部對應表。內部對應表告訴 TypeScript,your-library/foo
的類型定義位於 ./dist/foo.d.ts
中,這基本上複製了 exports
條件。您也可以使用 *
作為萬用字元,一次提供多個類型定義,而無需重複自己。請注意,如果您選擇使用 typesVersions
,您必須通過它宣告所有類型導入,包括根導入(定義為 "index.d.ts": [..]
)。
您可以在此處閱讀有關該功能的更多資訊。
最佳實踐
除非您打算讓您的套件僅供其他 SvelteKit 專案使用,否則您應該避免在您的套件中使用 SvelteKit 特有的模組,例如 $app/environment
。例如,您可以使用 import { BROWSER } from 'esm-env'
(請參閱 esm-env 文件),而不是使用 import { browser } from '$app/environment'
。您可能還希望將當前 URL 或導航操作之類的東西作為 prop 傳入,而不是直接依賴 $app/stores
、$app/navigation
等。以這種更通用的方式編寫您的應用程式,也更容易設定用於測試、UI 演示等的工具。
請確保您透過 svelte.config.js
(而不是 vite.config.js
或 tsconfig.json
)新增別名,以便它們被 svelte-package
處理。
您應該仔細考慮您對套件所做的變更是否是錯誤修復、新功能或重大變更,並相應地更新套件版本。請注意,如果您從現有的函式庫中移除 exports
中的任何路徑或其中的任何 export
條件,則應將其視為重大變更。
{
"exports": {
".": {
"types": "./dist/index.d.ts",
// changing `svelte` to `default` is a breaking change:
"svelte": "./dist/index.js"
"default": "./dist/index.js"
},
// removing this is a breaking change:
"./foo": {
"types": "./dist/foo.d.ts",
"svelte": "./dist/foo.js",
"default": "./dist/foo.js"
},
// adding this is ok:
"./bar": {
"types": "./dist/bar.d.ts",
"svelte": "./dist/bar.js",
"default": "./dist/bar.js"
}
}
}
選項
svelte-package
接受以下選項:
-w
/--watch
— 監看src/lib
中的檔案變更並重建套件-i
/--input
— 輸入目錄,其中包含套件的所有檔案。預設為src/lib
-o
/--output
— 處理後的檔案寫入的輸出目錄。您的package.json
的exports
應指向其中的檔案,並且files
陣列應包含該資料夾。預設為dist
-t
/--types
— 是否建立類型定義 (d.ts
檔案)。我們強烈建議這樣做,因為它有助於生態系統函式庫的品質。預設為true
--tsconfig
- tsconfig 或 jsconfig 的路徑。如果未提供,則會在工作區路徑中搜尋下一個較高的 tsconfig/jsconfig。
發佈
要發佈產生的套件
npm publish
注意事項
所有相對檔案導入都需要完整指定,並符合 Node 的 ESM 演算法。這表示對於像 src/lib/something/index.js
這樣的檔案,您必須包含帶有副檔名的檔名
import { import something
something } from './something/index.js';
如果您使用 TypeScript,您需要以相同的方式導入 .ts
檔案,但使用 .js
檔案結尾,而不是 .ts
檔案結尾。(這是 TypeScript 的設計決策,我們無法控制。) 在您的 tsconfig.json
或 jsconfig.json
中設定 "moduleResolution": "NodeNext"
將有助於您解決此問題。
除了 Svelte 檔案(預處理)和 TypeScript 檔案(轉換為 JavaScript)之外,所有檔案都會按原樣複製。