Skip to content

Commit

Permalink
docs(cn): adjust guides/code-splitting format
Browse files Browse the repository at this point in the history
docs(cn): adjust guides/code-splitting format
  • Loading branch information
Yucohny authored Jul 20, 2023
2 parents 54452ff + 445b505 commit b9b9134
Showing 1 changed file with 41 additions and 40 deletions.
81 changes: 41 additions & 40 deletions src/content/guides/code-splitting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ contributors:
- atesgoral
- snitin315
- artem-malko
- Yucohny
translators:
- QC-L
- jacob-lcs
Expand All @@ -48,19 +49,19 @@ related:
url: https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content
---

T> 本指南继续沿用 [起步](/guides/getting-started) 中的示例代码。请确保你已熟悉这些指南中提供的示例以及[输出管理](/guides/output-management/)章节。
T> 本指南继续沿用 [起步](/guides/getting-started) 中的示例代码。请确保你已熟悉这些指南中提供的示例以及 [管理输出](/guides/output-management/) 章节。

代码分离是 webpack 中最引人注目的特性之一。此特性能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。代码分离可以用于获取更小的 bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。
代码分离是 webpack 中最引人注目的特性之一。此特性能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。代码分离可以用于获取更小的 bundle、控制资源加载优先级,如果使用合理,会极大影响加载时间。

常用的代码分离方法有三种:

- **入口起点**:使用 [`entry`](/configuration/entry-context) 配置手动地分离代码。
- **防止重复**:使用 [Entry dependencies](/configuration/entry-context/#dependencies) 或者 [`SplitChunksPlugin`](/plugins/split-chunks-plugin) 去重和分离 chunk。
- **动态导入**:通过模块的内联函数调用来分离代码。

## 入口起点(entry point) $#entry-points$
## 入口起点 $#entry-points$

这是迄今为止最简单直观的分离代码的方式。不过,这种方式手动配置较多,并有一些隐患,我们将会解决这些问题。先来看看如何从 main bundle 中分离 another module(另一个模块)
这是迄今为止最简单直观的分离代码的方式。不过,这种方式手动配置较多,并有一些我们将会解决的隐患。先来看看如何从 main bundle 中分离另一个模块

**project**

Expand Down Expand Up @@ -104,7 +105,7 @@ console.log(_.join(['Another', 'module', 'loaded!'], ' '));
};
```

这将生成如下构建结果
构建后结果如下

```bash
...
Expand All @@ -124,13 +125,13 @@ webpack 5.4.0 compiled successfully in 245 ms
- 如果入口 chunk 之间包含一些重复的模块,那些重复模块都会被引入到各个 bundle 中。
- 这种方法不够灵活,并且不能动态地将核心应用程序逻辑中的代码拆分出来。

以上两点中,第一点对我们的示例来说无疑是个问题,因为之前我们在 `./src/index.js` 中也引入过 `lodash`,这样就在两个 bundle 中造成重复引用。在下一章节会移除重复的模块
以上两点中,第一点对我们的示例来说无疑是个问题,因为之前我们在 `./src/index.js` 中也引入过 `lodash`,这样就在两个 bundle 中造成重复引用。在下一章节会介绍如何移除重复的模块

## 防止重复(prevent duplication) $#prevent-duplication$
## 防止重复 $#prevent-duplication$

### 入口依赖 $#entry-dependencies$

