代码分割

Parcel 支持开箱即用的零配置代码拆分。这使你可以将应用代码拆分为可以按需加载的单独包,从而缩小初始包大小并加快加载时间。

¥Parcel supports zero configuration code splitting out of the box. This allows you to split your application code into separate bundles which can be loaded on demand, resulting in smaller initial bundle sizes and faster load times.

代码分割是通过使用动态 import() 语法来控制的,它的工作方式类似于普通的 import 语句,但返回一个 Promise。这意味着该模块可以异步加载。

¥Code splitting is controlled by use of the dynamic import() syntax, which works like the normal import statement, but returns a Promise. This means that the module can be loaded asynchronously.

使用动态导入

#

¥Using dynamic imports

以下示例展示了如何使用动态导入来按需加载应用的子页面。

¥The following example shows how you might use dynamic imports to load a sub-page of your application on demand.

pages/index.js:
import("./pages/about").then(function (page) {
// Render page
page.render();
});
pages/about.js:
export function render() {
// Render the page
}

由于 import() 返回 Promise,因此还可以使用 async/await 语法。

¥Because import() returns a Promise, you can also use async/await syntax.

pages/index.js:
async function load() {
const page = await import("./pages/about");
// Render page
page.render();
}
load();
pages/about.js:
export function render() {
// Render the page
}

摇树优化

#

¥Tree shaking

当 Parcel 可以确定你使用动态导入模块的哪些导出时,它会摇动该模块中未使用的导出。这适用于静态属性访问或解构,使用 await 或 Promise .then 语法。

¥When Parcel can determine which exports of a dynamically imported module you use, it will tree shake the unused exports from that module. This works with static property accesses or destructuring, with either await or Promise .then syntax.

注意:对于 await 情况,不幸的是,只有当 await 没有转译掉时(即使用现代 browserslist 配置),才能删除未使用的导出。

¥Note: For the await cases, unused exports can unfortunately only be removed when await is not transpilied away (i.e. with a modern browserslist config).

let { x: y } = await import("./b.js");
let ns = await import("./b.js");
console.log(ns.x);
import("./b.js").then((ns) => console.log(ns.x));
import("./b.js").then(({ x: y }) => console.log(y));

共享打包包

#

¥Shared bundles

当应用的多个部分依赖于相同的公共模块时,它们会自动进行数据去重并放入单独的包中。这允许常用的依赖与应用代码并行加载,并由浏览器单独缓存。

¥When multiple parts of your application depend on the same common modules, they are automatically deduplicated into a separate bundle. This allows commonly used dependencies to be loaded in parallel with your application code and cached separately by the browser.

例如,如果你的应用有多个带有 <script> 标签的页面,这些页面依赖于相同的公共模块,那么这些模块将被拆分为“共享包”。这样,如果用户从一个页面导航到另一个页面,他们只需要下载该页面的新代码,而不需要下载页面之间的公共依赖。

¥For example, if your application has multiple pages with <script> tags that depend on the same common modules, those modules will be split out into a "shared bundle”. This way, if a user navigates from one page to another, they only need to download the new code for that page, and not the common dependencies between the pages.

home.html:
<!doctype html>
<div id="app"></div>
<script type="module" src="home.js"></script>
home.js:
import { createRoot } from 'react-dom';

createRoot(app).render(<h1>Home</h1>, app);
profile.html:
<!doctype html>
<div id="app"></div>
<script type="module" src="profile.js"></script>
profile.js:
import { createRoot } from 'react-dom';

createRoot(app).render(<h1>Profile</h1>, app);

编译后的 HTML:

¥Compiled HTML:

home.html:
<!doctype html>
<div id="app"></div>
<script type="module" src="react-dom.23f6d9.js"></script>
<script type="module" src="home.fac9ed.js"></script>
profile.html:
<!doctype html>
<div id="app"></div>
<script type="module" src="react-dom.23f6d9.js"></script>
<script type="module" src="profile.9fc67e.js"></script>

在上面的示例中,home.jsprofile.js 都依赖于 react-dom,因此它被分成一个单独的包,并通过向两个 HTML 页面添加额外的 <script> 标签来并行加载。

¥In the above example, both home.js and profile.js depend on react-dom, so it is split out into a separate bundle and loaded in parallel by adding an extra <script> tag to both HTML pages.

这也适用于已使用动态 import() 进行代码分割的应用的不同部分。两个动态导入之间共享的公共依赖将被拆分出来,并与动态导入的模块并行加载。

¥This also works between different sections of an app that have been code split with dynamic import(). Common dependencies shared between two dynamic imports will be split out and loaded in parallel with the dynamically imported modules.

配置

#

¥Configuration

默认情况下,Parcel 仅在共享模块达到大小阈值时创建共享包。这可以避免拆分非常小的模块并创建额外的 HTTP 请求,即使使用 HTTP/2 也会产生开销。如果模块低于阈值,则会在页面之间复制该模块。

