Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

package.json 中 你还不清楚的 browser,module,main 字段优先级 #8

Open
SunshowerC opened this issue Jan 5, 2019 · 20 comments

Comments

@SunshowerC
Copy link
Owner

SunshowerC commented Jan 5, 2019

browser VS module VS main

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

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

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

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

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

文件优先级

在说 package.json 之前,先说下文件优先级

由于我们使用的模块规范有 ESM 和 commonJS 两种,为了能在 node 环境下原生执行 ESM 规范的脚本文件,.mjs 文件就应运而生。

当存在 index.mjsindex.js 这种同名不同后缀的文件时,import './index' 或者 require('./index') 是会优先加载 index.mjs 文件的。

也就是说,优先级 mjs > js

browsermodulemain 字段

字段定义

  • main : 定义了 npm 包的入口文件,browser 环境和 node 环境均可使用
  • module : 定义 npm 包的 ESM 规范的入口文件,browser 环境和 node 环境均可使用
  • ·browser : 定义 npm 包在 browser 环境下的入口文件

使用场景与优先级

首先,我们假定 npmtest 有以下目录结构

----- 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 种情况。

下面说下具体使用场景。

webpack + web + ESM

这是我们最常见的使用场景,通过 webpack 打包构建我们的 web 应用,模块语法使用 ESM

当我们加载

import test from 'test'

实际上的加载优先级是 browser = browser+mjs > module > browser+cjs > main
也就是说 webpack 会根据这个顺序去寻找字段指定的文件,直到找到为止。

然而实际上的情况可能比这个更加复杂,具体可以参考流程图
image

webpack + web + commonJS

const test = require('test')

事实上,构建 web 应用时,使用 ESM 或者 commonJS 模块规范对于加载优先级并没有任何影响

优先级依然是 browser = browser+mjs > module > browser+cjs > main

webpack + node + ESM/commonJS

我们清楚,使用 webpack 构建项目的时候,有一个 target 选项,默认为 web,即进行 web 应用构建。

当我们需要进行一些 同构项目,或者其他 node 项目的构建的时候,我们需要将 webpack.config.jstarget 选项设置为 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

import test from 'test'

只有 main 字段有效。

总结

  • 如果 npm 包导出的是 ESM 规范的包,使用 module
  • 如果 npm 包只在 web 端使用,并且严禁在 server 端使用,使用 browser。
  • 如果 npm 包只在 server 端使用,使用 main
  • 如果 npm 包在 web 端和 server 端都允许使用,使用 browser 和 main
  • 其他更加复杂的情况,如npm 包需要提供 commonJS 与 ESM 等多个规范的代码文件,请参考上述使用场景或流程图
@SunshowerC SunshowerC changed the title package.json 中 你可能还不清楚的 browser,module,main 字段优先级 package.json 中 你还不清楚的 browser,module,main 字段优先级 Jan 7, 2019
@terrilltang
Copy link

这个可有什么官方文档出处?
看 npm 的官方文档上只有以下两种。
https://docs.npmjs.com/files/package.json#main
https://docs.npmjs.com/files/package.json#browser

@SunshowerC
Copy link
Owner Author

@terrilltang
这里面的 module 其实是一个 proposal, 但是由于广泛被使用,所以这个字段已经被官方采纳落实了。可以在 这里 找到对应的官方说明。

至于这几个字段的优先级,我好像没找到详细的官方说明,所以我才自己测试下进行归纳总结。

@tianyn1990
Copy link

tianyn1990 commented Sep 17, 2019

webpack/webpack#5673 (comment)

另外这里有webpack的部分代码(搜 browser): https://github.com/webpack/webpack/blob/master/lib/WebpackOptionsDefaulter.js

按我的理解,正常来说main指向cjs,module指向mjs,当一个包兼容浏览器端与node端,但output不同时(比如同构应用),才使用 browser 指向兼容浏览器的版本,这种情况main和module两个都需要兼容版本,因此browser支持对象的格式提供一个映射关系。

@Yayure
Copy link

