依赖解析

当 Parcel 构建您的源代码时,它会发现依赖项,这允许将代码分解为单独的文件并在多个地方重用。依赖项描述了在哪里可以找到包含您所依赖的代码的文件,以及有关如何构建它的元数据。

依赖说明符

#

依赖项说明符是一个字符串,它描述了依赖项相对于导入它的文件的位置。例如,在 JavaScript 中,import语句或require函数可用于创建依赖关系。在 CSS 中,@importurl()可以使用。通常,这些依赖项不指定完整的绝对路径,而是指定一个较短的说明符,由 Parcel 和其他工具解析为绝对路径。

Parcel 实现了 Node module resolution algorithm的增强版本。它负责将依赖说明符转换为可以从文件系统加载的绝对路径。除了许多工具支持的标准依赖说明符外,Parcel 还支持一些附加说明符类型和功能。

相对说明符

#

相对说明符以...开头,并解析相对于导入文件的文件。

/path/to/project/src/client.js:
import "./utils.js";
import "../constants.js";

在上面的示例中,第一个导入将解析为 /path/to/project/src/utils.js,第二个将解析为 /path/to/project/constants.js

文件扩展名

#

建议在所有导入说明符中包含完整的文件扩展名。这既提高了依赖性解析性能,又减少了歧义。

也就是说,为了与 Node 中的 CommonJS 和 TypeScript 兼容,Parcel 允许为某些文件类型省略文件扩展名。可以省略的文件扩展名包括 .ts, .tsx, .js, .jsx, 和 .json。导入所有其他文件类型需要文件扩展名。

以下示例解析为与上述相同的文件。

/path/to/project/src/client.js:
import "./utils";
import "../constants";

请注意,只有在从 JavaScript 或 TypeScript 文件导入时,才能省略这些。在 HTML 和 CSS 等其他文件类型中定义的依赖项始终需要文件扩展名。

目录索引文件

#

在 JavaScript、Typescript 和其他基于 JS 的语言中,依赖说明符可能会解析为目录而不是文件。如果目录包含一个package.json文件,主条目将按照Package entries部分中的说明进行解析。如果package.json不存在, 它将尝试解析到目录中的索引文件,例如index.jsindex.ts。索引文件支持上面列出的所有扩展名。

/path/to/project/src/app.js:
import "./client";

例如,如果/path/to/project/src/client是目录,则上述说明符可以解析为/path/to/project/src/client/index.js

裸说明符

#

裸说明符以除., /, or ~之外的任何字符开头。 在 JavaScript、TypeScript 和其他基于 JS 的语言中,它们解析为node_modules。对于其他类型的文件,例如 HTML 和 CSS,裸说明符的处理方式与relative specifiers相同。

/path/to/project/src/client/index.js:
import "react";

在上面的示例中,react可能会解析为/path/to/project/node_modules/react/index.js。确切的位置将取决于node_modules目录的位置以及包内的配置。

node_modules从导入文件向上搜索目录。搜索在项目根目录处停止。例如,如果导入文件位于/path/to/project/src/client/index.js以下位置,则将被搜索:

一旦找到模块目录,就解析了包条目。有关此过程的更多详细信息,请参阅Package entries

子路径包

#

裸说明符也可以指定包内的子路径。例如,一个包可能会发布多个入口点,而不仅仅是一个入口点。

import "lodash/clone";

上面的示例lodash在如上所述的node_modules目录中解析,然后clone在包中解析模块而不是其主入口点。例如,这可能是一个node_modules/lodash/clone.js文件。

内置模块

#

Parcel 包括许多内置 Node.js 模块的垫片,例如pathurl。当依赖说明符引用这些模块名称之一时,内置模块优先于任何安装的node_modules 具有相同名称的模块。在为节点环境构建时,内置模块会从包中排除,否则会包含一个 shim。有关内置模块的完整列表,请参阅Node docs文档。

在为 Electron 环境构建时,该electron模块也被视为内置模块并从包中排除。

绝对路径

#

绝对路径以/开头, 并解析相对于项目根目录的文件。项目根目录是项目的基本目录,通常包含包管理器锁定文件(例如yarn.lockpackage-lock.json)或源代码控制目录(例如.git)。绝对说明符对于避免深度嵌套层次结构中非常长的相对路径可能很有用。

