utquidem
Version:
The meta-framework suite designed from scratch for frontend-focused modern web development.
151 lines (107 loc) • 5.3 kB
Markdown
---
sidebar_position: 7
---
# 模块的入口文件配置
对于可复用模块来说,`package.json` 是一个很重要的文件,其中包含了很多信息,例如项目的名称、依赖的模块。
本章将要介绍 `package.json` 文件中 "exports" 字段的使用以及 Modern.js 对于它做了哪些支持。
## package.json 中的 `exports` 是什么?
在一个包的 `package.json` 文件中,"main" 字段可以用于定义一个包的入口文件。这个字段在 Node.js 所有版本中都支持,并且在其他工具中(例如 webpack)中也默认支持。
虽然它已经被普遍支持,但是它提供的能力其实很有限:它仅仅只能定义一个主要的入口文件。
而 "exports" 字段提供了 "main" 的替代方案,它不仅可以定义主要的入口文件,还可以自定义入口。在自定义入口的同时还起到了封装入口的作用:使用者不可以访问未在 "exports" 中定义的入口路径以外的任何其他路径。
### 举个例子
如果有一个 **foo** 包,其中包含了 `./index.js`、`./lib.js`、`./feature.js` 三个文件,如果它的 `package.json` 定义如下:
```json
{
"name": "foo",
"exports": {
".": "./index.js",
"./lib": "./lib.js"
}
}
```
在支持 "exports" 的环境里,你可以在代码中这样导入模块 **foo** 的文件:
``` js
const foo = require('foo');
const fooLib = require('foo/lib');
```
但是你无法这样使用:
``` js
const fooFeat = require('foo/feature');
```
因为在 `package.json` 中未定义 `./feature` 入口路径。
### 关于"exports" 与 "main"之间的优先级
如果同时定义了 "exports" 和 "main","exports" 字段优先于 "main"。因此如果当 "exports" 字段存在的时候,"main" 字段的配置会被覆盖掉。
"exports" 字段不是特定作用于 ES Module 还是 CommonJS,因此不能使用 "main" 字段来作为使用 CommonJS 的回退手段。但它可以作为不支持 "exports "字段的旧版本的 Node.js 的回退手段。
因此对于上面的例子中我们就可以这样修改:
**package.json**
```
{
"name": "foo",
"main": "./index.js",
"exports": {
".": "./index.js",
"./lib": "./lib.js"
}
}
```
### 条件导出
"exports" 字段提供了条件导出的功能。通过条件导出,可以定义每个环境对应不同的模块包的入口文件,这也包括模块包被使用的时候是通过 `require` 的方式还是通过 `import` 的方式。
例如针对上面的例子进行扩展,假设该模块包提供了 ES Module 实现的文件 `wrapper.mjs` ,那么 `package.json` 会有如下的变化:
``` json
{
"name": "foo",
"main": "./index.cjs",
"exports": {
"import": "./wrapper.mjs",
"require": "./index.cjs"
}
}
```
此时如果通过 `require('foo')` 这样的方式使用该模块,则实际上导入的是 `foo/index.cjs` 文件。
而当通过 `import foo from 'foo';` 这样使用的时候,则实际导入的是 `foo/wrapper.mjs` 文件。
:::info 补充信息
关于 `.cjs` 与 `.mjs`
* `.mjs` 为后缀的文件其代码会被识别为使用了 ES Module 模块系统。
* `.cjs` 为后缀的文件其代码会被识别为使用了 CommonJS 模块系统。
:::
除了像上面使用直接映射方式之外,还可以使用嵌套条件的方式来定义 "exports",例如:
``` json
{
"main": "./main.js",
"exports": {
"node": {
"import": "./feature-node.mjs",
"require": "./feature-node.cjs"
},
"default": "./feature.mjs",
}
}
```
> "node" 用于匹配任何 Node.js 环境,可以是 CommonJS 或者是 ES Module 文件。
> "default" 用于在不满足上面任何条件情况下的回退手段。
关于更多关于 "exports" 的使用,可以阅读 [Node.js Documentation About Packages](https://nodejs.org/api/packages.html)
## 在 Modern.js 中提供的支持
对于不同的可复用模块来说,可能会有不同的(语法以及模块化)构建产物需求 Modern.js 针对该需求提供了不同形式的构建产物以满足各种场景。
在 [构建可复用模块](/docs/guides/features/modules/build) 章节中提到,默认情况下,在 JS 构建产物目录(`dist/js` )下会存在三个目录:
- `dist/js/modern`
- `dist/js/node`
- `dist/js/treeshaking`
这些目录从上到下分别对应了:`ES6 + ES Module`、`ES6 + CommonJS`、`ES5 + ES Module` 这三种构建产物。
因此如果可复用模块是用于在 Node.js 中使用,那么可以在 `package.json` 中按照如下方式进行配置:
``` json
{
"name": "node-lib",
"main": "./dist/js/node/index.js",
"exports": {
"node": {
"require": "dist/js/node/index.js",
"import": "dist/js/modern/index.js"
},
"default": "dist/js/treeshaking/index.js"
}
}
```
:::info 补充信息
对于其他环境,例如 webpack。可能存在不完全支持 "exports"的情况,因此对于在非 Node.js 环境运行的可复用模块要谨慎使用。
:::
除了默认的三种产物以外,Modern.js 还提供了多个预设配置供不同场景去选择使用。具体如何使用,可以查看 [output.packageMode](/docs/apis/config/output/package-mode) 以及 [output.packageFields](/docs/apis/config/output/pacakge-fields)。