Yayure commented Sep 23, 2019

测了下webpack的模块选择优先级可以根据resolve.mainFields配置的顺序进行调整,参见https://webpack.docschina.org/configuration/resolve/#resolve-mainfields

@gitHber
Copy link

gitHber commented Apr 29, 2020

试了browser,貌似只能使用string,不能使用对象

@colgin
Copy link

colgin commented Sep 11, 2020

@SunshowerC 请问博主有考虑webpack resolve.mainFields配置吗。
按照文档,浏览器环境下,解析顺序是 browser > module > main。但是看上面的图,会首先看module 是否有值,如果没有值的话,就算browser 有值,也会用main。这似乎有些出入?
其次,不知道图中的顺序是基于webpack resolve.mainFields什么顺序得出的结论?

@SunshowerC
Copy link
Owner Author

SunshowerC commented Sep 13, 2020

@SunshowerC 请问博主有考虑webpack resolve.mainFields配置吗。
按照文档,浏览器环境下,解析顺序是 browser > module > main。但是看上面的图,会首先看module 是否有值,如果没有值的话,就算browser 有值,也会用main。这似乎有些出入?
其次,不知道图中的顺序是基于webpack resolve.mainFields什么顺序得出的结论?

@colgin 流程图确实不够全,实际上不存在 module 时会再判断有没有 browser 的,解析顺序是 browser > module > main,所以有 browser 无 module 的场景下是用 browser 的。
如果自己的项目有用到 webpack 的,可以通过自行配置 webpack -> mainFields 改变其加载优先级 。
如果没用 webpack 的,可以参考上述默认的加载优先级。

@Zippowxk
Copy link

问一个看似不大相关的问题,
假设:test模块打包出的esm规范的文件 test.esm.js
有下面三种引用方式:

  1. 发布到npm,在目标工程中添加依赖,使用import test from 'test'直接引入
  2. 使用import test from './node_modules/test/dist/test.esm.js'引入
  3. 把打包后的文件test.esm.js 拷贝到目标工程直接引用 import test from './test.esm.js'
    这三种引入方式 在目标工程打包时会不会有区别,我发现2 3 两种情况有可能会报错

@senfish
Copy link

senfish commented Apr 28, 2021

@Zippowxk 第三种方式可能会报错,你项目工程里面不一定有你test.esm.js的运行环境。

@Zippowxk
Copy link

@Zippowxk 第三种方式可能会报错,你项目工程里面不一定有你test.esm.js的运行环境。

有道理 但是第二种方式报错,就是因为打包环境不一致导致的吗?比如eslint 和 babel的preset不一致

helloint pushed a commit to hint-test/test-rollup that referenced this issue Aug 25, 2021
…) format, unless you set `type: 'module'` in package.json, then it become a ES modules (esm)

We use 'cjs' format for 'main' since it is by default, then use 'esm' format for 'module'
For the file name, 'cjs' to be '.js', 'esm' to be '.mjs' (reference: SunshowerC/blog#8)
Set 'browser' with 'umd' format, for browser use (maybe through https://unpkg.com/).
@BoBoooooo
Copy link

mark

@NameWjp
Copy link

NameWjp commented Dec 29, 2021

讲的很清楚,谢谢

@arleyGuoLei
Copy link

m

@easy1090
Copy link

easy1090 commented Apr 7, 2022 via email

@woow-wu7
Copy link

总结的时有错误:

错误:如果 npm 包在 web 端和 server 端都允许使用,使用 browser 和 main
改正:如果 npm 包在 web 端和 server 端都允许使用,使用 module 和 main

@ying-bin
Copy link

Mark

@easy1090
Copy link

easy1090 commented Nov 25, 2022 via email

@oneyoung19
Copy link

前排围观

@iocscix
Copy link

iocscix commented Jan 31, 2024

”总结的时有错误“可以修改总结部分吗

@SirHe
Copy link

SirHe commented Apr 8, 2024

也就是说,优先级 mjs > js,这个结论是错误的

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests