迁移

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

¥For the most part, Parcel 2 works quite similarly to Parcel 1, but there are a few things you’ll need to change when upgrading.

入门

#

¥Getting started

让我们逐步完成从 Parcel 1 升级到 Parcel 2 的几个基本步骤。

¥Let's walk through a couple basic steps to upgrade from Parcel 1 to Parcel 2.

包名字

#

¥Package name

从 Parcel 1 升级到 Parcel 2 时首先要注意的是,npm 包名称已从 parcel-bundler 更改为 parcel。你需要相应地更新 package.json 中的依赖。

¥The first thing to note when upgrading from Parcel 1 to Parcel 2 is that the npm package name has changed from parcel-bundler to parcel. You'll need to update the dependencies in your package.json accordingly.

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

你还可以使用包管理器来执行此操作,例如 npmyarn

¥You can also do this by using your package manager, e.g. npm or yarn.

yarn remove parcel-bundler
yarn add parcel --dev

缓存位置

#

¥Cache location

Parcel 缓存的默认位置也从 .cache 更改为 .parcel-cache。你需要修改你的 .gitignore 或类似内容以解决此问题:

¥The default location of the Parcel cache has also changed from .cache to .parcel-cache. You'll need to modify your .gitignore or similar to account for this:

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

代码变更

#

¥Code Changes

<script type="module">

#

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

¥In Parcel 1, JavaScript files referenced from a <script> tag in an HTML file were treated as modules, supporting both ES module and CommonJS syntax for importing and exporting values. However, this did not match how browsers actually work, where "classic scripts" do not support imports and exports and top-level variables are treated as globals.

Parcel 2 与浏览器行为匹配:经典 <script> 标签不支持导入或导出。使用 <script type="module"> 元素引用模块。这还将自动为旧浏览器生成 nomodule 版本,具体取决于你的 browserslist。详情请参见 差异化打包

¥Parcel 2 matches browser behavior: classic <script> tags do not support imports or exports. Use a <script type="module"> element to reference a module. This will also automatically generate a nomodule version as well for older browsers, depending on your browserslist. See Differential bundling for details.

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 上的文档,了解有关模块和经典脚本之间差异的更多信息。

¥Note: Adding the type="module" attribute also affects the loading behavior of scripts. Classic scripts are "render blocking", meaning the rest of the HTML document is not parsed until script execution is complete. Module scripts are not render blocking, and execution is deferred until the HTML is fully parsed. Parcel inserts the defer attribute automatically to match this behavior in older browsers and in development mode. This means features like document.write do not work in module scripts. If you are relying on these features, either migrate to modern APIs or continue using a classic script for that part of your app. See the docs on MDN to learn more about the differences between modules and classic scripts.

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

¥See Classic scripts for more details about classic scripts vs module scripts.

从 JavaScript 导入非代码资源

#

¥Importing non-code assets from JavaScript

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

¥In Parcel 1, importing any non-JavaScript file such as an image or video resulted in a URL. In Parcel 2, this still works for known file types such as images, but other file types without default support will require code changes.

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

¥The preferred approach for referencing URLs in JavaScript is to use the URL constructor. However, you may also choose to prefix the dependency specifier in an import statement with 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 选择旧行为。使用带有 glob 的 @parcel/transformer-raw 插件来获取你需要的扩展。

¥Alternatively, you can use a custom .parcelrc to opt into the old behavior. Use the @parcel/transformer-raw plugin with a glob for the extensions you need.

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

转译

#

¥Transpilation

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

¥Parcel 1 automatically transpiled your JavaScript to support a default set of browsers. Parcel 2 no longer does any transpilation by default. This means if you use modern JavaScript syntax in your source code, that's what Parcel will output. To enable transpilation, set the browserslist field in your package.json to define your supported browser targets.

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 快得多。

¥Like Parcel 1, Parcel 2 automatically detects .babelrc and other Babel config files. However, if you're only using @babel/preset-env, @babel/preset-typescript, and @babel/preset-react, Babel may no longer be necessary. Parcel supports all of these features automatically without a Babel config, and Parcel's default transpiler is much faster than Babel.

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

¥If you only use the above presets, you can delete your Babel config entirely. This will use Parcel's default transpiler instead, which should improve your build performance significantly. Make sure to configure browserslist in your package.json to match the targets previously used by @babel/preset-env.

如果你的 Babel 配置中有自定义预设或插件,你可以保留它们,但删除上面列出的预设。这也应该会提高性能(尽管稍差一些)。有关更多详细信息,请参阅 JavaScript 文档中的 Babel

¥If you do have custom presets or plugins in your Babel config, you can keep those but remove the presets listed above. This should also improve performance (albeit a bit less). See Babel in the JavaScript docs for more details.

在本例中,.babelrc 仅包含 @babel/preset-env@babel/preset-react,因此可以将其删除,并用 package.json 中的 browserslist 密钥替换。

¥In this example, .babelrc contains only @babel/preset-env and @babel/preset-react, so it can be deleted, and replaced with a browserslist key in package.json.

.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,这显着提高了转译性能。

¥Parcel 1 transpiled TypeScript using tsc (the official TypeScript compiler). Parcel 2 now uses SWC instead, which improves transpilation performance significantly.

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

