Stories

Detail Return Return

NPM package.json 中 browser,module,main - Stories Detail

參考:https://github.com/SunshowerC/blog/issues/8

browser VS module VS main

我們經常見到 package.json 裏有這樣一段內容:

{
...
  main: dist/index
  module: dist/index.esm.js
  browser: dist/zhihu-hybrid.umd.js
}

當我們在不同環境下 import 一個 npm 包時,到底加載的是 npm 包的哪個文件?有人很快地給出答案:main 字段中指定的文件。
然而我們清楚 npm 包其實又分為:

  • 只允許在客户端使用的
  • 只允許在服務端使用的
  • 瀏覽器/服務端都可以使用

如果我們需要開發一個 npm 包同時兼容支持 web端 和 server 端,需要在不同環境下加載npm包不同的入口文件,顯然一個 main 字段已經不能夠滿足我們的需求,這就衍生出來了 module 與 browser 字段。本文就來説下 這幾個字段的使用場景,以及同時存在這幾個字段時,他們之間的優先級。

文件優先級

在説 package.json 之前,先説下文件優先級。
由於我們使用的模塊規範有 ESM 和 commonJS 兩種,為了能在 node 環境下原生執行 ESM 規範的腳本文件,.mjs 文件就應運而生。
當存在 index.mjs 和 index.js 這種同名不同後綴的文件時,import './index' 或者 require('./index') 是會優先加載 index.mjs 文件的。也就是説,優先級 mjs > js

browser,module 和 main 字段

字段定義

  • main : 定義了 npm 包的入口文件,browser 環境和 node 環境均可使用
  • module : 定義 npm 包的 ESM 規範的入口文件,browser 環境和 node 環境均可使用
  • browser : 定義 npm 包在 browser 環境下的入口文件

使用場景與優先級

首先,我們假定 npm 包 test 有以下目錄結構

----- lib
   |-- index.browser.js
   |-- index.browser.mjs
   |-- index.js
   |-- index.mjs

其中 *.js 文件是使用 commonJS 規範的語法(require('xxx')),*.mjs 是用 ESM 規範的語法(import 'xxx')
其 package.json 文件:

{
...
  "main": "lib/index.js",  // main 
  "module": "lib/index.mjs", // module

  // browser 可定義成和 main/module 字段一一對應的映射對象,也可以直接定義為字符串
  "browser": {
    "./lib/index.js": "./lib/index.browser.js", // browser+cjs
    "./lib/index.mjs": "./lib/index.browser.mjs"  // browser+mjs
  },

  // "browser": "./lib/index.browser.js" // browser
}

根據上述配置,那麼其實我們的 package.json 指定的入口可以有

  • main
  • module
  • browser
  • browser+cjs
  • browser+mjs
    這 5 種情況。下面説下具體使用場景。

    Web + ESM 模式

    當我們代碼中使用

    import test from 'test'

    加載 test 模塊時, 實際上的加載優先級是: browser = browser+mjs > module > browser+cjs > main
    也就是説打包工具會根據這個順序去尋找 test package.json 文件中該字段指定的文件,直到找到為止。
    然而實際上的情況可能比這個更加複雜,具體可以參考流程圖:
    image.png

Web + commonJS 模式

當我們代碼中使用

const test = require('test')

加載 test 模塊時,優先級依然是 browser = browser+mjs > module > browser+cjs > main,打包工具會根據這個順序去尋找 test package.json 文件中該字段指定的文件,直到找到為止。

node + ESM/commonJS 模式

我們清楚,使用 webpack 構建項目的時候,有一個 target 選項,默認為 web,即進行 web 應用構建。
當我們需要進行一些 同構項目,或者其他 node 項目的構建的時候,我們需要將 webpack.config.js 的 target 選項設置為 node 進行構建。

import test from 'test'
// 或者 const test = require('test')

優先級是: module > main

node + commonJS 模式

通過 node test.js 直接執行腳本

const test = require('test')

只有 main 字段有效。

node + ESM

通過 --experimental-modules 可以讓 node 執行 ESM 規範的腳本(必須是 mjs 文件後綴)

node --experimental-modules test.mjs

test.mjs:

import test from 'test'

只有 main 字段有效。

總結

以下是當你創建一個 NPM 包事,package.json 中引用入口的聲明總結:

  • 如果導出是的 ESM 規範的包,使用 module 來聲明入口
  • 如果 npm 包只在 Web 端使用,並且嚴禁在 server 端使用,使用 browser 來聲明入口
  • 如果 npm 包只在 Server 端使用,使用 main 來聲明入口
  • 如果 npm 包在 Web 端和 Server 端都允許使用,使用 browser(可選,用到了宿主環境能力時需要聲明) 和 main
  • 如果你的 NPM 包滿足上面多種情況,可以把符合的字段都聲明出來,如 Server 端 ESM包,聲明 module 和 main
user avatar haoqidewukong Avatar banana_god Avatar zero_dev Avatar wanhuabandedasuan Avatar dunizb Avatar febobo Avatar imba97 Avatar munergs Avatar weidewei Avatar tanggoahead Avatar joe235 Avatar huangmingji Avatar
Favorites 54 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.