本文是基于《前端工程体验优化实战》小册做的性能优化方案总结。
资源优先级提示
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')
)
代码分割
- webpack 传统代码分割方案
- 配置复杂
- 配置可能适应不了频繁迭代更新的代码
- 细粒度代码分割方案:比较通用
- 建议产物数量不超过 20 个,
maxInitialRequest: 20 - cacheGroup 划分
lib组和shared组- lib:缓存来自 node_modules 目录,源码体积大于
MAX_LIB_CHUNK_SIZE的模块。用于把体积较大的包拆分,产生独立产物,提高缓存命中率 - shared:匹配
minChunks: 2,指定 shared 缓存组包含被 2 个及以上区块公用的模块代码,将共享块拆分成一个单独的 chunk,生成一个独立的文件,减少资源体积 - 指定 lib 优先级更高,优先拆分出独立性较强的产物
- lib:缓存来自 node_modules 目录,源码体积大于
CDN 优化
- 选择临近用户的 CDN 加速区域
- 配置最长缓存时间,结合内容 hash 文件名
- 让 CDN 域名符合同名策略
- 选择先进的
Brotli算法 - 使用新版 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 元素
- 浏览器兼容性一般