迁移

在大多数情况下,Parcel 2 的工作方式与 Parcel 1 非常相似,但在升级时,您需要更改一些内容。

入门

#

让我们逐步了解从 Parcel 1 升级到 Parcel 2 的一些基本步骤。

包名称

#

从 Parcel 1 升级到 Parcel 2 时需要注意的第一件事是 npm 包名称已从 parcel-bundler 更改为 parcel。您需要相应地更新 package.json 中的依赖项。

package.json:
{
"devDependencies": {
"parcel-bundler": "^1.12.5"
}
}
package.json:
{
"devDependencies": {
"parcel": "^2.0.0"
}
}

您也可以使用包管理器(例如 npmyarn)来执行此操作。

yarn remove parcel-bundler
yarn add parcel --dev

缓存位置

#

Parcel 缓存的默认位置也已从 .cache 更改为 .parcel-cache。您需要修改您的 .gitignore 或类似文件以说明这一点。

.gitignore:
.cache
.gitignore:
.parcel-cache

代码更改

#

<script type="module">

#

在 Parcel 1 中,从 HTML 文件中的 <script> 标签引用的 JavaScript 文件被视为模块,支持 ES 模块和 CommonJS 语法来导入和导出值。但是,这与浏览器的实际工作方式不符,在浏览器中,“经典脚本”不支持导入和导出,顶层变量被视为全局变量。

Parcel 2 匹配浏览器行为:经典 <script> 标签不支持导入或导出。使用 <script type="module"> 元素引用模块。这也会根据您的 browserslist 自动生成 nomodule 版本,以便在较旧的浏览器中使用。有关详细信息,请参阅 差异化打包

index.html:
<!doctype html>
<html>
<head>
<script src="app.js"></script>
</head>
</html>
index.html:
<!doctype html>
<html>
<head>
<script type="module" src="app.js"></script>
</head>
</html>

注意:添加 type="module" 属性也会影响脚本的加载行为。经典脚本是“渲染阻塞”的,这意味着在脚本执行完成之前不会解析其余的 HTML 文档。模块脚本不是渲染阻塞的,并且执行会延迟到 HTML 完全解析后。Parcel 会自动插入 defer 属性以在较旧的浏览器和开发模式下匹配此行为。这意味着像 document.write 这样的功能在模块脚本中不起作用。如果您依赖这些功能,请迁移到现代 API 或继续使用经典脚本来处理应用程序的该部分。有关模块和经典脚本之间差异的更多信息,请参阅 MDN 文档。

有关经典脚本与模块脚本的更多详细信息,请参阅 经典脚本

从 JavaScript 导入非代码资产

#

在 Parcel 1 中,导入任何非 JavaScript 文件(例如图像或视频)都会导致 URL。在 Parcel 2 中,这仍然适用于图像等已知文件类型,但其他没有默认支持的文件类型需要代码更改。

在 JavaScript 中引用 URL 的首选方法是使用 URL 构造函数。但是,您也可以选择在 import 语句中的依赖项说明符前加上 url:

index.js:
import downloadUrl from "./download.zip";

document.body.innerHTML = `<a href="${downloadUrl}">Download</a>`;
const downloadUrl = new URL('download.zip', import.meta.url);

document.body.innerHTML = `<a href="${downloadUrl}">Download</a>`;

或者,您可以使用自定义 .parcelrc 来选择旧的行为。使用 @parcel/transformer-raw 插件,并使用通配符指定您需要的扩展名。

yarn add @parcel/config-default @parcel/transformer-raw --dev
.parcelrc
{
"extends": "@parcel/config-default",
"transformers": {
"*.{zip,tgz}": ["@parcel/transformer-raw"]
}
}

转译

#

Parcel 1 会自动转译您的 JavaScript 以支持一组默认的浏览器。Parcel 2 默认情况下不再进行任何转译。这意味着如果您在源代码中使用现代 JavaScript 语法,Parcel 将输出的就是这些语法。要启用转译,请在 package.json 中设置 browserslist 字段以定义您的支持浏览器目标。