¥By default, Parcel only creates shared bundles when shared modules reach a size threshold. This avoids splitting out very small modules and creating extra HTTP requests, which have overhead even with HTTP/2. If a module is below the threshold, it will be duplicated between pages instead.

Parcel 还具有最大并行请求限制,以避免一次因过多请求而使浏览器过载,并且如果达到此限制,则会复制模块。创建共享包时,较大的模块优先于较小的模块。

¥Parcel also has a maximum parallel request limit to avoid overloading the browser with too many requests at once, and will duplicate modules if this limit is reached. Larger modules are prioritized over smaller ones when creating shared bundles.

默认情况下,这些参数已针对 HTTP/2 进行了调整。但是,你可以调整这些选项以针对你的应用提高或降低它们。你可以通过在项目根目录的 package.json 中配置 @parcel/bundler-default 密钥来完成此操作。

¥By default, these parameters have been tuned for HTTP/2. However, you can adjust these options to raise or lower them for your application. You can do this by configuring the @parcel/bundler-default key in the package.json in your project root.

package.json:
{
"@parcel/bundler-default": {
"minBundles": 1,
"minBundleSize": 3000,
"maxParallelRequests": 20
}
}

可用的选项有:

¥The available options are:

http minBundles minBundleSize maxParallelRequests
1 1 30000 6
2(默认) 1 20000 25

你可以在 web.dev 上阅读有关此主题的更多信息。

¥You can read more about this topic on web.dev.

内部化的异步包

#

¥Internalized async bundles

如果从同一个包中同步和异步导入模块,而不是将其拆分到单独的包中,则异步依赖将被“内部化”。这意味着它将与动态导入保存在同一包中以避免重复,但封装在 Promise 中以保留语义。

¥If a module is imported both synchronously and asynchronously from within the same bundle, rather than splitting it out into a separate bundle, the async dependency will be “internalized”. This means it will be kept within the same bundle as the dynamic import to avoid duplication, but wrapped in a Promise to preserve semantics.

因此,动态导入只是暗示不需要同步依赖,并不能保证创建新的包。

¥For this reason, dynamic import is merely a hint that a dependency is not needed synchronously, not a guarantee that a new bundle will be created.

数据去重

#

¥Deduplication

如果动态导入的模块具有在其所有可能的祖级中都可用的依赖,则会对其进行数据去重。例如,如果页面导入的库也由动态导入的模块使用,则该库将仅包含在父级中,因为它在运行时已经存在于页面上。

¥If a dynamically imported module has a dependency that is already available in all of its possible ancestries, it will be deduplicated. For example, if a page imports a library which is also used by a dynamically imported module, the library will only be included in the parent since it will already be on the page at runtime.

手动共享打包

#

¥Manual shared bundles

注意:手动共享打包包目前处于实验阶段,可能会发生变化。

¥Note: Manual shared bundles are currently experimental and subject to change.

默认情况下,Parcel 会自动将常用模块拆分为 "共享打包包" 并在上面列出的场景中创建打包包。但是,在某些情况下,你可能希望准确指定打包包中包含哪些内容以及谁可以请求该打包包。

¥By default, Parcel automatically splits commonly used modules into "shared bundles" and creates bundles in the scenarios listed above. However, in certain cases, you may want to specify exactly what goes into a bundle and who can request that bundle.

这些场景包括但不限于...

¥These scenarios include but are not limited to...

可以在项目根目录 package.json 中指定手动共享打包包。assets 属性必须设置为 glob 列表。与这些 glob 匹配的任何资源文件路径都将包含在手动共享包中。

¥Manual Shared Bundles can be specified in your project root package.json. The assets property must be set to a list of globs. Any asset file paths matching these globs will be included in the manual shared bundle.

此示例创建一个供应商包,其中包含图中从 manual.js 开始的所有 JS 资源,分为 3 个并行 HTTP 请求。

¥This example creates a vendor bundle which includes all JS assets in the graph starting from manual.js, split into 3 parallel HTTP requests.

package.json:
{
"@parcel/bundler-default": {
"manualSharedBundles": [
{
"name": "vendor",
"root": "manual.js",
"assets": ["**/*"],
"types": ["js"],
"split": 3
},
],
},
}

完整的选项列表如下:

¥The full list of options are as follows:

当心!

¥Be careful!

配置手动共享包会覆盖通常由 Parcel 完成的所有自动代码分割,并且可能会导致意外的加载顺序问题,因为它映射到整个代码库(包括 node_modules)。请注意你使用的 glob,每种文件类型仅指定 1 个打包包,并且我们建议你指定一个根文件。

¥Configuring manual shared bundles overrides all automatic code splitting normally done by Parcel, and can cause unintended load-order issues as it maps on your entire code base, including node_modules. Be mindful of the globs you use, only specify 1 bundle per file type, and we recommend you specify a root file.

Parcel 中文网 - 粤ICP备13048890号