exports使用 package.json字段控制如何訪問你的 npm 包

目錄

想象一下你正在開發一個 npm 包……

術語

什么是exports領域?

exports好處

保護內部文件

多格式包

將子路徑映射到dist目錄

子路徑導出

單一入口點

多個入口點

公開軟件包文件的子集

有條件出口

設置使用條件

默認條件

句法

針對 Node.js 和瀏覽器


想象一下你正在開發一個 npm 包……

您希望提供多個入口點,但同時限制對內部文件的訪問。您需要同時支持 CJS 和 ESM,包含類型定義,甚至可能還要確保瀏覽器兼容性。您如何管理所有這些需求?

在早期版本的 Node.js 中,包使用main字段 in?package.json來定義單個入口點這種方法雖然簡單,但存在局限性:它只允許一個入口點,并且包中的所有文件都可訪問,無法保護內部文件。隨著生態系統的發展(尤其是 ESM 的興起和對多格式包的需求),這種方法很快就顯得力不從心。

術語

  • ECMAScript 模塊(ESM):JavaScript 使用原生import&export語法的標準化模塊格式。

  • CommonJS (CJS):Node.js 的遺留模塊格式,用于require()導入和module.exports導出。

  • 包入口點:訪問包的入口路徑(例如pkg-apkg-a/file)。

  • 包子路徑:包名稱后面的路徑(例如,/this/is/subpath.jspkg-a/this/is/subpath.js)。

什么是exports領域?

該字段在Node.js v12.7.0(2019 年 7 月)中引入,通過兩個核心功能滿足了這些需求:exports package.json

  1. 子路徑導出:包可以定義多個入口點,只允許公開特定文件,同時阻止對包內部的訪問。

  2. 條件導出:包可以切換入口點,以針對不同的環境(例如,Node.js 與瀏覽器)和模塊類型(例如,CJS 與 ESM)解析不同的文件。

從那時起,exports它得到了主要 JavaScript 工具和構建系統的廣泛支持,例如 TypeScript、Deno、Vite、Webpack、esbuild 等。

exports好處

保護內部文件

以前,用戶可以導入軟件包中的任何文件,甚至是內部文件。這導致軟件包維護者難以更新或重構軟件包,因為他們無法判斷用戶是否依賴這些內部文件。現在exports,維護者可以明確定義哪些文件可以訪問,從而建立清晰的公共 API,并防止意外導入內部文件。這有助于維護者管理更新,而不會給用戶帶來損壞的風險。


您可以使用子路徑模式 ( ) 使軟件包中的所有文件均可訪問*。此模式會捕獲子路徑(包括嵌套路徑)中的任何字符串,并將其映射到目標文件路徑。使用此設置,用戶可以通過引用路徑來導入軟件包中的任何文件。

*? 匹配一切

*字符的行為與 glob 語法不同。它會捕獲嵌套路徑,并可能暴露比您預期更多的文件