import "/src/client.js";

上面的示例可以放在项目目录结构中的任何位置的任何文件中,并且始终解析/path/to/project/src/client.js

波浪符说明符

#

波浪符说明符以~开头,并相对于导入文件中最近的项目根目录进行解析。项目根目录是一个包含文件package.json的目录,通常可以在改目录中找到node_modules,波浪符说明符可用于与绝对说明符类似的目的,但当您拥有多个包时更有用。

/path/to/project/packages/frontend/src/client/index.js:
import "~/src/utils.js";

上面的示例将解析为/path/to/project/packages/frontend/src/utils.js

查询参数

#

依赖说明符还可以包括查询参数,这些参数指定解析文件的转换选项。例如,您可以指定宽度和高度以在加载图像时调整图像大小。

.logo {
background: url(logo.png?width=400&height=400);
}

有关图像的更多详细信息,请参阅Image transformer图片转换器文档。您还可以在自定义Transformer插件中使用查询参数。

注意: CommonJS 说明符(由require函数创建)不支持查询参数。

URL 方案

#

依赖说明符可以使用 URL 方案来定位Named pipelines。这些允许您指定不同的管道来编译文件,而不是默认管道。例如,该bundle-text:方案可用于将已编译的捆绑包内联为文本。有关更多详细信息,请参阅Bundle inlining

有一些保留的 URL 方案可能不会用于命名管道,并且具有内置行为。

全局说明符

#

Parcel 支持通过 glob 一次导入多个文件,但是,由于 glob 导入是非标准的,因此它们不包含在默认 Parcel 配置中。要启用它们,请添加@parcel/resolver-glob到您的.parcelrc

.parcelrc:
{
"extends": "@parcel/config-default",
"resolvers": ["@parcel/resolver-glob", "..."]
}

启用后,您可以使用诸如./files/*.js。这将返回一个对象,其键对应于文件名。

import * as files from "./files/*.js";

相当于:

import * as foo from "./files/foo.js";
import * as bar from "./files/bar.js";

let files = {
foo,
bar,
};

具体来说,glob 模式的动态部分成为对象的键。如果有多个动态部分,将返回一个嵌套对象。例如,如果pages/profile/index.js文件存在,则以下内容将匹配它。

import * as pages from "./pages/*/*.js";

console.log(pages.profile.index);

这也适用于 URL 方案bundle-text:,如 ,以及动态导入。使用动态导入时,生成的对象将包括文件名到函数的映射。可以调用每个函数来加载已解析的模块。这意味着每个文件都是按需加载的,而不是全部预先加载的。

let files = import("./files/*.js");

async function doSomething() {
let foo = await files.foo();
let bar = await files.bar();
return foo + bar;
}

Globs 也可用于从 npm 包中导入文件:

import * as locales from "@company/pkg/i18n/*.js";

console.log(locales.en.message);

Glob 导入也适用于 CSS:

@import "./components/*.css";

相当于:

@import "./components/button.css";
@import "./components/dropdown.css";

包目录

#

解析包目录时,package.json会查阅该文件以确定包条目。Parcel 检查以下字段(按顺序):

如果这些字段均未设置,或者它们指向的文件不存在,则解析回退到索引文件。有关详细信息,请参阅目录索引文件(Directory index files)

别名 Aliases

#

别名可用于覆盖依赖项的正常解析。例如,您可能希望使用不同但与 API 兼容的替换来覆盖模块,或者将依赖项映射到由从 CDN 加载的库定义的全局变量。

别名是通过 package.json 中的alias定义的。它们可以在最接近package.json包含依赖项的源文件中本地定义,也可以package.json在项目根目录中全局定义。全局别名适用于项目中的所有文件和包,包括node_modules

包别名

#

包别名将node_modules依赖项映射到不同的包或项目中的本地文件。例如,要在项目中将reactreact-dom 替换为 Preact 您可以在项目根目录package.json中定义全局别名。

package.json:
{
"alias": {
"react": "preact/compat",
"react-dom": "preact/compat"
}
}

您还可以使用定义别名的相对路径将模块映射到项目中的package.json文件。

