对我们的下一本书感兴趣吗?了解更多关于 使用 React 构建大型 JavaScript Web 应用程序

性能模式

预取

预取 (<link rel="prefetch">) 是一种浏览器优化,它允许我们在需要之前获取可能需要用于后续路由或页面的资源。预取可以通过多种方式实现。它可以在 HTML 中声明式地完成(如以下示例所示),通过 HTTP 标头 (Link: </js/chat-widget.js>; rel=prefetch),服务工作者 或者通过更自定义的方式,例如通过 Webpack。

<link rel="prefetch" href="/pages/next-page.html" />
<link rel="prefetch" href="/js/emoji-picker.js" />

预取

在显示如何根据可见性或交互导入模块的示例中,我们发现从点击按钮以切换组件到在屏幕上显示实际组件之间经常存在一些延迟。这是因为,当用户点击按钮时,模块仍然需要被请求和加载!

在许多情况下,我们知道用户将在页面初始渲染后不久请求某些资源。虽然它们可能不会立即可见,因此不应包含在初始捆绑包中,但最好尽可能减少加载时间以提供更好的用户体验!

我们可以预取我们知道将在应用程序中使用的组件或资源。我们可以通过在导入语句中添加 神奇注释 来让 Webpack 知道某些捆绑包需要预取:/* webpackPrefetch: true */

const EmojiPicker = import(/* webpackPrefetch: true */ "./EmojiPicker");
ChatInput.js
1import React, { Suspense, lazy } from "react";
2import Send from "./icons/Send";
3import Emoji from "./icons/Emoji";
4
5const EmojiPicker = lazy(() =>
6 import(/*webpackPrefetch: true,
7 webpackChunkName: "emoji-picker"*/
8 "./EmojiPicker")
9);
10const ChatInput = ({ emojiPicker, gifPicker }) => {
11 const [pickerOpen, togglePicker] = React.useReducer(state => !state, false);
12
13 return (
14 <div className="chat-input-container">
15 <input type="text" placeholder="Type a message..." />
16 <Emoji onClick={togglePicker} />
17 {pickerOpen && (
18 <Suspense fallback={<p id="loading">loading</p>}>
19 <EmojiPicker />
20 </Suspense>
21 )}
22 <Send />
23 </div>
24 );
25};
26
27console.log("ChatInput loading", Date.now());
28
29export default ChatInput;

构建应用程序后,我们可以看到 EmojiPicker 将被预取。

 Asset                             Size       Chunks                          Chunk Names
    emoji-picker.bundle.js         1.49 KiB   emoji-picker [emitted]          emoji-picker
    vendors~emoji-picker.bundle.js 171 KiB    vendors~emoji-picker [emitted]  vendors~emoji-picker
    main.bundle.js                 1.34 MiB   main  [emitted]                 main

Entrypoint main = main.bundle.js
(prefetch: vendors~emoji-picker.bundle.js emoji-picker.bundle.js)

实际输出在文档的 head 中以带有 rel="prefetch"link 标签形式显示。

<link rel="prefetch" href="emoji-picker.bundle.js" as="script" />
<link rel="prefetch" href="vendors~emoji-picker.bundle.js" as="script" />

预取的模块即使在 **用户请求资源之前** 也会被浏览器请求和加载。当浏览器处于空闲状态并计算出它有足够的带宽时,它将发出请求以加载资源,并将其缓存。由于资源已缓存,因此可以显著减少加载时间,因为我们不必等到用户点击按钮后请求完成。它可以简单地从缓存中获取已加载的资源。

虽然预取是优化加载时间的好方法,但不要过度使用。如果用户最终没有请求 EmojiPicker 组件,我们就会不必要地加载资源。这可能会给用户带来额外的成本,或者减慢应用程序速度。只预取必要的资源。

您可能会发现以下有关预取的资源很有帮助