package.json
{
"name": "my-project",
"browserslist": "> 0.5%, last 2 versions, not dead",
"scripts": {
"start": "parcel index.html",
"build": "parcel build index.html"
},
"devDependencies": {
"parcel": "latest"
}
}

Babel

#

与 Parcel 1 一样,Parcel 2 会自动检测 .babelrc 和其他 Babel 配置文件。但是,如果您只使用 @babel/preset-env@babel/preset-typescript@babel/preset-react,则可能不再需要 Babel。Parcel 自动支持所有这些功能,无需 Babel 配置,并且 Parcel 的默认转译器比 Babel 快得多。

如果您只使用上述预设,则可以完全删除您的 Babel 配置。这将使用 Parcel 的默认转译器,这将显着提高您的构建性能。确保在 package.json 中配置 browserslist 以匹配 @babel/preset-env 以前使用的目标。

如果您在 Babel 配置中确实有自定义预设或插件,则可以保留这些预设或插件,但删除上面列出的预设。这也会提高性能(尽管效果略微逊色)。有关详细信息,请参阅 JavaScript 文档中的 Babel

在此示例中,.babelrc 只包含 @babel/preset-env@babel/preset-react,因此可以删除它,并用 package.json 中的 browserslist 键替换它。

.babelrc:
{
"presets": [
["@babel/preset-env", {
"targets": "> 0.25%, not dead"
}],
"@babel/preset-react"
]
}
package.json:
{
"browserslist": "> 0.25%, not dead"
}

Typescript

#

Parcel 1 使用 tsc(官方 TypeScript 编译器)来转译 TypeScript。Parcel 2 现在使用 SWC,这显着提高了转译性能。

但是,默认转译器对 tsconfig.json 的支持有限。如果您使用超出 JSX 相关选项和 experimentalDecorators 的自定义编译器选项,则可以使用 @parcel/transformer-typescript-tsc 将 Parcel 的默认 TypeScript 转换器替换为 TSC。为此,请安装默认配置和 TSC 插件,并在项目的根目录中创建一个 .parcelrc 文件。

yarn add @parcel/config-default @parcel/transformer-typescript-tsc --dev
.parcelrc
{
"extends": "@parcel/config-default",
"transformers": {
"*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"]
}
}

有关使用 TypeScript 与 Parcel 的更多信息,请参阅 TypeScript 文档

Flow

#

与 Parcel 1 一样,Parcel 2 在安装 flow-bin 时会自动支持 Flow。这目前使用 @babel/preset-flow 实现。如果您有一个 Babel 配置,其中只包含该预设,则可以按照 上面 所述删除它。

与 Parcel 1 不同,您的 Babel 配置会覆盖 Parcel 2 中的默认配置,而不是与之合并。如果您有除 Flow 之外的自定义 Babel 插件,则还需要添加 @babel/preset-flow

导入 GraphQL

#

当导入 GraphQL 文件 (.gql) 时,导入仍然会解析/内联(使用 graphql-import-macro),但您现在会获得处理后的 GraphQL 查询作为字符串,而不是 Apollo AST。

DataComponent.js:
import fetchDataQuery from "./fetchData.gql"; // fetchDataQuery is the parsed AST

const DataComponent = () => {
const { data } = useQuery(fetchDataQuery, {
fetchPolicy: "cache-and-network",
});

// ...
};
DataComponent.js:
import gql from "graphql-tag";
import fetchDataQuery from "./fetchData.gql"; // fetchDataQuery is a string

// Convert to the Apollo Specific Query AST
const parsedFetchDataQuery = gql(fetchDataQuery);

const DataComponent = () => {
const { data } = useQuery(parsedFetchDataQuery, {
fetchPolicy: "cache-and-network",
});

// ...
};

借助 Parcel 2 的新插件架构,创建在构建时将字符串解析为 AST 的插件(如 Parcel 1 所做的那样)非常容易。有关详细信息,请参阅 转换器 文档。

package.json#main

#

