前端性能问题
FP、FCP、FMP、LCP都是什么P??
旧的指标
FP
首屏绘制(First Paint),第一个像素绘制
影响因素:网速、服务器、缓存
FCP
首屏内容绘制(First Contentful Paint),第一个元素绘制
影响因素:除了FP影响因素外,还有阻塞dom渲染的文件大小和文件数量,自定义字体展示设置、重定向
// 浏览器输入这个可以直接拿到FP和FCP
performance.getEntriesByType("paint")
// defer async
FMP
首次有效绘制(First Meaningful Paint),太主观了,所以不关注
TTI
可交互时间(Time To Interactive)
新的指标
LCP
最大内容绘制(Largest Contentful Paint),所有内容绘制
可以直接使用 PerformanceObserver 来捕获 LCP:
const observer = new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
const lastEntry = entries[entries.length - 1];
const lcp = lastEntry.renderTime || lastEntry.loadTime;
console.log('LCP:', lcp);
});
observer.observe({ entryTypes: ['largest-contentful-paint'] });
LCP也不是完美的,也很容易出错,它会在用户进行交互后就停止捕获,可能会获取到错误的结果,如果有占据页面很大的轮播图也会产生问题会不断的更新 LCP。
LCP也有现成的计算工具库 web-vitals:
import { getLCP } from 'web-vitals';
// Measure and log the current LCP value,
// any time it's ready to be reported.
getLCP(console.log);
CLS
(累计位移偏移(Cumulative Layout Shift))
CLS会计算出页面整个生命周期中所有发生的预料之外的布局偏移的得分的总和。 每当一个可视元素位置发生改变,就是发生了布局偏移。
使用以下代码监听:
const observer = new PerformanceObserver((list) => {
let sum = 0;
for (const entry of list.getEntries()) {
sum += entry.value;
}
console.log(sum);
});
observer.observe({ type: 'layout-shift', buffered: true });
- 基于骨架图
- 为图片设置宽高
- 避免动态插入内容
解决方案
FP/FCP/LCP
针对以上三种指标,主要优化策略就是通过缓存、预加载、减少请求资源大小等方式加快资源请求速度。
- 压缩文件、使用 Tree-shaking 删除无用代码
- 服务端配置 Gzip 进一步再压缩文件体积
- 资源按需加载
- 使用 CDN 加载资源及 dns-prefetch 预解析 DNS 的 IP 地址
- 缓存文件,对首屏数据做离线缓存
- 图片优化,包括:用 CSS 代替蹄片、裁剪适配屏幕的图片大小、小图使用 base64 或者 PNG 格式、支持 WebP 就尽量使用 WebP、渐进式加载图片
项目优化
- 301 302
- service worker => 不一定会加快页面加载速度
- 304 协商缓存 200 强缓存
- DNS浏览器 DNS缓存 host DNS服务器 CDN解析到最近的服务器
- TCP 三次握手 http2以前 http3改为UDP
- 浏览器服务器 off bakend for frontend 请求合并
- 升级到http2
- 1.1 重复建立tcp,keepalive串行,会存在队首阻塞的问题
- 2 对头部做了HPACK算法压缩,在客户端和服务器两端都维护了一个索引表,用于存储已经出现过的头部字段
- 1.1 使用纯文本传输,2 引入了二进制分帧,将所有传输的信息分割为更小的帧,提高了传输效率
- 减少reflow 和 repeat
- 懒加载
- webp
- 图片压缩,图片压缩服务器,jpg => interlace交叉渲染(图片由模糊到清晰),云存储提供商一般都有图片压缩参数
- 视频压缩,mp4 h264
- baseline 兼容性最好
- main 兼容性中等
- high 最小,但对解码器要求最高
- meta:moov => 放在视频前面,如果放在后面,有一些解码器要等文件加载完才知道是mp4 此外:gif为什么比mp4大,gif是i帧(每一帧都是图片),p帧根据前一帧偏移算法推算,b帧是根据前后帧计算出来的
BIGPIPE
如果页面区块很多,一次性拿到所有区块时间太长,最慢的区块会影响页面的整体速度
解决:将页面分区块加载,首先加载骨架,然后加载各个区块,成为pgaelet
service worker
- ios wkwebview 不支持 service worker
- sw本身有启动市场,比纯http缓存要慢
- 文件更新机制做得好
- 部分注销存在问题
webview预缓存(app中使用)
app使用隐藏的webview预先将资源加载一遍,很明显,首页不能用
优势:
- 开发成本低
- 原生
劣势:
- 不支持预装
- 如果webview在一级就不行
对于app来说webview是一个包含生命周期函数的功能强大的组件
安卓实现很简单 ios比较麻烦 webview => 发起请求 => 触发hook => 匹配本地资源 => 返回本地资源 => 结束
react
- react.memo
- shouldComponentUpdate
- list => key
- react.lazy + suspence
vue
- compute
- list => key
- 不要有太多watch