代码分割
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.
由于 import()
返回 Promise,因此还可以使用 async/await 语法。
¥Because import()
returns a Promise, you can also use async/await syntax.
摇树优化
#¥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.
共享打包包
#¥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.
编译后的 HTML:
¥Compiled HTML:
在上面的示例中,home.js
和 profile.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.
可用的选项有:
¥The available options are:
-
minBundles – 对于要分割的资源,它必须被超过
minBundles
个打包包使用。¥minBundles – For an asset to be split, it must be used by more than
minBundles
bundles. -
minBundleSize – 对于要创建的共享打包,它必须至少为
minBundleSize
字节大(在缩小和树摇动之前)。¥minBundleSize – For a shared bundled to be created, it has to be at least
minBundleSize
bytes big (before minification and tree shaking). -
maxParallelRequests – 为了防止网络因太多并发请求而过载,这可确保最多可以一起加载
maxParallelRequests
个同级包。¥maxParallelRequests – To prevent overloading the network with too many concurrent requests, this ensures that a maximum of
maxParallelRequests
sibling bundles can be loaded together. -
http – 将上述值设置为针对 HTTP/1 或 HTTP/2 优化的默认值的简写。有关这些默认值,请参阅下表。
¥http – A shorthand for setting the above values to defaults which are optimized for HTTP/1 or HTTP/2. See the table below for these default values.
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
默认情况下,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...
-
将你的配置从另一个构建工具或打包程序移植到 Parcel
¥Porting over your config from another build tool or bundler to Parcel
-
减少 HTTP 请求而不重复资源,有利于过度获取
¥Reducing your HTTP requests without duplicating assets, in favor of over-fetching
-
过度获取和加载较少的包总体上可以有利于像 time-to-interactive 这样的测量,特别是对于非常大的项目。
¥Over-fetching and loading fewer bundles overall can benefit measurements like time-to-interactive, especially for very large projects.
-
-
为特定路由或一组模块创建优化的共享包
¥Creating an optimized shared bundle for a specific route or set of modules
可以在项目根目录 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.
完整的选项列表如下:
¥The full list of options are as follows:
-
名称(可选) - 将打包包上的字段
manualSharedBundle
设置为 <name>,这可以在自定义报告器或命名器中读取以用于开发目的¥name (optional) - Sets field
manualSharedBundle
on bundle to <name>, this can be read in a custom reporter or namer for development purposes -
根(可选) - 将 glob 的范围缩小到指定的文件。在上面的示例中,glob
**/*
将匹配manual.js
内的所有导入¥root (optional) - Narrows the scope of the glob to the file specified. In the example above, the glob,
**/*
will match all imports withinmanual.js
-
资源(必填) - glob 用于匹配要匹配的 Parcel。除非另有指定,否则与 glob 匹配的文件将被放入单个包中,并在整个项目中进行数据去重。如果未指定
root
,Parcel 会尝试全局匹配 glob。¥assets (required) - glob for Parcel to match on. Files that match the glob will be placed into a singular bundle, and deduplicated across the project unless otherwise specified. If no
root
is specified, Parcel attempts to match the glob globally. -
类型(可选) - 限制 glob 仅匹配特定类型。如果你的
root
文件导入多种类型(例如 JS 和 CSS)或者assets
glob 可以匹配不同类型,则必须设置此字段。一个打包包只能包含相同类型的资源。¥types (optional) - Limits globs to only match on a certain type. This field must be set if your
root
file imports multiple types (e.g. JS and CSS) or if theassets
glob can match different types. A bundle can only contain assets of the same type.-
根文件可以包含多种类型的导入。确保在每种类型的
manualSharedBundle
数组中添加一个对象。¥A root file can contain imports of multiple types. Make sure to add an object in the
manualSharedBundle
array per type.
-
-
拆分(可选) - 将手动打包包拆分为 x 个打包包。
¥split (optional) - splits the manual bundle into x bundles.
-
对于非常大的打包包,拆分为多个并行 HTTP 请求可以改善 CHR(缓存命中率)等测量结果,因为较小的打包包对于给定的更改会失效。
¥For very large bundles, splitting into multiple parallel HTTP requests can improve measurements like CHR (cache hit ratio), as a smaller bundle is invalidated for a given change.
-