许多 package.json 文件(例如由 npm init 生成的文件)包含一个 main 字段,该字段会被大多数工具(对于非库项目)忽略。但是,当看到 main 字段时,Parcel 会推断出您的项目是一个库,并将其用作输出路径。对于大多数 Web 应用程序,应删除此行。

package.json:
{
"main": "index.js",
"scripts": {
"start": "parcel index.html",
"build": "parcel build index.html"
}
}
package.json:
{
"scripts": {
"start": "parcel index.html",
"build": "parcel build index.html"
}
}

如果您确实需要保留 main 字段,并且希望 Parcel 忽略它,则可以在 package.json 中添加 "targets": { "main": false }。有关详细信息,请参阅 库目标

CLI

#

--target

#

在 Parcel 1 中,--target CLI 选项控制编译代码的环境。在 Parcel 2 中,这在 package.json 中配置。例如,将 engines 字段设置为包含 nodeelectron 键将相应地更改目标。

parcel build index.js --target node
package.json:
{
"engines": {
"node": "10"
}
}

您还可以在 Parcel 2 中同时为多个目标构建。有关详细信息,请参阅 目标

--experimental-scope-hoisting

#

Parcel 2 默认情况下启用了作用域提升。要禁用它,请添加 --no-scope-hoist

parcel build index.js --experimental-scope-hoisting
parcel build index.js
parcel build index.js
parcel build index.js --no-scope-hoist

--bundle-node-modules

#

要将 node_modules 中的包打包到 Node.js 中,您现在应该在目标配置中指定这一点。

parcel build index.js --target node --bundle-node-modules
package.json:
{
"targets": {
"default": {
"includeNodeModules": true
}
},
"engines": {
"node": "10"
}
}

此选项比 CLI 参数更通用。例如,您还可以选择性地包含包。有关详细信息,请参阅目标文档中的 includeNodeModules

--out-dir

#

--out-dir CLI 选项已重命名为 --dist-dir,以匹配 package.json 中的 distDir 选项。有关详细信息,请参阅 目标

parcel build index.html --out-dir www
parcel build index.html --dist-dir www

--out-file

#

--out-file CLI 选项已删除,路径应改为在 package.json 中指定。有关详细信息,请参阅 多个目标库目标

parcel build index.js --out-file lib.js
package.json:
{
"name": "my-library",
"version": "1.0.0",
"main": "lib.js"
}

--log-level

#

日志级别现在使用名称而不是数字(noneerrorwarninfoverbose)。

parcel build index.js --log-level 1
parcel build index.js --log-level error

--global

#

此选项已删除,没有替换项(目前)。

parcel build index.js --global mylib

--no-minify

#

此选项已重命名为 --no-optimize

parcel build index.js --no-minify
parcel build index.js --no-optimize

API

#

可以使用 @parcel/core 包以编程方式使用 Parcel 2,而不是 parcel-bundler。API 已发生重大更改。有关详细信息,请参阅 Parcel API

挂钩到包事件

#

Parcel 1 允许您使用 API 挂钩并监听事件,例如 buildEndbuildError。API 已更改,但您仍然可以像这样监听事件。

index.js:
import Bundler from "parcel-bundler"

const bundler = new Bundler({ /* ... */ })
bundler.bundle()

bundler.on("buildEnd", () => { /* ... */ })
bundler.on("buildError", (error) => { /* ... */ })
import Parcel from "@parcel/core"

const bundler = new Parcel({ /* ... */ })

bundler.watch((err, buildEvent) => {
if (buildEvent.type === "buildSuccess") { /* ... */ }
if (buildEvent.type === "buildFailure") { /* ... */ }
})

插件

#

Parcel 2 中的插件系统已完全更改。Parcel 1 插件与 Parcel 2 不兼容。有关 Parcel 2 插件 API 的详细信息,请参阅 插件系统

使用插件

#

在 Parcel 1 中,将插件安装到您的项目中并在 package.json 依赖项中列出它们会自动启用它们。在 Parcel 2 中,插件在 .parcelrc 中配置。有关详细信息,请参阅 Parcel 配置