package.json 之 Node.js 相關字段

前言

這陣子在工做中參與了業務的抽象,計劃打造通用組件併發布成 npm 包便於後續項目的共建,自熱而然,學習到了一些關於 package.json 以前沒有接觸到的知識點。node

在此,介紹一些在 package.json 中被 Node.js 所使用的字段,如下所使用的 Nodejs 版本均爲 v14.14.0.npm

  • name - 定義了以 package 形式導入時的名稱,同時它決定了它在 npm 源上的惟一名稱。
  • type - 規定 package 下的 .js 文件被 Node.js 以 CommonJS Modules 或 ECMAScript Modules 加載。
  • exports - 設置 package 的導出。
  • main - 規定加載 package 時的默認入口文件。

name

{  "name": "package-name"}複製代碼

"name" 字段定義你的 package 名稱,當你將 package 發佈到 npm 源上,牢記 name 須要知足 規範json

當你下載的 package 位於 node_modules 時,你就可使用如下方式導入:併發

const pkg = require("package-name");// import pkg from 'package-name';複製代碼

type

在 js 的世界中,存在着兩種影響力最大的模塊規範,它們是 CommonJS Modules 以及 ECMAScript Modules,好在 Nodejs 自從 v12 起就所有支持了,.js .cjs 文件默認以 CommonJS Modules 執行,.mjs 則默認以 ECMAScript Modules 執行。app

而 "type" 字段的出現讓咱們更好得決定 .js 文件被哪一種模塊規範執行,它的值有兩個,分別是 "module" 和 "commonjs"ide

{  "type": "commonjs"}複製代碼

當你設置爲 "commonjs" 時,那些以該 package.json 做爲最近的父級配置文件的 .js .cjs 文件默認以 CommonJS Modules 執行,若是你想執行 ECMAScript Modules,就必須將後綴名改成 .mjs學習

{  "type": "module"}複製代碼

同理,當你設置爲 "module" 時,那些以該 package.json 做爲最近的父級配置文件的 .js .mjs 文件默認以 ECMAScript Modules 執行,若是你想執行 CommonJS Modules,就必須將後綴名改成 .cjsui

exports

"exports" 字段容許你經過引用本身的 package name(Self-referencing a package using its name)來定義 package 的入口文件,舉個例子:this

// package.json{  "name": "pkg",  "exports": {".": "./main.mjs","./foo": "./foo.js"
  }
}複製代碼

以上能夠被解讀爲:ip

  "exports": {"pkg": "pkg/main.mjs","pkg/foo": "pkg/foo.js"
  }複製代碼
import { something } from "pkg"; // from "pkg/main.mjs"複製代碼
const { something } = require("pkg/foo"); // require("pkg/foo.js")複製代碼

它從 Node.js v12 開始被支持,並做爲 "main"(具體介紹請看下一節) 字段的替代方案。

他最大的一個特性就是 條件導出(Conditional Exports),當該 package 被導入時,可以判斷被導入時的模塊環境,從而執行不一樣的文件,簡而言之就是,咱們若是使用 import 命令,入口會加載 ECMAScript Modules 文件,若是使用 require 命令,入口則加載 CommonJS Modules 文件。

咱們來一探究竟,具體目錄結構以下:

├── mod
│   ├── mod.js
│   ├── mod.cjs
│   ├── package.json
│── app.js
└── app.mjs複製代碼

mod 做爲一個本地的 package,它的 package.json 定義以下:

{  "name": "mod",  "main": "index.js",  "type": "module",  "exports": {"require": "./mod.cjs","import": "./mod.js"
  }
}複製代碼

而且他提供了實現相同功能的兩個腳本文件,以應對不一樣的模塊環境:

// mod.cjsexports.name = "cjs";複製代碼
// mod.jsexport const name = "mjs";複製代碼

調用該模塊的文件以下:

// app.jsconst { name } = require("mod");console.log(name);複製代碼
// app.mjsimport { name } from "mod";console.log(name);複製代碼

這一切還不夠,由於 mod 如今做爲一個本地 package,它既不是從 npm 上 download 下來,也不處於 node_modules 目錄中,沒法經過 package name 的形式導入。

辦法老是有的,藉助於 package link,咱們能夠實現本地 package 的關聯,而不用正式發佈到 npm 源上。

先在 ./mod 目錄下執行 yarn link:

success Registered "mod".
info You can now run `yarn link "mod"` in the projects where you want to use this package and it will be used instead.複製代碼

而後在項目根目錄下,執行 yarn link "mod":

success Using linked package for "mod".複製代碼

最後,驗證咱們的 "exports" 字段是否起做用了:

$ node app.js$ cjs$ node app.mjs$ mjs複製代碼

結果證明,在不一樣的模塊環境中,執行了不一樣的腳本文件。

注意,對於全部在 "exports" 中定義的路徑都必須是絕對路徑。即 ./ 的形式。

main

{  "main": "./main.js"}複製代碼

"main" 字段定義了導入 package 目錄時使用的入口文件,舉個例子:

const pkg = require("package-name");複製代碼

因爲 node_modules 的查找機制,會被解析成如下路徑:

const pkg = require("./node_modules/package-name");複製代碼

這裏只導入了一個目錄,他不是一個具體模塊,可是藉助 main 即入口文件,文件路徑最終被解析爲:

const pkg = require("./node_modules/package-name/main.js");複製代碼

注意,當一個 package 同時擁有 "exports" 和 "main" 字段時,在被以 package name 方式導入時,exports 的優先級較高。

相關文章
相關標籤/搜索