构建超快网站的 25 条重要提示!!!
我最近做了一个直播,解释了我为我的网站 ( dustinbrett.com ) 做了哪些“最重要的”事情,让它“快速”起来。视频时长超过 2 小时,所以我讲解得相当详细。本文底部有一个嵌入的视频。
在本文中,我将尝试总结每个技巧并添加屏幕截图/链接。
1.内容分发网络(CDN)
这或许是提升网站速度的最佳方法。在我看来,更快地将文件交付给用户是提升性能的最大途径。我的 Web 服务器托管文件的速度相当慢,因此用户可能需要几秒钟才能解析我域名的 DNS 并获取初始文件。而且我的 Web 服务器集中在一个地方。借助 CDN,它可以从更靠近请求文件的用户的边缘服务器提供静态文件的缓存版本。
就 CloudFlare 而言,我使用他们的免费套餐,并通过他们路由我的 dustinbrett.com 的 DNS。它会指向我实际的 Web 服务器,当缓存失效/被清除时,CloudFlare 会去那里获取文件。CloudFlare 还提供了很多自定义和切换选项,可以进一步提升速度。我已链接到免费套餐的信息以及他们的网站速度优化指南。
- https://www.cloudflare.com/en-ca/plans/free/
- https://developers.cloudflare.com/fundamentals/get-started/task-guides/optimize-site-speed/
2. HTTP/2 和 HTTP/3
只要你的 Web 服务器/CDN 支持,这是一个简单的技巧。请确保使用最新的 HTTP 协议来提供内容,因为它在某些情况下可以提供性能优化。
- https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol
- https://en.wikipedia.org/wiki/HTTP/3#Comparison_with_HTTP/1.1_and_HTTP/2
3. Brotli 压缩与 GZip 压缩
在服务器端,另一个简单的技巧是启用 Brotli 压缩(如果支持)。它被认为是 GZip 的继承者,确实能让文件变得更小,理想情况下也意味着速度更快,在本例中似乎确实如此。
- https://en.wikipedia.org/wiki/Brotli
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding
4. HTTP 标头
这很重要,理想情况下默认为一些合理的值,但有些情况下,如果您不设置规则,您将提供未缓存的文件,并且每次都会被请求。我遇到的一个问题就是.ini
服务器不知道这些文件是文本,因此使用 来提供,而Content-Type
其中的application/octet-stream
似乎也将其设置Cache-Control
为max-age=0
。我花了一段时间才注意到这一点,因为我正在 DevTools->Network 中查看我的页面加载请求标头。我服务器端的解决方案是将这些扩展名与 MIME 类型正确关联,例如text/plain
,其合理Cache-Control
值为 1 周。这里的教训是确保向用户发送正确的缓存标头,以便他们的浏览器知道不要不必要地从您的服务器请求东西。
- https://developers.cloudflare.com/cache/about/cache-control/
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
5. 早期提示
我对这个建议有很复杂的感受,因为我无法让它正确地与使用srcset
( <img>
)/ imagesrcset
( <link>
) 的响应式图像一起工作。理论上这似乎是一个非常有用的功能,如果我有其他文件需要这样做,我会考虑设置它。但我现在理想的用例只是图像,为此我需要等待支持响应式Link
标头的服务器。我很高兴被证明是错的,但对我来说,如果您的图像基于 dpi,似乎不可能让 103 Early Hints 依赖于请求浏览器的 dpi 并仅向它们发送相关图像,而不是所有分辨率。对于任何使用非响应式link
标头并使用 CloudFlare 或任何其他支持 103 Early Hints 的服务器的人来说,这似乎是一个很好的功能,因为它会在用户看到带有预加载标签的 HTML 之前告诉他们获取图像<link>
。
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/103
- https://developer.chrome.com/blog/new-in-chrome-103/#http103
- https://developer.chrome.com/blog/early-hints/
- https://developers.cloudflare.com/cache/about/early-hints/
6. 处理初始 CDN MISS
这在一定程度上算是一个技巧,但我越思考就越怀疑它的实用性。对于像我这样的处于大量开发中的网站,经常清除缓存对我来说是有意义的,因为我每周都会更改相当多的文件。因此,每个边缘服务器都需要先转到我缓慢的 Web 服务器,然后才能缓存文件以提供给更近的用户。我所做的就是访问该网站并确保在 CloudFlare 的 HTTP 标头中显示一个 ,而不是MISS
来自边缘服务器的缓存,而是HIT
。但是当我考虑这一点时,我意识到它只是将其缓存在我碰巧访问的边缘服务器上。所以对我来说这样更快,因为后续访问是HIT
,但对于世界各地的用户来说,如果他们的边缘服务器中有人尚未触发 ,他们就会收到那个缓慢的初始请求MISS
。
7. HSTS 标头
我不确定这会带来什么样的性能提升,但我确实喜欢将我的域名添加到某个浏览器列表中,并设置始终通过 HTTPS 访问。这样可以避免那些通过 HTTP 访问你网站并被重定向到 HTTPS 的用户造成的网站速度缓慢。
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
- https://hstspreload.org/?domain=dustinbrett.com
8.<link />
预加载和预连接
我发现这些预加载功能非常有用,因为我可以在 DevTools->Network 中看到,图片在我的动态网站决定需要显示之前就开始加载了。像我这样的网站,首页内容是用户可以更改的桌面环境,因此这些预加载标头对于已经访问过我的网站并删除了显示这些图片的相关动态内容的用户来说可能没什么用。但对我来说,对于大多数首次访问并能更快看到图片的用户来说,这还是值得的,因为这些 HTML 标签告诉他们的浏览器获取我知道大多数用户都需要的图片。
这在加载后也很有用,当用户将光标悬停在菜单按钮上时,我会使用它。在鼠标悬停时,我会将预加载链接标头插入文档头,因为大多数用户不会在鼠标悬停的毫秒内点击菜单按钮,这给了浏览器一些时间来预加载菜单中很可能包含的图片。同样,我的网站是动态的,用户可能会更改菜单内容,这样一些预加载请求就没有必要了。但对于想要动态体验的回访者来说,这只是一个小小的成本。
- https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types
- https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-type
9. 获取优先级
这是目前仅在 Chromium 浏览器上提供的另一个新功能,但如果您的用户支持它,似乎值得使用。该fetchpriority
概念可用于img
、fetch
& link
。对于我希望尽快发生的请求,我会指定high
优先级。
10. HTML 压缩/标签删除
我一直喜欢尽可能精简 HTML 代码,所以 html-minifier-terser 的出现让我感觉很棒,因为它能删除我以为需要但实际并非必需的标签。例如<head>
,,,等标签<body>
。</html>
引号通常也不需要。这款压缩工具非常擅长删除无用的 HTML。之后,我还会运行一个脚本,删除其他一些我不需要<noscript>
的标签,例如 Next.js JSON 数据。
- https://github.com/terser/html-minifier-terser
- https://validator.w3.org/
- https://nextjs.org/docs/basic-features/pages#static-generation-recommended
- https://github.com/DustinBrett/daedalOS/blob/main/scripts/minifyHtml.js
11. 图像缩小/简化
我通常会尝试做的另一件事是尽可能缩小图片尺寸。我在其他技巧中提到过更多,但其中一个实用的方法是缩小图片尺寸。我使用 Windows 工具 FileOptimizer 对所有图片进行无损压缩。我还使用 SVGO 来缩小 SVG 尺寸,因为通常情况下,path
简化值不会造成任何数据/质量损失。最后,我使用的另一种简化技巧可能并不适合所有人,那就是使用最小的图标设置。我使用的绝对最小值是根目录中只有一个 favicon.ico 文件,没有支持指向高分辨率版本的 HTML。根据你的使用情况,你可能需要一些图标标签,但保持尽可能小的尺寸仍然是最理想的。
- https://nikkhokkho.sourceforge.io/static.php?page=FileOptimizer
- https://github.com/DustinBrett/daedalOS/blob/main/scripts/createIcons.bat
- https://jakearchibald.github.io/svgomg/
- https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/The_head_metadata_in_HTML#adding_custom_icons_to_your_site
- https://en.wikipedia.org/wiki/Favicon
12. WEBP、PNG 和 AVIF
至于使用哪种图像格式,这在一定程度上取决于您想要呈现的图像类型。如果您用相机拍摄的是一张有损照片,那么 AVIF 可能是理想的选择。如果是无损缩略图/图标,那么 WEBP 可能会提供更好的效果,尤其是在您不需要 AVIF 提供的一些新功能的情况下。但只要您的用户支持,我认为在大多数情况下,我们应该乐意迁移到现代的 AVIF/WEBP 图像格式,而不是 JPG/PNG,因为根据我的经验,它们似乎在较小的文件中具有相同的视觉质量。
- https://avif.io/blog/comparisons/avif-vs-webp/
- https://caniuse.com/webp
- https://developers.google.com/speed/webp/docs/cwebp
13. 延迟加载/交叉观察器
我使用了几种延迟加载方式,但对我的加载时间最有用的还是动态导入。这使得我避免在加载时打包大部分应用。相反,组件/模块会根据 Webpack 创建的块按需加载。
我实现延迟加载的另一种方法是针对所有表示文件或文件夹的图标。它直到检测到图像已进入视口才会加载图标。对于需要抓取文件本身的动态图标,我使用 JavaScript 和 Intersection ObservergetIcon
在图标按钮到达视口时运行该函数。
- https://developer.mozilla.org/en-US/docs/Web/Performance/Lazy_loading
- https://nextjs.org/docs/advanced-features/dynamic-import
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import
14. 测试灯塔 / GTMetrix / WebpageTest
边做边测试更改是了解所做更改是否朝着正确方向的最佳方法。我所做的许多更改都是基于此类测试的结果。需要注意的是,这些测试虽然会提供建议,但它们并不完全了解您的网站,有时它们会提供一些不值得做、对用户基本没有影响的建议。
- https://github.com/GoogleChrome/lighthouse
- https://pagespeed.web.dev/
- https://gtmetrix.com/
- https://www.webpagetest.org/
15. Web Worker 和屏幕外画布
对我来说,这是一项非常酷的浏览器技术,只要有机会,我都会尝试使用它。我的时钟和壁纸都运行在 Web Worker 中,并且都会将更新绘制到屏幕外的画布上。将我的网站组件迁移到这个系统的另一个好处是,即使主线程卡住了,时钟和壁纸仍然可以继续运行。目前,大多数有用的功能仍然在主线程上运行,但我希望有一天能将所有内容迁移到单独的 Web Worker 中。
- https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
- https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas
- https://developer.mozilla.org/en-US/docs/Glossary/Main_thread
- https://partytown.builder.io/
16. 针对现代浏览器(避免使用 Polyfill 和 ES5)
如果您不需要支持像 IE 这样的老旧浏览器,那么我认为是时候放弃尽可能多的 polyfill 并依靠现代浏览器来实现我们需要的功能,而无需向用户提供更多代码。
17.高级库设置
这取决于你使用的库和框架。就我而言,有 3 个地方可以添加额外的优化:Next.js、Framer Motion 和 Styled Components。所有情况下都有高级配置,我喜欢仔细查看,以便在可能的情况下找到可以添加的小调整。每当我添加任何东西时,npm
我都会查看高级配置设置,只是为了了解哪些是可行的,以及我是否喜欢默认设置。
- https://nextjs.org/docs/advanced-features/compiler
- https://www.framer.com/docs/guide-reduce-bundle-size/
- https://styled-components.com/docs/tooling#dead-code-elimination
18. 预构建 JSON(fs、搜索、预加载)
每当我注意到代码中存在相同的 JSON 结构时,我都会喜欢进行这项优化。这通常是一个创建一次并通过静态文件访问的机会,这通常更快,但并非总是如此,所以请进行测试。
- https://nodejs.org/api/fs.html
- https://github.com/DustinBrett/daedalOS/blob/main/scripts/searchIndex.js
- https://github.com/DustinBrett/daedalOS/blob/main/scripts/preloadIcons.js
- https://github.com/DustinBrett/daedalOS/blob/main/scripts/fs2json.js
19. 捆绑分析器
当我真正花时间查看我的 bundle 包以及里面的内容时,我意识到我在主应用 bundle 包中导入了很多不必要的东西,而用户加载时却需要用到这些东西。这是一个非常有用的工具,可以查看 Webpack 文件的内容,然后你可以使用 dynamicimport
将其拆分成单独的块,只在需要时加载。
20. 内联 CSS
我认为在元素中加载 CSS<head>
仍然是将样式呈现给用户的最快方法之一。使用样式组件和大多数 CSS-in-JS 解决方案的一个优势是,它可以将相关的 CSS 内联到静态 HTML 文件中,以便尽快使用。我个人不使用任何 CSS 文件,但如果有人要这样做,其他技巧(例如 CDN、链接预加载和早期提示)可以改善这些文件的加载速度。
21. 延迟 JavaScript
一些已经使用此属性的框架免费提供了此提示,但请记住,如果您有可以使用的<script>
标签,那么它们就不会阻塞解析器并且可以在之后执行。<head>
defer
DOMContentLoaded
22. 系统字体
这或许并非适用于所有人,但对于我这个在浏览器中创建桌面环境的人来说,尽可能使用操作系统的“系统”字体似乎是最合适的选择。这样做的一个重要性能优势是用户无需下载任何字体,因为他们已经拥有了所需的字体。这样做的一个问题是操作系统之间的一致性,但我发现,总体而言,字体比较相似,用户也比较熟悉。
23. 异步图像解码
关于这一点,我没什么可说的,只能说根据描述,如果你想“减少呈现其他内容的延迟”,你应该使用decoding=async
。它可能只会产生很小的差别,但对于较大的图像来说,这可能是一个明显的变化。
24. 响应式图像
使用<picture>
可以让你更好地控制图片。根据浏览器支持的内容和媒体查询的状态加载不同的图片,可以在任何情况下加载完美大小的图片,这样浏览器就无需调整图片大小,避免图片过小或过大(两者都不理想)。
- https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images
- https://caniuse.com/mdn-html_elements_link_imagesrcset
- https://developer.mozilla.org/en-US/docs/Web/CSS/@media/resolution
25. 定义图像尺寸
我最后的建议,也是内容布局转换中一个很重要的建议,就是尽可能地定义图片的尺寸。定义好高度和宽度后,浏览器就能提前分配空间,而不是在图片加载时移动页面上的元素,因为浏览器会意识到图片的高度和宽度是事先没有设置的。
感谢阅读!
感谢您抽出时间阅读我的文章。如果您想深入了解这些技巧,下方我添加了我的直播,我花了两个多小时讲解这些技巧,并在我的网站 ( dustinbrett.com ) 上展示了它们的工作原理。