package.json:
{
"alias": {
"react": "./my-react.js"
}
}

也支持仅对模块的某些子路径 sub-paths进行别名。此示例将别名lodash/clonetiny-clonelodash包中的其他导入将不受影响。

package.json:
{
"alias": {
"lodash/clone": "tiny-clone"
}
}

这也适用于另一种方式:如果整个模块有别名,则该包的任何子路径导入都将在别名模块中解析。例如,如果您使用别名将lodashmy-lodash,并且导入lodash/clone,这将解析为my-lodash/clone

文件别名

#

别名也可以定义为相对路径,以用不同的文件替换包中的特定文件。这可以使用alias字段来无条件地替换文件,或者使用sourcebrowser字段来有条件地完成。有关这些字段的详细信息,请参阅上面包目录

例如,要将某个文件替换为特定于浏览器的版本,您可以使用该browser字段。

package.json:
{
"browser": {
"./fs.js": "./fs-browser.js"
}
}

现在,如果my-module/fs.js在浏览器环境中导入,它们实际上会得到my-module/fs-browser.js. 这既适用于从外部(例如子路径 package sub-paths)的导入,也适用于模块内部的导入。

全局别名

#

文件别名也可以使用 glob 定义,它允许使用单个模式替换许多文件。替换可以包括诸如$1访问捕获的全局匹配的模式。这可以使用alias字段来无条件地替换文件,或者使用sourcebrowser字段来有条件地完成。有关这些字段的详细信息,请参阅上面的包目录 Package entries

例如,您可以使用source字段来提供包中已编译代码与原始源代码之间的映射。当模块被符号链接时,或者在 monorepo 中,这将允许 Parcel 从源代码编译模块,而不是使用预编译版本。

package.json:
{
"source": {
"./dist/*": "./src/$1"
}
}

现在,每次导入目录中的src文件时,都会加载文件夹 dist 中的相应文件。

垫片别名

#

文件或包可以别名为false从构建中排除,并替换为空模块。例如,这对于从仅在 Node.js 中工作的浏览器构建中排除某些模块可能很有用。

package.json:
{
"alias": {
"fs": false
}
}

全局别名

#

文件或包也可以别名为将在运行时定义的全局变量。例如,可以从 CDN 加载特定库。而不是捆绑它,只要解决了对该库的依赖关系,它将被替换为对该全局变量的引用,而不是被捆绑。

这可以通过为具有global属性的对象创建别名来完成。以下示例将jquery依赖说明符别名为全局变量$

package.json:
{
"alias": {
"jquery": {
"global": "$"
}
}
}

配置其他工具

#

本节介绍如何配置其他工具以使用 Parcel 对节点解析算法的扩展。

TypeScript

#

需要将 TypeScript 配置为支持 Parcel 功能,例如绝对和波浪号依赖说明符以及别名。这可以使用 中的paths选项来完成tsconfig.json。有关更多信息,请参阅TypeScript 模块解析文档。

例如,要将波浪号路径映射到根目录,可以使用以下配置:

tsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"~*": ["./*"]
}
}
}

启用对URL schemes的支持,可以通过在项目中创建ambient module 例如,要将随方案加载的依赖项映射到字符串,您可以使用以下声明。这可以放置在文件parcel.d.ts中,例如项目中的任何位置。

parcel.d.ts:
declare module "bundle-text:*" {
const value: string;
export default value;
}

Flow

#

Flow 需要配置为支持绝对和波浪符说明符以及别名。这可以使用您module.name_mapper.flowconfig.

例如,要映射绝对说明符以从项目根目录解析,可以使用此配置:

.flowconfig:
[options]
module.name_mapper='^\/\(.*\)$' -> '<PROJECT_ROOT>/\1'

要启用url 方案 URL schemes您需要创建到导出预期类型的.flow描述文件 declaration file。例如,要将随bundle-text:方案加载的依赖项映射到字符串,您可以创建一个名为的文件bundle-text.js.flow,并将引用该方案的所有依赖项映射到它。

bundle-text.js.flow:
// @flow
declare var value: string;
export default value;
.flowconfig:
[options]
module.name_mapper='^bundle-text:.*$' -> '<PROJECT_ROOT>/bundle-text.js'