Skip to content
On this page

package.json 中 module、main、browser 的优先级

前言

前端开发中使用到 npm 包那可算是家常便饭,而使用到 npm 包总免不了接触到 package.json 包配置文件。

那么这里就有一个问题,当我们在不同环境下 import 一个 npm 包时,到底加载的是 npm 包的哪个文件?

老司机们很快地给出答案:main 字段中指定的文件

然而我们清楚 npm 包其实又分为:只允许在客户端使用的,只允许造服务端使用的,浏览器/服务端都可以使用。
如果我们需要开发一个 npm 包同时兼容支持 web端 和 server 端,需要在不同环境下加载npm包不同的入口文件,显然一个 main 字段已经不能够满足我们的需求,这就衍生出来了 module 与 browser 字段。

本文就来说下 这几个字段的使用场景,以及同时存在这几个字段时,他们之间的优先级。

搭建测试环境

# 创建项目
mkdir package-demo && cd package-demo && code .
# 初始化npm
npm init -y
# 安装依赖
npm i webpack-cli -D
# 初始化webpack
npx webpack init

初始化webpack image.png

根目录下的 package.json 文件如下:

{
  "name": "my-webpack-project",
  "version": "1.0.0",
  "description": "My webpack project",
  "main": "index.js",
  "scripts": {
    "serve": "webpack serve"
  },
  "keywords": [],
  "author": "",
  "license": "MIT",
  "devDependencies": {
    "@babel/core": "^7.22.5",
    "@babel/preset-env": "^7.22.5",
    "@webpack-cli/generators": "^3.0.7",
    "babel-loader": "^9.1.2",
    "html-webpack-plugin": "^5.5.3",
    "webpack": "^5.88.0",
    "webpack-cli": "^5.1.4",
    "webpack-dev-server": "^4.15.1"
  }
}

2、创建packages/pk1文件夹

├── packages
    ├── pk1
        ├── index.js
        ├── index.mjs.js
        ├── index.browser.js
        ├── package.json

// index.js
exports.log = function () {
    console.log('this is main entry')
}
// index.mjs.js
export const log = function () {
    console.log('this is module entry')
}
// index.browser.js
export const log = function () {
    console.log('this is browser entry')
}
{
  "name": "pk1",
  "version": "1.0.0",
  "main": "index.js",
  "module": "index.mjs.js",
  "browser": "index.browser.js",
  "keywords": [],
  "author": "",
  "license": "MIT"
}

3、在 src/index.cjs 和 src/index.mjs 中引入依赖包 pk1

// src/index.cjs
const { log } = require('pk1')
log()
// src/index.mjs
import { log } from 'pk1'
log()

这是我们好奇依赖包 pk1 是怎么来的?

下面我们修改修改根目录下的package.json,添加字段 workspaces,代码如下:

{
  "name": "my-webpack-project",
  "version": "1.0.0",
  "description": "My webpack project",
  "main": "index.js",
  "scripts": {
    "serve": "webpack serve"
  },
  "keywords": [],
  "author": "",
  "license": "MIT",
  "devDependencies": {
    "@babel/core": "^7.22.5",
    "@babel/preset-env": "^7.22.5",
    "@webpack-cli/generators": "^3.0.7",
    "babel-loader": "^9.1.2",
    "html-webpack-plugin": "^5.5.3",
    "webpack": "^5.88.0",
    "webpack-cli": "^5.1.4",
    "webpack-dev-server": "^4.15.1"
  },
  "workspaces": [
    "packages/*"
  ]
}

具体对 workspaces 字段解释,请参考我的博客package.json一文

配置完成后,执行npm link ./packages/pk1

测试

我们分两个场景进行测试,一个是 node环境,另一个是 浏览器环境

node 环境

回顾一下,我们当前src/index.cjs 和 src/index.mjs 代码

// src/index.cjs
const { log } = require('pk1')
log()

// src/index.mjs
import { log } from 'pk1'
log()

下面我们执行代码 node index.cjs, 发现打印出来的是 this is main entry,再执行代码 node index.mjs, 打印的还是 this is main entry,也就是我们 main 字段指定的入口。

所以得出第一个结论,在 node环境 不论用 ESMcommonjs都是优先采用 main 字段作为入口。

那么如果我们把 main 字段给删了,会不会采用 modulebrowser 字段指定的入口呢?

接下来我们删除 packages/pk1/package.json 中的 main 字段,然后接着执行 node index.cjs,发现打印出来的是 this is main entry;再执行 node index.mjs,打印结果仍是 this is main entry;最后我们删除packages/pk1/index.js,发现控制台报错了。

这样得出了第二个结论,在 node环境 只会使用 main 字段指定的入口文件,如果没有配置 main 字段,默认采用当前包根目录下的 index.js。

测试完,我们恢复删除的"main": "index.js", 恢复packages/pk1/index.js文件。

浏览器环境

首先我们启动项目npm run serve,发现控制台打印的 this is browser entry

得出第一个结论,在 浏览器环境browser 的优先级最高

接下来我们删除 packages/pk1/package.json 中的 browser 字段并重启项目,发现这次控制台打印的是 this is module entry

得出第二个结论,在 浏览器环境module 的优先级仅次于 browser。然后,我们删除 module 字段并重启项目,这次不出意外打印的是 this is main entry

webpack 的 resolve.mainFields 重新配置三个字段的优先级. 例如下面配置:

// webpack.config.js
{
    resolve: {
        mainFields: ['main', 'module', 'browser']
    },
}

我们重新测试,会发现优先级是按照 mainFields 字段配置从左往右的顺序执行

总结一下

1、在 node 环境中 不论是使用 ESMcommonjs 都只会使用 main 字段指定的入口,如果没有配置main 字段,则默认使用包根目录下的index.js,如果不存在该文件则报错。

2、在 浏览器 环境中,优先级如下 browser > module > main

3、如果配置了webpack的 resolve.mainFields,优先级被重新定义