{"name": "pkg-a","exports": {"./*": "./*" // 公開所有文件,包括嵌套路徑}
}

盡可能避免暴露所有文件

允許用戶導入任何文件意味著即使對界面進行微小的更改(包括您不希望用戶訪問的文件(例如,捆綁塊))也會成為重大更改,并且需要進行重大的 semver 更新。

import foo from 'pkg-a' // 🚫 已阻止(入口點未定義)
import { name } from 'pkg-a/package.json' // ?

多格式包

如今,軟件包經常面臨支持多種環境的挑戰——Node.js、瀏覽器、ESM、CJS 和 TypeScript 定義。exports中的字段package.json允許您為每個環境和模塊格式指定不同的文件。這確保了兼容性,并通過僅包含與每個目標相關的內容來優化導入。


為了讓您的包同時支持 ESM 和 CommonJS 使用者,您可以根據包的導入方式指定需要加載的文件。這樣,Node.js 和 TypeScript 就能在各自的上下文中解析正確的代碼 (?import?vs?require) 和合適的類型定義 (?.d.mtsvs?.d.cts)。

{"name": "pkg-a","exports": {"require": {"types": "./dist/index.d.cts",     // Types for require('pkg-a')"default": "./dist/index.cjs"      // Code for require('pkg-a')},"import": {"types": "./dist/index.d.mts",     // Types for import 'pkg-a'"default": "./dist/index.mjs"      // Code for import 'pkg-a'}}
}

將其與子路徑導出相結合,您可以為每個入口點導出不同的類型,同時仍然支持 ESM 和 CommonJS 消費者:

{"name": "pkg-a","exports": {".": {"require": {"types": "./dist/index.d.cts",    // Types for require('pkg-a')"default": "./dist/index.cjs"     // Code for require('pkg-a')},"import": {"types": "./dist/index.d.mts",    // Types for import 'pkg-a'"default": "./dist/index.mjs"     // Code for import 'pkg-a'}},"./feature": {"require": {"types": "./dist/feature.d.cts",  // Types for require('pkg-a/feature')"default": "./dist/feature.cjs"   // Code for require('pkg-a/feature')},"import": {"types": "./dist/feature.d.mts",  // Types for import 'pkg-a/feature'"default": "./dist/feature.mjs"   // Code for import 'pkg-a/feature'}}}
}

常問問題

  • 我是否需要為require和提供單獨的類型文件import

    是的。TypeScript 使用文件的擴展名.d.ts來推斷其描述的模塊格式。一個.d.cts文件代表一個 CommonJS.cjs文件,一個.d.mts文件代表一個 ESM.mjs文件。如果將兩個文件放在同一個.d.ts文件里,TypeScript 會錯誤地解釋模塊格式,并可能導致代碼在運行時失敗。

    請參閱 Andrew Branch (TypeScript 核心團隊) 在類型錯誤嗎?中解釋這種不匹配→ 🎭 偽裝成 CJS。

  • 每個條件塊內的鍵的順序重要嗎?

    是的。該types字段必須放在前面default,TypeScript 才能正確識別。如果放在后面,TypeScript 會忽略它。

  • 消費者需要哪些 TypeScript 設置?

    它們必須在其 中設置?moduleResolution?Node16NodeNext或。這些模式啟用條件導出解析和正確的模塊格式檢測。Bundler,tsconfig.json

要了解更多信息,請參閱TypeScript 文檔。其中深入介紹了配置exportsTypeScript 的其他方法(例如,跨 TypeScript 版本導出不同類型)。

將子路徑映射到dist目錄

JavaScript 項目經常將目錄中的代碼編譯src到 中dist,從而產生類似 的導入import foo from 'pkg-a/dist/util'。包作者可能不希望dist在導入路徑中包含 ,以獲得更簡單的 API,但將文件輸出到包根目錄需要復雜的發布步驟,這可能會污染開發環境。

通過該exports字段,包子路徑可以直接映射到dist目錄內部,從而允許消費者使用更清潔的導入,而import foo from 'pkg-a/util'無需為維護者提供復雜的發布腳本。


exports字段的 subpaths 對象允許您將任意子路徑定義為映射到包中文件路徑的鍵。這使您可以使用更簡單、更短的子路徑來公開深度嵌套的路徑。

{"name": "pkg-a","exports": {"./deep-file": "./dist/deep/deep/file.js", // 直接映射到文件"./*": "./dist/*" // 在根級別公開 dist 中的所有內容}
}
import foo from 'pkg-a' // 🚫 已阻止(入口點未定義)
import bar from 'pkg-a/deep-file' // ? - 解析為 dist/deep/deep/file.js
import baz from 'pkg-a/file.js' // ? - 解析為 dist/file.js

子路徑導出

子路徑導出允許您定義包的入口點并將它們映射到包內的文件路徑。


要定義多個入口點,exports可以將該字段設置為子路徑對象,其中每個鍵都以 開頭..鍵表示主包入口,子路徑以 開頭./。鍵可以映射到包內的文件路徑,也可以映射到條件對象(我們稍后會討論)。

單一入口點

該字段最簡單的用法exports是指向包入口文件的字符串。雖然它與 字段類似main,但有一個顯著的區別:一旦使用exports它就會將您的包黑框起來。這意味著除非明確指定,否則默認情況下任何子路徑(甚至package.json)都無法訪問。

{"name": "pkg-a","exports": "./index.js" // Package entry point
}
import foo from 'pkg-a' // ? Resolves to pkg-a/index.js
import { name } from 'pkg-a/package.json' // 🚫 Blocked

多個入口點

要定義多個入口點,請設置exports為一個子路徑對象——該對象的每個鍵都以 開頭.,值是包內某個文件的相對路徑。如上所述,.鍵表示主包入口,子路徑以 開頭./

{"name": "pkg-a","exports": {".": "./index.js", // Package entry point"./package.json": "./package.json" // Allow importing pkg-a/package.json}
}
import foo from 'pkg-a' // ?
import { name } from 'pkg-a/package.json' // ?


公開軟件包文件的子集

要僅公開特定目錄,請將子路徑模式放置在該子目錄中。此方法允許使用者僅從指定目錄導入文件。此外,您還可以通過將子路徑映射到 來阻止對子路徑的訪問null

{"name": "pkg-a","exports": {"./dist/*": "./dist/*", // Only expose the dist directory"./dist/internal/*": null // Blocks access to dist/internal}
}

import foo from 'pkg-a' // 🚫 Blocked (entry point not defined)
import bar from 'pkg-a/dist/file.js' // ?
import baz from 'pkg-a/dist/dir/file.js' // ?
import qux from 'pkg-a/dist/internal/file.js' // 🚫 Blocked
import quux from 'pkg-a/dist/internal/dir/file.js' // 🚫 Blocked

?

有條件出口

條件導出是一個非常強大的功能。它使你的包能夠根據使用者提供的條件動態加載不同的文件。利用此功能,你可以針對各種環境優化你的包。

舉個簡單的例子,假設你希望你的包入口點在兩個不同的文件之間切換。為此,請在你的 字段中設置一個條件導出對象exports package.json

{"name": "pkg-a","exports": {// Ordered by priority"condition-a": "./file-a.js","condition-b": "./file-b.js"}
}

導入此包時,加載的文件取決于運行時提供的條件:

import foo from 'pkg-a' // ? 根據提供的條件可以是file-a.js或file-b.js

設置使用條件

node

現在你已經為你的包設置了條件和入口點,那么如何在用戶端切換它們呢?這取決于誰在解析導入。

  • 如果您使用的是 Node.js,則可以使用標志指定條件。例如,這將加載,因為我們指定了:--conditions, -Cfile-a.js?condition-a

$ node --conditions=condition-a ./load-pkg-a.js
  • 如果您使用捆綁器,則可以在配置中傳入條件。例如,使用 Vite 時,您可以傳入條件(下面列出了支持條件的工具的文檔)。resolve.conditions

  • 如果沒有提供條件,則將無法解決并引發錯誤,因為沒有default定義條件。

vite

?包的情景模式

// package.json"exports": {".": {"custom": "./index.custom.js","import": "./index.mjs","require": "./index.cjs"}}

配置?

// vite.config.tsresolve: {conditions: ['custom'],},

?

?

默認條件

每個運行時/解析器通常設置自己的默認條件(這些不是按順序排列的):

  • Node.js:node,,,import??require??default
  • Vite:import,,,,,,或require???default??module??browser?production?development
  • esbuild:import,,,,,,require??default??browser??node??module

句法

與子路徑對象相反,條件導出對象exports字段內的任何對象,其鍵并非全部以 開頭.

條件(對象鍵)按優先級排序,并解析為第一個匹配的條目。(這可能感覺不直觀,因為 JavaScript 中的對象在技術上是無序的。)對象也可以嵌套,以指定解析文件所需的條件組合。

條件鍵的順序很重要

由于解析器具有默認條件,并且返回其匹配的第一個條件,因此應始終首先指定您的自定義條件(例如,無法達到requireimport、以下的任何內容)。default

    "exports": {"import": "./prod.mjs","require": "./prod.cjs",// 這將永遠不會匹配,因為它低于默認條件"this-will-never-match": "./dev.ts"}
}

針對 Node.js 和瀏覽器

exports字段可以定義一個適應 Node.js 或瀏覽器環境的入口點。在 Node.js 運行時中,用于解析的默認條件包括nodedefault和導入類型(import對于 ESM 為 ,require對于 CJS 為 )。

條件的優先級取決于包的條件導出對象中的關鍵順序。

{"name": "pkg-a","exports": {"node": "./dist/for-node.js", // Resolved by Node.js"default": "./dist/for-browsers.js" // Resolved by other environments}
}

default條件適用于非 Node 環境。或者,您可以使用browser?Vite、Webpack 和 Parcel 等 Web 應用打包工具能夠識別的條件。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/92082.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/92082.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/92082.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

AngularJS 安裝使用教程

一、AngularJS 簡介 AngularJS 是 Google 開發的一款前端 JavaScript 框架,采用 MVVM 架構,提供了數據雙向綁定、依賴注入、模塊化、路由管理等強大功能,適合構建單頁面應用(SPA)。注意:AngularJS&#xf…

基于python和neo4j構建知識圖譜醫藥問答系統

一、pyahocorasick1.安裝 pyahocorasick 包: pip install pyahocorasick -i https://pypi.tuna.tsinghua.edu.cn/simple/pip install pyahocorasick :安裝名為 pyahocorasick 的第三方庫👉 這個庫是一個 Aho-Corasick 多模匹配算法 的 Python…

片上網絡(NoC)拓撲結構比較

1. 拓撲結構拓撲結構延遲吞吐量跳數功耗面積開銷可擴展性容錯性布線復雜度適合通信模式Mesh(網格)低(O(√N))高(多路徑并行)O(√N)中高(路由器多)中高(規則布線&#xff…

git merge 命令有什么作用?具體如何使用?

回答重點git merge 命令主要用于將兩個分支的歷史和內容合并在一起。簡而言之,它會將一個分支的更改引入到當前分支中。常見的使用場景是將功能分支(feature branch)的修改合并回主分支(main branch)或者開發分支&…

【NLP輿情分析】基于python微博輿情分析可視化系統(flask+pandas+echarts) 視頻教程 - jieba庫分詞簡介及使用

大家好,我是java1234_小鋒老師,最近寫了一套【NLP輿情分析】基于python微博輿情分析可視化系統(flaskpandasecharts)視頻教程,持續更新中,計劃月底更新完,感謝支持。今天講解 jieba庫分詞簡介及使用 視頻在線地址&…

大模型的后訓練與邏輯能力

《DeepSeek原生應用與智能體開發實踐》【摘要 書評 試讀】- 京東圖書 在人工智能與機器學習領域,模型的后訓練階段不僅是技術流程中的關鍵環節,更是提升模型性能,尤其是數學邏輯能力的“黃金時期”。這一階段,通過對已初步訓練好…

pycharm安裝教程-PyCharm2025安裝步驟【MAC版】附帶安裝包

pycharm安裝教程-PyCharm2025安裝詳細步驟【MAC版】安裝安裝包獲取(文章末尾)今天來給大家分享 Mac 系統安裝 PyCharm,附帶安裝包資源安裝, PyCharm 相關就不敘述了,直接開始安裝! 安裝 2024版本、2025年…

【React Native】路由跳轉

Link 跳轉的路徑,就在href里寫/details。路徑都是相對于app目錄來寫的,也就是說app目錄就是/。很多時候,需要跳轉的組件比較復雜。比方說,要在里面要嵌套按鈕,或者其他東西。這種情況下,就可以在Link組件里…

使用 Spring Boot + AbstractRoutingDataSource 實現動態切換數據源

1. 動態切換數據源的原理AbstractRoutingDataSource 是 Spring 提供的一個抽象類,它通過實現 determineCurrentLookupKey 方法,根據上下文信息決定當前使用的數據源。核心流程如下:定義多數據源配置:注冊多個數據源。實現動態數據…

Kubernetes (K8S)知識詳解

Kubernetes (K8S) 是什么? Kubernetes 是 Google 在 2014 年開源的生產級別的容器編排技術(編排也可以簡單理解為調度、管理),用于容器化應用的自動化部署、擴展和管理。它的前身是 Google 內部的 Borg 項目,Borg 是 …

在github上傳python項目,然后在另外一臺電腦下載下來后如何保障成功運行

如何在 GitHub 上傳并在另一臺電腦成功運行 Python 項目? 一、上傳前(本地準備) 在你的項目文件夾中進行以下準備: 1. 確保結構清晰 my_project/ ├── main.py ├── utils.py ├── config.yaml ├── requirements.txt └── README…

詳解Mysql Order by排序底層原理

MySQL 的 ORDER BY 子句實現排序是一個涉及查詢優化、內存管理和磁盤 I/O 的復雜過程。其核心目標是高效地將結果集按照指定列和順序排列。一、確定排序模式 (Sort Mode)MySQL 根據查詢特性和系統變量決定采用哪種排序策略&#xff1a;1.1 Rowid 排序<sort_key, rowid> 模…

SpringBoot的介紹和項目搭建

SpringBoot是簡化Spring應用開發的一個框架&#xff0c;他是Spring技術棧的整合。優點&#xff1a;能夠快速創建獨立運行的Spring項目以及與主流框架集成使用嵌入式的Servlet容器&#xff0c;應用無需打成war包&#xff0c;內嵌tomcatStarters自動依賴和版本控制大量的自動裝配…

Selenium 攻略:從元素操作到 WebDriver 實戰

在自動化測試、網頁數據爬取、批量操作網頁等場景中&#xff0c;Selenium 無疑是最受歡迎的工具之一。作為一款強大的 Web 自動化工具&#xff0c;它能模擬人類操作瀏覽器的行為&#xff0c;實現點擊、輸入、跳轉等一系列動作。本文將從基礎到進階&#xff0c;全面解析 Seleniu…

【算法訓練營Day14】二叉樹part4

文章目錄找樹左下角的值路徑總和總結&#xff1a;遞歸函數的返回值路徑總和 II總結&#xff1a;二叉樹遞歸的思考從中序與后序遍歷序列構造二叉樹找樹左下角的值 題目鏈接&#xff1a;513. 找樹左下角的值 解題邏輯&#xff1a; 使用層序遍歷&#xff0c;將最后一層的第一個元…

工資系統如何計算工資

工資系統計算工資是一個集成數據收集、規則應用、自動核算和合規審核的自動化過程&#xff0c;以下是其核心原理和步驟&#xff0c;結合技術實現與法規要求進行說明&#xff1a;?? 一、工資系統的基本框架與數據準備系統初始化與規則配置企業信息設置&#xff1a;錄入公司名稱…

車載通信架構 --- DoIP協議通信

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 鈍感力的“鈍”,不是木訥、遲鈍,而是直面困境的韌勁和耐力,是面對外界噪音的通透淡然。 生活中有兩種人,一種人格外在意別人的眼光;另一種人無論…

基于Event Sourcing和CQRS的微服務架構設計與實戰

基于Event Sourcing和CQRS的微服務架構設計與實戰 業務場景描述 在電商系統中&#xff0c;訂單的高并發寫入與復雜的狀態流轉&#xff08;下單、支付、發貨、退貨等&#xff09;給傳統的CRUD模型帶來了挑戰&#xff1a; 數據一致性難保證&#xff1a;跨服務事務處理復雜&#x…

初級安全課第二次作業

&#xff08;一&#xff09;xss-labs 1~8關 1、前期準備 &#xff08;1&#xff09;打開小皮面板&#xff0c;并啟動Apache和MySQL&#xff08;2&#xff09;將 xss-labs放到 phpstudy_pro 的 WWW 目錄下&#xff08;3&#xff09;訪問連接&#xff1a;http://localhost/xss-la…

從零搭建智能搜索代理:LangGraph + 實時搜索 + PDF導出完整項目實戰

傳統的AI聊天系統往往局限于預訓練數據的知識范圍&#xff0c;無法獲取實時信息。本文將詳細闡述如何構建一個基于LangGraph的智能代理系統&#xff0c;該系統能夠智能判斷何時需要進行網絡搜索、有效維護對話上下文&#xff0c;并具備將對話內容導出為PDF文檔的功能。 本系統…