配置 [`dependOn` option](/configuration/entry-context/#dependencies) 选项,这样可以在多个 chunk 之间共享模块:
配置 [`dependOn`](/configuration/entry-context/#dependencies) 选项,这样可以在多个 chunk 之间共享模块:

**webpack.config.js**

Expand Down Expand Up @@ -159,7 +160,7 @@ webpack 5.4.0 compiled successfully in 245 ms
};
```

如果我们要在一个 HTML 页面上使用多个入口时,还需设置 `optimization.runtimeChunk: 'single'`,否则还会遇到[这里](https://bundlers.tooling.report/code-splitting/multi-entry/)所述的麻烦。
如果我们要在一个 HTML 页面上使用多个入口时,还需设置 `optimization.runtimeChunk: 'single'`,否则还会遇到 [这里](https://bundlers.tooling.report/code-splitting/multi-entry/) 所述的麻烦。

**webpack.config.js**

Expand Down Expand Up @@ -240,7 +241,7 @@ webpack 5.4.0 compiled successfully in 249 ms
};
```

使用 [`optimization.splitChunks`](/plugins/split-chunks-plugin/#optimization-splitchunks) 配置选项之后,现在应该可以看出,`index.bundle.js``another.bundle.js` 中已经移除了重复的依赖模块。需要注意的是,插件将 `lodash` 分离到单独的 chunk,并且将其从 main bundle 中移除,减轻了大小。执行 `npm run build` 查看效果:
使用 [`optimization.splitChunks`](/plugins/split-chunks-plugin/#optimization-splitchunks) 配置选项之后,将会发现 `index.bundle.js``another.bundle.js` 中已经移除了重复的依赖模块。需要注意的是,插件将 `lodash` 分离到单独的 chunk,并且将其从 main bundle 中移除,减轻了 bundle 大小。执行 `npm run build` 查看效果:

```bash
...
Expand All @@ -260,13 +261,13 @@ webpack 5.4.0 compiled successfully in 241 ms

以下是由社区提供,一些对于代码分离很有帮助的 plugin 和 loader:

- [`mini-css-extract-plugin`](plugins/mini-css-extract-plugin): 用于将 CSS 从主应用程序中分离。
- [`mini-css-extract-plugin`](plugins/mini-css-extract-plugin)用于将 CSS 从主应用程序中分离。

## 动态导入(dynamic import) $#dynamic-imports$
## 动态导入 $#dynamic-imports$

当涉及到动态代码拆分时,webpack 提供了两个类似的技术。第一种,也是推荐选择的方式是,使用符合 [ECMAScript 提案](https://github.com/tc39/proposal-dynamic-import)[`import()` 语法](/api/module-methods/#import-1) 来实现动态导入。第二种,则是 webpack 的遗留功能,使用 webpack 特定的 [`require.ensure`](/api/module-methods/#requireensure)。让我们先尝试使用第一种……
当涉及到动态代码拆分时,webpack 提供了两个类似的技术。第一种,也是推荐选择的方式,使用符合 [ECMAScript 提案](https://github.com/tc39/proposal-dynamic-import)[`import()` 语法](/api/module-methods/#import-1) 实现动态导入。第二种则是 webpack 的遗留功能,使用 webpack 特定的 [`require.ensure`](/api/module-methods/#requireensure)。让我们先尝试使用第一种……

W> `import()` 调用会在内部用到 [promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)。如果在旧版本浏览器中(例如,IE 11)使用 `import()`,记得使用一个 polyfill 库(例如 [es6-promise](https://github.com/stefanpenner/es6-promise)[promise-polyfill](https://github.com/taylorhakes/promise-polyfill)来 shim `Promise`
W> `import()` 调用会在内部使用到 [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)。如果在旧版本浏览器中(例如,IE 11)使用 `import()`,记得使用一个 polyfill 库(例如 [es6-promise](https://github.com/stefanpenner/es6-promise)[promise-polyfill](https://github.com/taylorhakes/promise-polyfill))来 shim `Promise`

在我们开始之前,先从上述示例的配置中移除掉多余的 [`entry`](/concepts/entry-points/)[`optimization.splitChunks`](/plugins/split-chunks-plugin/#optimization-splitchunks),因为接下来的演示中并不需要它们:

Expand Down Expand Up @@ -309,7 +310,7 @@ webpack-demo
|- /node_modules
```

现在,我们不再使用 statically import(静态导入) `lodash`而是通过 dynamic import(动态导入) 来分离出一个 chunk:
现在,我们不再静态导入 `lodash`而是通过动态导入来分离出一个 chunk:

**src/index.js**

Expand All @@ -320,7 +321,7 @@ webpack-demo
+function getComponent() {
- const element = document.createElement('div');

- // Lodash, now imported by this script
- // lodash 现在使用 import 引入
- element.innerHTML = _.join(['Hello', 'webpack'], ' ');
+ return import('lodash')
+ .then(({ default: _ }) => {
Expand Down Expand Up @@ -356,7 +357,7 @@ cacheable modules 530 KiB
webpack 5.4.0 compiled successfully in 268 ms
```

由于 `import()` 会返回一个 promise,因此它可以和 [`async` 函数](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function)一起使用。下面是如何通过 async 函数简化代码
由于 `import()` 会返回 promise,因此它可以和 [`async` 函数](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) 一起使用。下面是使用 `async` 简化后的代码

**src/index.js**

Expand Down Expand Up @@ -384,18 +385,18 @@ webpack 5.4.0 compiled successfully in 268 ms
});
```

T> 在稍后示例中,可能会根据计算后的变量(computed variable)导入特定模块时,可以通过向 `import()` 传入一个 [动态表达式](/api/module-methods/#dynamic-expressions-in-import)
T> 在稍后示例中,当需要根据计算后的变量导入特定模块时,可以通过向 `import()` 传入一个 [动态表达式](/api/module-methods/#dynamic-expressions-in-import) 实现

## 预获取/预加载模块(prefetch/preload module) $#prefetchingpreloading-modules$
## 预获取/预加载模块 $#prefetchingpreloading-modules$

Webpack v4.6.0+ 增加了对预获取和预加载的支持
Webpack v4.6.0+ 增加了对预获取(prefetch)和预加载(preload)的支持

在声明 import 时,使用下面这些内置指令,可以让 webpack 输出 "resource hint(资源提示)",来告知浏览器:
在声明 `import` 时,使用下面这些内置指令,可以让 webpack 输出resource hint,来告知浏览器:

- **prefetch**(预获取):将来某些导航下可能需要的资源
- **preload**(预加载):当前导航下可能需要资源
- **prefetch**预获取:将来某些导航下可能需要的资源
- **preload**预加载:当前导航下可能需要资源

下面这个 prefetch 的简单示例中,有一个 `HomePage` 组件,其内部渲染一个 `LoginButton` 组件,然后在点击后按需加载 `LoginModal` 组件。
下面这个预获取的简单示例中,有一个 `HomePage` 组件,其内部渲染一个 `LoginButton` 组件,然后在点击后按需加载 `LoginModal` 组件。

**LoginButton.js**

Expand All @@ -406,18 +407,18 @@ import(/* webpackPrefetch: true */ './path/to/LoginModal.js');

这会生成 `<link rel="prefetch" href="login-modal-chunk.js">` 并追加到页面头部,指示着浏览器在闲置时间预取 `login-modal-chunk.js` 文件。

T> 只要父 chunk 完成加载,webpack 就会添加 prefetch hint(预取提示)
T> 只要父 chunk 完成加载,webpack 就会添加预获取提示

与 prefetch 指令相比,preload 指令有许多不同之处
与预获取指令相比,预加载指令有许多不同之处

- preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。
- preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。
- preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻。
- 预加载 chunk 会在父 chunk 加载时,以并行方式开始加载。预加载 chunk 会在父 chunk 加载结束后开始加载。
- 预加载 chunk 具有中等优先级,并立即下载。预加载 chunk 在浏览器闲置时下载。
- 预加载 chunk 会在父 chunk 中立即请求,用于当下时刻。预加载 chunk 会用于未来的某个时刻。
- 浏览器支持程度不同。

下面这个简单的 preload 示例中,有一个 `Component`依赖于一个较大的 library,所以应该将其分离到一个独立的 chunk 中。
下面这个简单的预加载示例中,有一个 `Component`依赖于一个较大的库,所以应该将其分离到一个独立的 chunk 中。

我们假想这里的图表组件 `ChartComponent` 组件需要依赖一个体积巨大的 `ChartingLibrary` 库。它会在渲染时显示一个 `LoadingIndicator(加载进度条)` 组件,然后立即按需导入 `ChartingLibrary`
假想这里的图表组件 `ChartComponent` 组件需要依赖一个体积巨大的 `ChartingLibrary` 库。它会在渲染时显示一个 `LoadingIndicator` 组件,然后立即按需导入 `ChartingLibrary`

**ChartComponent.js**

Expand All @@ -426,7 +427,7 @@ T> 只要父 chunk 完成加载,webpack 就会添加 prefetch hint(预取提
import(/* webpackPreload: true */ 'ChartingLibrary');
```

在页面中使用 `ChartComponent` 时,在请求 ChartComponent.js 的同时,还会通过 `<link rel="preload">` 请求 charting-library-chunk。假定 page-chunk 体积比 charting-library-chunk 更小,也更快地被加载完成,页面此时就会显示 `LoadingIndicator(加载进度条)` ,等到 `charting-library-chunk` 请求完成,LoadingIndicator 组件才消失。这将会使得加载时间能够更短一点,因为只进行单次往返,而不是两次往返尤其是在高延迟环境下。
在页面中使用 `ChartComponent` 时,在请求 `ChartComponent.js` 的同时,还会通过 `<link rel="preload">` 请求 `charting-library-chunk`。假定 page-chunk 体积比 `charting-library-chunk` 更小,也更快地被加载完成,页面此时就会显示 `LoadingIndicator` ,等到 `charting-library-chunk` 请求完成,`LoadingIndicator` 组件才消失。这将会使得加载时间能够更短一点,因为只进行单次往返,而不是两次往返尤其是在高延迟环境下。

T> 不正确地使用 `webpackPreload` 会有损性能,请谨慎使用。

Expand All @@ -439,7 +440,7 @@ const lazyComp = () =>
// 例如,我们可以在网络错误的情况下重试请求
});
```
如果在 webpack 开始加载该脚本之前脚本加载失败(如果该脚本不在页面上,webpack 只是创建一个 script 标签来加载其代码),则该 catch 处理程序将不会启动,直到 [chunkLoadTimeout](/configuration/output/#outputchunkloadtimeout) 未通过。此行为可能是意料之外的。但这是可以解释的 - webpack 不能抛出任何错误,因为 webpack 不知道那个脚本失败了。Webpack 将在错误发生后立即将 onerror 处理脚本添加到 script 中。
如果在 webpack 开始加载该脚本之前脚本加载失败(如果该脚本不在页面上,webpack 只是创建一个 script 标签来加载其代码),则该 catch 处理程序将不会启动,直到 [chunkLoadTimeout](/configuration/output/#outputchunkloadtimeout) 未通过。此行为可能是意料之外的。但这是可以解释的 —— webpack 不能抛出任何错误,因为 webpack 不知道那个脚本失败了。webpack 将在错误发生后立即将 onerror 处理脚本添加到 script 中。

为了避免上述问题,你可以添加自己的 onerror 处理脚本,将会在错误发生时移除该 script。

Expand All @@ -451,18 +452,18 @@ const lazyComp = () =>
></script>
```

在这种情况下,错误的 script 将被删除。Webpack 将创建自己的 script,并且任何错误都将被处理而没有任何超时。
在这种情况下,错误的 script 将被删除。webpack 将创建自己的 script,并且任何错误都将被处理而没有任何超时。

## bundle 分析(bundle analysis) $#bundle-analysis$
## bundle 分析 $#bundle-analysis$

一旦开始分离代码,一件很有帮助的事情是,分析输出结果来检查模块在何处结束。 [官方分析工具](https://github.com/webpack/analyse) 是一个不错的开始。还有一些其他社区支持的可选项:
一旦开始分离代码,一件很有帮助的事情是,分析输出结果来检查模块在何处结束。[官方分析工具](https://github.com/webpack/analyse) 是一个不错的开始。还有一些其他社区支持的可选项:

- [webpack-chart](https://alexkuz.github.io/webpack-chart/): webpack stats 可交互饼图。
- [webpack-visualizer](https://chrisbateman.github.io/webpack-visualizer/): 可视化并分析你的 bundle,检查哪些模块占用空间,哪些可能是重复使用的。
- [webpack-chart](https://alexkuz.github.io/webpack-chart/)webpack stats 可交互饼图。
- [webpack-visualizer](https://chrisbateman.github.io/webpack-visualizer/):分析并可视化 bundle,检查哪些模块占用空间,哪些可能是重复使用的。
- [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer):一个 plugin 和 CLI 工具,它将 bundle 内容展示为一个便捷的、交互式、可缩放的树状图形式。
- [webpack bundle optimize helper](https://webpack.jakoblind.no/optimize)这个工具会分析你的 bundle,并提供可操作的改进措施,以减少 bundle 的大小。
- [webpack bundle optimize helper](https://webpack.jakoblind.no/optimize)这个工具会分析 bundle,并提供可操作的改进措施,以减少 bundle 的大小。
- [bundle-stats](https://github.com/bundle-stats/bundle-stats):生成一个 bundle 报告(bundle 大小、资源、模块),并比较不同构建之间的结果。

## 下一步 $#next-steps$

接下来,查看 [延迟加载](/guides/lazy-loading/) 来学习如何在实际一个真实应用程序中使用 `import()` 的具体示例,以及查看 [缓存](/guides/caching/) 来学习如何有效地分离代码。
接下来,查看 [延迟加载](/guides/lazy-loading/) 来学习如何在真实的应用程序中使用 `import()` 的具体示例,以及查看 [缓存](/guides/caching/) 来学习如何有效地分离代码。

0 comments on commit b9b9134

Please sign in to comment.