13 性能优化
约 2785 字大约 9 分钟
2025-09-07
- DCL(DOM Content Loaded):dom解析完毕,不包括css、图像等资源的加载 监听:
document.addeventListener('DOMContentLoaded', function() {}, false)
计算方式:
const dclTime = performance.timing.domContentLoadedEventEnd - performance.timing.domContentLoadedEventStart
- load (Onload Event):页面中依赖的所有资源加载完的事件 监听:
window.onload
计算方式
const loadTime = performance.timing.loadEventEnd - performance.timing.loadEventStart;
- FP (First Paint):渲染出第一个像素点。(一般在html解析完成或者解析一部分的时候触发)
const fp = performance.getEntriesByType('paint').filter(entry => entry.name == 'first-paint')[0].startTime;
- FCP(First Contentful Paint):渲染出第一个内容 计算方式
const fcp = performance.getEntriesByType('paint').filter(entry => entry.name == 'first-contentful-paint')[0].startTime;
- FMP(First Meaningful Paint):首次渲染出有意义的内容的事件。没有标准的定义 由于没有标准的定义,需要自己使用MutationObserver计算
- LCP(Largest Contentful Paint):最大内容渲染时间 计算方式
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('LCP candidate:', entry.startTime, entry);
}
}).observe({type: 'largest-contentful-paint', buffered: true});
- 白屏时间:地址栏输入网址后回车 - 浏览器出现第一个元素 白屏结束时间 = FP事件触发时间
- 首屏时间:地址栏输入网址后回车 - 浏览器第一屏渲染完成 首屏结束时间 = FCP事件触发时间 或 FMP、LCP
可交互时间
- TTI(Time to Interactive):首次可交互时间。计算较为复杂,需要满足以下条件:
- 从 FCP 指标后开始计算;
- 持续 5 秒内无长任务(执行时间超过 50 ms)且无两个以上正在进行中的 GET 请求;
- 往前回溯至 5 秒前的最后一个长任务结束的时间。 计算方式
const timeToInteractive = performance.timing.domInteractive - performance.timing.fetchStart
- FID(First Input Delay):首次输入延迟时间,记录在FCP和TTI之间,用户首次与页面交互时响应的延迟 计算方式
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('fid', entry.processingStart - entry.startTime);
}
});
observer.observe({type: 'first-input', buffer: true});
稳定性指标
- CLS(Cumulative Layout Shift):在页面的整个生命周期中发生的每一次意外布局变化的最大布局变化得分,得分越小页面越稳定 不稳定元素:一个非用户操作但发生较大偏移的可见元素称为不稳定元素。 布局变化得分:元素从原始位置偏移到当前位置影响的页面比例 * 元素偏移距离比例 计算方式
import {getCLS} from 'web-vitals';
getCLS(console.log);
流畅性指标
FPS
- Chrome DevTool 中有一栏 Rendering 中包含 FPS 指标,但目前浏览器标准中暂时没有提供相应 API ,只能手动实现。这里需要借助
requestAnimationFrame
方法模拟实现,浏览器会在下一次重绘之前执行 rAF 的回调,因此可以通过计算每秒内 rAF 的执行次数来计算当前页面的 FPS。 - FPS过低会让用户感觉卡顿,因此这个计算可以用来监控页面卡顿情况。
首屏加载速度
- 打包工具压缩
- 异步加载
- 使用新版本库:有的库老版本不支持树摇优化,新版本支持,更新可以减小打包体积
- 对于使用量小的工具库可以自己手动实现,因为一个库为了兼容性或功能可能会有很多代码,自己实现可以依据自己的需求得到最小实现代码体积
- 对于体积较大的图片不使用base64
- 首屏数据尽量并行,如果业务允许可以将小数据量的接口合并到其他接口中,请求接口数量
- 页面包含大量dom可以分批跟随滚动渲染
- 骨架屏、loading、进度条减少用户焦虑
什么情况会造成操作卡顿和渲染慢
- 一次性操作大量dom
- 进行复杂度很高的运算
vue的优化
- 正确使用v-show和v-if
- v-for使用合理的key值
- keep-alive缓存
- 区分请求粒度,减少请求范围
- 不变数据、定期失效可以缓存再cookies或者localstorage中
- 对于要页面要缓存的数据存于内存中(全局对象、pinia),能够保证更新、
亮点难点
移动端
- 多屏幕适配rem,px转vw,媒体查询,js解决
- 移动端、c端项目通常具有非常高的还原度要求
- uniapp条件编译
- 使用兼容性高的api
B端
文件上传
- 大文件上传、断点续传
- webworker计算
- excel、pdf、word预览、导出,在线excel
- 富文本编辑器
可视化
- 难度比较高的图表,如:高度定制的地图、流程图、关系图
- 按照公司ui设计自定义图表风格和样式,形成公司风格的ui组件库
- 3d可视化绘图
其他
- 即时通讯webscoket,在线协作,消息提醒
- webrtc
- webgl
- canvas功能
- 图片滤镜,剪裁,压缩
工作亮点
- 规范定制,并落实到工程化工具中
- 组件封装
行为埋点
埋点
埋点主要用于收集用户行为数据。通过在前端代码中插入代码或脚本的方式来实现埋点功能。 埋点的主要作用就是:捕获特定用户行为(如点击、浏览、提交表单、页面跳转等)以及关键业务数据(如下单金额、商品类别等)
埋点的实现方案大致可以分为以下三大类:
- 手动埋点:在代码中手动加入记录代码来捕获特定事件。
- 自动埋点:利用DOM事件代理等技术来捕获页面上所有事件,从而减少手动配置。
- 可视化埋点:通过工具界面标记需要采集的元素和事件,可以不用手写代码。
手动埋点
手动埋点就是在用户操作的行为添加监听,并将该操作记录,比如:按钮点击埋点就是监听目标按钮的点击事件然后将这些信息记录下来
监控
监控主要关注系统的性能和稳定性。在日常开发中,我们会通过采集页面加载时间、资源请求、错误日志等数据的方式来实现前端监控。 监控的主要作用就是:及时发现并定位页面性能瓶颈或代码异常,目的是为了保障系统不出bug
监控一般需要完成以下三大部分:
- 性能监控:如:首屏加载时间、页面交互耗时、资源加载耗时等。
- 错误监控:捕获JEvaScript错误、网络请求失败、资源加载异常等。
- 用户体验监控:收集白屏、卡顿等影响用户体验的问题等。
区别
维度 | 埋点 | 监控 |
---|---|---|
目标 | 捕获用户行为数据 | 监控系统性能、错误、稳定性 |
数据类型 | 用户点击、表单提交,页面跳转等 | 页面加载事件、错误日志、卡顿情况等 |
实现方式 | 手动埋点、自动埋点、可视化埋点 | 错误捕获、性能指标采集 |
核心关注点 | 用户行为、业务数据 | 系统bug、性能优化 |
性能监控
如果要监控某个操作的性能可以该操作开始前与结束后分别记录要监控的性能指标,比如:首屏渲染耗时,就需要在加载前记录一下当前的时间戳,等加载完成后再记录下时间戳,用加载完成后的时间减去开始时的时间就能得到耗时 性能监控一般使用preference全局对象中提供的函数等接口获取数据的
错误监控
监听js-error
事件并根据不同的错误类型进行判断来监控
上报
对于用户行为的埋点和错误监控,一般都是需要上报的,上报给服务器服务器接收后进行处理将数据展示出来以便分析并优化程序
上报也分为统一上报和实时上报
ECharts性能优化
1. 分段加载
使用dataZoom配置只显示一部分区域内的数据
优点 可以很好的解决 ECharts 首次进行大数据量渲染造成的卡顿体验问题,不需要额外的数据处理,只需要通过简单的配置 dataZoom
缩放组件就可以实现
缺点
- 无法进行全局概览数据,只能分段查看数据
- 可能需要根据数据量动态的配置属性值,start、end、minSpan 和 maxSpan
2. 降采样
通过sampling属性配置过滤数据点的策略,被过滤的数据将不会被渲染 可选值有以下几种:
- lttb: 采用
Largest-Triangle-Three-Bucket
算法,可以最大程度保证采样后线条的趋势,形状和极值。 - average: 取过滤点的平均值
- min: 取过滤点的最小值
- max: 取过滤点的最大值
- minmax: 取过滤点绝对值的最大极值 (从 v5.5.0 开始支持)
- sum: 取过滤点的和
优点
- 使用简单,ECharts 内部降采样算法,效果显著
- 可以完整的将曲线趋势展示出来,和原曲线基本一致
缺点
- 并不是展示的所有点,会删除一些无用的点,保证渲染性能
- 最大程度保证采样后线条的趋势,形状和极值,但是某些情况下,极值有偏差
3. 使用后台线程进行数据处理
使用webworker在后台处理数据,不阻塞主线程
4. 过滤不必要的数据
5. 使用canvas
相比于svg,canvas没有真实dom也没有事件,所以在没有复杂事件需求的图表中使用canvas可以提升性能
6. 使用appendData异步分片加载数据
appendData是v5版本提出的,只有少部分图表支持,它可以分片异步加载数据和增量渲染 允许向指定的系列(series)追加数据,而无需重新设置整个 option
。
echartsInstance. appendData
(opts: {
// 要增加数据的系列序号。
seriesIndex?: string,
// 增加的数据。
data?: Array|TypedArray
}) => string
示例:
myChart.appendData({
seriesIndex: 0, // 指定要追加数据的系列索引
data: [
// 新的数据块
[timestamp1, value1],
[timestamp2, value2],
// ...
]
});
单页应用首屏加载速度慢的解决方法
- 使用路由懒加载、异步组件,实现组件拆分,减少入口文件体积大小(优化体验骨架屏)
- 抽离公共代码,采用splitchunks进行代码分割。
- 组件加载采用按需加载的方式。
- 静态资源缓存,采用HTTP 缓存(强制缓存、对比缓存)、使用localStorage实现缓存资源。
- 图片资源的压缩,雪碧图、对小图片进行base64 减少http 请求。
- 打包时开启gzip压缩处理compression-webpack-plugin插件
- 静态资源采用CDN提速。终极的手段
- 使用SSR对首屏做服务端染。
贡献者
版权所有
版权归属:wynnsimon