从 Parcel 1 迁移
总的来说,Parcel 2 的工作方式与 Parcel 1 非常相似,但在升级时您需要更改一些内容。
入门
#让我们一起走过从 Parcel 1 升级到 Parcel 2 的几个基本步骤。
包名
#从 Parcel 1 升级到 Parcel 2 时需要注意的第一件事是,npm 包名已从 parcel-bundler
更改为 parcel
。您需要相应地更新 package.json
中的依赖。
{
"devDependencies": {
"parcel-bundler": "^1.12.5"
}
}
{
"devDependencies": {
"parcel": "^2.0.0"
}
}
您也可以使用包管理器,如 npm
或 yarn
。
yarn remove parcel-bundler
yarn add parcel --dev
缓存位置
#Parcel 缓存的默认位置也从 .cache
更改为 .parcel-cache
。您需要修改 .gitignore
或类似文件以适应这一变化:
.cache
.parcel-cache
代码变更
#<script type="module">
#在 Parcel 1 中,HTML 文件中 <script>
标签引用的 JavaScript 文件被视为模块,支持导入和导出值的 ES 模块和 CommonJS 语法。然而,这与浏览器的实际工作方式不符,其中"经典脚本"不支持导入和导出,并且顶层变量被视为全局变量。
Parcel 2 匹配浏览器行为:经典 <script>
标签不支持导入或导出。使用 <script type="module">
元素来引用模块。这还将根据您的 browserslist
自动为旧版浏览器生成 nomodule
版本。有关详细信息,请参见差异化捆绑。
<!doctype html>
<html>
<head>
<script src="app.js"></script>
</head>
</html>
<!doctype html>
<html>
<head>
<script type="module" src="app.js"></script>
</head>
</html>
note:添加 type="module"
属性还会影响脚本的加载行为。经典脚本是"渲染阻塞"的,这意味着在脚本执行完成之前不会解析 HTML 文档的其余部分。模块脚本不会阻塞渲染,并且在 HTML 完全解析后执行。Parcel 会自动插入 defer
属性,以在旧版浏览器和开发模式下匹配此行为。这意味着 document.write
等功能在模块脚本中不起作用。如果您依赖这些功能,请迁移到现代 API 或继续为应用的该部分使用经典脚本。请参阅 MDN 文档了解有关模块脚本和经典脚本之间差异的更多信息。
有关经典脚本和模块脚本的更多详细信息,请参见经典脚本。
从 JavaScript 导入非代码资源
#在 Parcel 1 中,导入任何非 JavaScript 文件(如图像或视频)都会生成一个 URL。在 Parcel 2 中,对于图像等已知文件类型,这仍然有效,但对于没有默认支持的其他文件类型,需要进行代码更改。
在 JavaScript 中引用 URL 的首选方法是使用 URL 构造函数。但是,您也可以选择在 import
语句的依赖说明符前加上 url:
前缀。
import downloadUrl from "./download.zip";
document.body.innerHTML = `<a href="${downloadUrl}">下载</a>`;
const downloadUrl = new URL('download.zip', import.meta.url);
document.body.innerHTML = `<a href="${downloadUrl}">下载</a>`;
或者,您可以使用自定义的 .parcelrc
来选择旧行为。使用 @parcel/transformer-raw
插件和所需扩展名的通配符。
yarn add @parcel/config-default @parcel/transformer-raw --dev
{
"extends": "@parcel/config-default",
"transformers": {
"*.{zip,tgz}": ["@parcel/transformer-raw"]
}
}
转译
#Parcel 1 会自动将 JavaScript 转译为支持默认浏览器集的代码。Parcel 2 默认不进行任何转译。这意味着如果您在源代码中使用现代 JavaScript 语法,Parcel 将原样输出。要启用转译,请在 package.json
中设置 browserslist
字段以定义支持的浏览器目标。
{
"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 自动支持所有这些功能,并且 Parcel 的默认转译器比 Babel 快得多。
如果您仅使用上述预设,可以完全删除 Babel 配置。这将使用 Parcel 的默认转译器,这应该显著提高构建性能。确保在 package.json
中配置 browserslist
,以匹配之前由 @babel/preset-env
使用的目标。
如果您的 Babel 配置中有自定义预设或插件,可以保留这些预设,但删除上面列出的预设。这也应该提高性能(尽管提升较少)。有关更多详细信息,请参见 JavaScript 文档中的 Babel。
在此示例中,.babelrc
仅包含 @babel/preset-env
和 @babel/preset-react
,因此可以删除,并在 package.json
中替换为 browserslist
键。
{
"presets": [
["@babel/preset-env", {
"targets": "> 0.25%, not dead"
}],
"@babel/preset-react"
]
}
{
"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
{
"extends": "@parcel/config-default",
"transformers": {
"*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"]
}
}
有关使用 Parcel 处理 TypeScript 的更多信息,请参见 TypeScript 文档。
Flow
#就像 Parcel 1 一样,当安装 flow-bin
时,Parcel 2 会自动支持 Flow。这目前是使用 @babel/preset-flow
实现的。如果您的 Babel 配置中只有该预设,可以按照上文所述将其删除。
与 Parcel 1 不同,在 Parcel 2 中,您的 Babel 配置会覆盖默认配置,而不是合并到默认配置中。如果您有除 Flow 之外的自定义 Babel 插件,还需要添加 @babel/preset-flow
。
导入 GraphQL
#导入 GraphQL 文件(.gql
)时,导入仍然被解析/内联(使用 graphql-import-macro
),但现在得到的是处理后的 GraphQL 查询字符串,而不是 Apollo AST。
import fetchDataQuery from "./fetchData.gql"; // fetchDataQuery 是解析后的 AST
const DataComponent = () => {
const { data } = useQuery(fetchDataQuery, {
fetchPolicy: "cache-and-network",
});
// ...
};
import gql from "graphql-tag";
import fetchDataQuery from "./fetchData.gql"; // fetchDataQuery 是一个字符串
// 转换为 Apollo 特定的查询 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 应用,应删除这一行。
{
"main": "index.js",
"scripts": {
"start": "parcel index.html",
"build": "parcel build index.html"
}
}
{
"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
中配置。例如,设置包含 node
或 electron
键的 engines
字段将相应地更改目标。
parcel build index.js --target node
{
"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.js 时捆绑 node_modules
中的包,现在应在目标配置中指定:
parcel build index.js --target node --bundle-node-modules
{
"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
{
"name": "my-library",
"version": "1.0.0",
"main": "lib.js"
}
--log-level
#日志级别现在使用名称而不是数字(none
、error
、warn
、info
、verbose
)。
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-bundler
)以编程方式使用 Parcel 2 是可能的。API 已显著更改。有关详细信息,请参见 Parcel API。
挂接到捆绑事件
#Parcel 1 允许您使用 API 挂接并监听 buildEnd
或 buildError
等事件。API 已更改,但您仍可以监听事件:
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 配置。