¥However, the default transpiler has limited support for tsconfig.json. If you use custom compiler options beyond the JSX-related options and experimentalDecorators, you can replace Parcel's default TypeScript transformer with TSC using @parcel/transformer-typescript-tsc. To do this, install the default config and the TSC plugin and create a .parcelrc file in the root of your project.

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 文档

¥See the TypeScript docs for more information on using TypeScript with Parcel.

Flow

#

就像 Parcel 1 一样,Parcel 2 在安装 flow-bin 时自动支持 Flow。目前这是使用 @babel/preset-flow 实现的。如果你的 Babel 配置仅包含该预设,则可以按照 above 的描述将其删除。

¥Just like Parcel 1, Parcel 2 supports Flow automatically when flow-bin is installed. This is currently implemented using @babel/preset-flow. If you have a Babel config with only that preset, it can be removed as described above.

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

¥Unlike Parcel 1, your Babel config overrides the default in Parcel 2 rather than being merged into it. If you have custom Babel plugins other than Flow, you'll need to add @babel/preset-flow as well.

导入 GraphQL

#

¥Importing GraphQL

导入 GraphQL 文件 (.gql) 时,导入仍会解析/内联(使用 graphql-import-macro),但你现在以字符串形式获取处理后的 GraphQL 查询,而不是 Apollo AST。

¥When import GraphQL files (.gql), imports are still resolved/inlined (using graphql-import-macro), but you now get the processed GraphQL query as a string instead of an 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 所做的那样)非常容易。有关详细信息,请参阅 转换器 文档。

¥With Parcel 2's new plugin architecture, creating a plugin that parses the string into an AST at build time (as Parcel 1 did) is very easy. See the Transformer docs for details.

package.json#main

#

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

¥Many package.json files (e.g. the one generated by npm init) contain a main field, which is ignored by most tools (for non-library projects). However, when a main field is seen, Parcel infers that your project is a library and uses it as the output path. For most web apps, this line should be removed.

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 忽略它,你可以将 "targets": { "main": false } 添加到 package.json 中。详情请参见 库目标

¥If you do need to keep the main field, and want Parcel to ignore it, you can add "targets": { "main": false } to your package.json. See Library targets for details.

CLI

#

--target

#

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

¥In Parcel 1, the --target CLI option controlled which environment your code was compiled for. In Parcel 2, this is configured in package.json instead. For example, setting the engines field to include a node or electron key will change the target accordingly.

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

你还可以在 Parcel 2 中同时构建多个目标。详情请参见 目标

¥You can also build for multiple targets simultaneously in Parcel 2. See Targets for details.

--experimental-scope-hoisting

#

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

¥Parcel 2 has scope hoisting enabled by default. To disable it, add --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.js 为目标时打包 node_modules 中的包,你现在应该在目标配置中指定:

¥To bundle packages from node_modules when targetting Node.js, you now should specify that in the target configuration:

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

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

¥This option is more versatile than the CLI parameter. For example, you can also selectively include packages. see includeNodeModules in the Targets docs for details.

--out-dir

#

--out-dir CLI 选项已重命名为 --dist-dir,以匹配 package.json 中的 distDir 选项。详情请参见 目标

¥The --out-dir CLI option was renamed to --dist-dir to match the distDir option in package.json. See Targets for details.

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

--out-file

#

--out-file CLI 选项已删除,路径应在 package.json 中指定。详情请参见 多个目标库目标

¥The --out-file CLI option was removed, and the path should instead be specified in package.json. See Multiple targets and Library targets for details.

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

--log-level

#

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

¥The log levels now have names instead of numbers (none, error, warn, info, verbose).

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

--global

#

此选项已被删除且没有替代(目前)。

¥This option has been removed without a replacement (for now).

parcel build index.js --global mylib

--no-minify

#

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

¥This option has been renamed to --no-optimize.

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

API

#

可以通过 @parcel/core 包(而不是 parcel-bundler)以编程方式使用 Parcel 2。API 发生了显着变化。详情请参见 Parcel API

¥Using Parcel 2 programmatically is possible through the @parcel/core package, rather than parcel-bundler. The API has changed significantly. See Parcel API for details.

钩子打包事件

#

¥Hooking into Bundle Events

Parcel 1 让你可以使用 API 钩子并监听 buildEndbuildError 等事件。API 已更改,但你仍然可以监听事件,如下所示:

¥Parcel 1 let you hook in and listen to events like buildEnd or buildError using the API. The API has changed but you can still listen for events like so:

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") { /* ... */ }
})

插件

#

¥Plugins

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

¥The plugin system has been completely changed in Parcel 2. Parcel 1 plugins are not compatible with Parcel 2. See Plugin System for details about the Parcel 2 plugin APIs.

使用插件

#

¥Using plugins

在 Parcel 1 中,将插件安装到项目中并将它们列在 package.json 依赖中会自动启用它们。在 Parcel 2 中,插件在 .parcelrc 中配置。详情请参见 Parcel 配置

¥In Parcel 1, installing plugins into your project and listing them in package.json dependencies enabled them automatically. In Parcel 2, plugins are configured in .parcelrc. See Parcel configuration for details.

Parcel 中文网 - 粤ICP备13048890号