前端性能优化总结

149 阅读4分钟

本文是基于《前端工程体验优化实战》小册做的性能优化方案总结。

资源优先级提示

preload:预加载

提高当前页面中某个资源的加载优先级,确保关键资源优先加载完成。

适用于那些在页面初次渲染时就立即需要加载的资源,加速首屏渲染。

此时 link标签需配合 as使用。

prefetch:预取回

浏览器和网络带宽空闲时,预先下载指定的资源,存储在浏览器缓存中。

适用于非首屏资源,如下一步可能导航到的页面的资源。

preconnect:预连接

提前与目标域名建立连接。

dns-prefetch:dns 预取回

提前对目标域名进行 DNS 寻址,取回并缓存域名对应的 IP 地址。

<!-- prefetch -->
<link ref="prefetch" href="https://xxx.com/xxx.png"></link>

<!-- preload -->
<link ref="preload" as="font" href="https://xxx.com/xxx.ttf"></link>

<!-- preconnect -->
<link ref="preconnect" href="https://xxx.com"></link>

<!-- dns-prefetch -->
<link ref="dns-prefetch" href="https://xxx.com"></link>

代码模块懒加载

import()动态懒加载

1、按照路由进行懒加载

const LoginPage = () => import(/* webpackChunkName: "login" */ './pages/login.vue')

2、按照模块进行懒加载

import { defineAsyncComponent } from 'vue';

// 需要使用框架提供的 defineAsyncComponent 方法包裹动态导入语法
const loginButton = defineAsyncComponent(
  () => import('./pages/login-button.vue')
)

代码分割

  1. webpack 传统代码分割方案
  • 配置复杂
  • 配置可能适应不了频繁迭代更新的代码
  1. 细粒度代码分割方案:比较通用
  • 建议产物数量不超过 20 个,maxInitialRequest: 20
  • cacheGroup 划分 lib组和 shared
    • lib:缓存来自 node_modules 目录,源码体积大于 MAX_LIB_CHUNK_SIZE的模块。用于把体积较大的包拆分,产生独立产物,提高缓存命中率
    • shared:匹配 minChunks: 2,指定 shared 缓存组包含被 2 个及以上区块公用的模块代码,将共享块拆分成一个单独的 chunk,生成一个独立的文件,减少资源体积
    • 指定 lib 优先级更高,优先拆分出独立性较强的产物

CDN 优化

  1. 选择临近用户的 CDN 加速区域
  2. 配置最长缓存时间,结合内容 hash 文件名
  3. 让 CDN 域名符合同名策略
  4. 选择先进的 Brotli算法
  5. 使用新版 HTTP 协议

渲染优化

渲染方式

  • 服务端渲染:在服务端生成 HTML 内容的技术,然后将生成的 HTML 页面发送到客户端进行展示。
  • 客户端渲染:通过 JavaScript 动态生成页面内容。

服务端渲染的核心原理

  • 服务端:renderToString方法生成完成的 HTML。
  • 浏览器:hydrate激活。

同构架构

  • 同构架构:通过运行同一套代码,在服务端和客户端分别渲染出 HTML。一般仅会在第一次请求的时候由服务端生成 HTML,后续客户端激活之后,切换页面就交由 vue-router、react-router 来进行了,不必再请求服务器了

服务端渲染的类型

  • SSR:服务端渲染,每请求一次,都会触发服务端渲染
  • SSG:服务端静态站点生成,处理一些完全静态的页面,将这些内容提前生成一份 HTML 代码,客户端请求时直接响应这份 HTML,不必再额外执行生成逻辑
  • ISR:增量静态页面再生,配合 LRU 缓存算法,若请求的页面还未失效,则返回缓存中的内容,否则会重新生成

图片、视频优化

图片优化

  • 传统图片:png、jpg、gif、svg
    • 兼容性好
    • 体积大
  • 现代图片:webp、avif
    • 兼容性稍差
    • 体积小
    • 无损压缩
<picture>
  <source type="image/avif" srcset="https://cdn.com/image.avif" />
  <source type="image/webp" srcset="https://cdn.com/image.webp" />
  <!-- 必须有一个 img 标签作为兜底处理方案 -->
  <img src="https://cdn.com/image.jpg" alt="Example" />
</picture>

视频优化

  • GIF 动图
    • 体积大
    • 加载慢
    • 帧数低
    • 全部加载完成才开始播放
  • 视频
    • 体积小
    • 加载完成一部分之后会开始播放
<video muted autoplay loop playsinline>
  <source src="GIF-demo.webm" type="video/webm">
  <source src="GIF-demo.mp4" type="video/mp4">
</video>

资源懒加载

监听 scroll 滚动事件

  • 需要一直计算目标元素到视口的距离,可能会造成渲染阻塞
  • 需要对 scroll 的触发做节流。若触发频率太高,会造成性能问题,若触发频率太低,目标元素不能及时加载

Intersaction Observer API

// 创建 IntersectionObserver 实例
const observer = new IntersectionObserver(entries => {
  // 遍历所有观察的元素
  entries.forEach(entry => {
    // 如果该元素进入了视口,就加载该元素的图片
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      observer.unobserve(img);
    }
  });
});

const lazyImages = document.querySelectorAll('img.lazy');

// 开始监听所有需要懒加载的图片元素
lazyImages.forEach(img => {
  observer.observe(img);
});

浏览器原生懒加载方案

loading="lazy"属性

  • 仅支持 img、iframe 元素
  • 浏览器兼容性一般