博客列表 >记一次播放器页面白屏时间优化方案

记一次播放器页面白屏时间优化方案

P粉962186587
P粉962186587原创
2022年03月17日 19:27:321030浏览

优化对象

此次优化是针对,Web 端视频播放页面。白屏时间按照播放视频状态有两种情况,分别是:

外部进入,包括筛选页进入、分享链接进入、用户详情页进入和页面强制刷新,此时白屏时间可以理解为浏览器输入 url 到播放器播放的时间,主要流程分为四部分:

完全白屏,FP(First Paint) 之前流程
初始渲染视图和 loading 出现,sessions 接口请求极其依赖逻辑执行
sessions 之后的流程
loading 消失,开始播放
内部切换,通过手动点击【用户会话】列表项切换视频,此时白屏时间可以理解为 loading 出现到消失的阶段,有三部分:

loading 出现
sessions 之后的流程
loading 消失,开始播放
整理流程图示如下:

预期结果
正常情况下,新版本视频目标达到 秒开,老版本视频因为需要手动解析事件,可能会稍慢一些。

已做工作
事件流分页(加载第一页数据后即开始播放)
提前渲染,不等待数据准备完毕,第一时间渲染播放器、会话列表和事件流框架,播放前展示 loading ,给用户视觉上更好的体验,「觉得快」
引入 Web Worker

新版本视频因为返回数据为字符串,需要手动解析为 JSON,因此下载和解析逻辑移入 Web Worker,充分利用 CPU 资源,避免影响主线程逻辑和渲染,播放前主要是不影响数据流,事件流的解析和渲染。
老版本视频下载和解析逻辑移入 Web Worker。
全页面去掉自定义滚动条
开发者工具 source 面板懒加载
整体分析
借助 Chrome Performance 录制白屏至视频开播之间的片段,进行分析:

因为 FP 用于记录页面第一次绘制像素的时间,之后即会进入动态代码阶段,所以下面以 FP 为界限,分别分析前和后两部分。

FP 之前分析
下面是 FP 之前的详情截图

从图中可以看出耗时的任务主要为两部分:

my-details.js 文件下载(目前 gzip 压缩之后,体积为737.25 KB)
一个大 Task
问题定位
文件下载阶段,打包文件过大,通过 webpack 的打包插件 webpack-bundle-analyzer 分析,如图所示:

其中 monaco-editor 是最大的一个问题点。

js 文件执行阶段,具体分析下大 Task,Performance 放大截图如下

分析 Task 执行顺序,执行流程图如下(只列出主要流程,打包之后执行流程会不同,但不影响分析):

结合 Performance 结果,可以看出其中 monaco-editor 仍然是最大的问题点。

进一步分析问题如下:

chunk-vendor.js 是项目所有的依赖库,从它是打包了node_modules可以看出,所以很影响性能
monaco-editor 编辑器占了很大体积
对于一些其他的工具库,未采用按需引入的方式
优化方案
优化 FP 指标,关键是缩短资源的下载时间,以及减少阻塞浏览器渲染 DOM 的任务的执行时间,通常来说的手段有骨架图、分块加载、缓存、减少同步js代码、资源压缩和资源按需加载等等。对我们来说大部分工作已经做了,现在关键点为有几个文件很大。

因此,总结下优化方案:

拆分过大 js 文件,尤其是 monaco-editor 的抽离,因为用的地方太多了, 导致很难做成按需加载了的了,可以使用splitChunks分离代码并实现相关模块共享。单独打包monaco-editor,最终减少请求资源的大小和请求次数
替换过大的第三方类库,主要是 lodash.js 和 moment.js
工具库采用按需引入的方式,尤其是 element-ui
缓存部分静态资源,提高加载速度
ps: Webpack 调优已完成,补充文档 webpack 性能调优报告

FP 之后分析
问题定位
FP 之后就进入了代码执行阶段,下面为 8 个会话按照几个重要节点进行耗时统计的柱状图:

由柱状图可以看出,vue 数据处理和 dom 挂载(mounted之前)占用了很小的范围,几乎对白屏没有任何影响。重点关注两个阶段

mounted -> playInit ready,即播放初始数据(playInit)准备好之前
playInit ready -> play,即拿到 playInit 到正常播放
这两个阶段加一起的耗时平均接近 2 秒,极端情况可能达到 4 - 5 秒。(统计日期 2021年12月28日,此时有部分已做工作还没有完成)
拿到 playInit 之后就只有两个步骤,new Player(),执行 play() 方法,因此整个的流程图如下:

然后再看下 Chrome Performance 在 FP 之后的详情截图

有图中看出大任务分别是

sessions 接口请求后的逻辑任务
play/init 接口请求后的逻辑任务
session_events 接口请求后的逻辑任务
执行 play 方法
网络请求耗时最长的是

事件流接口(session_events)
播放初始数据接口(play/init)
结合代码流程图和 Performance 截图,最后总结下当前存在问题:

session_events 接口(事件流)当前为异步执行,有可能会出现晚于视频播放的情况,不符合产品需求
session_events 接口本身存在数据耦合度太大导致下载时间慢的问题(需要和后端沟通方案)
sessions 接口(会话列表)没必要做为前置条件,现在之所以前置是因为其他接口依赖 sessionId,但 获取 sessionId 和 sessions 接口其实没有强关联关系

外部进入情况下,可以直接从 url 参数获取 sessionId
内部切换的情况下,可以直接获取 sessionId
session 接口(会话详情)在当前环境下只提供老版本视频资源 url ,和 play/init 功能重复
sources 接口和 paly/init 接口只依赖于 sessionId,没必要后置
拿到接口数据后的任务过大,耗时严重,因为播放前所有任务都必须执行完毕,因此异步并不能解决问题
优化方案
干掉 session 接口(会话详情),资源 url 统一放到 paly/init 接口,减少网络请求(需要后端配合)
并行执行 sessions 、sources 、session_events 和 paly/init 四个接口,减少相互不必要的等待
session_events 接口变为两个

第一个只获取标题信息减少资源下载时间,另一个获取全量数据(需要支持分页),播放前只通过第一个接口,加载标题信息
第二个接口异步请求全部数据存储到内存,用户展开的时读取,如果用户点击展开的时候全量数据还没有返回,则根据点击项的索引分页请求对应的数据之后再展开,此时全量数据过来后需要有去重策略
使用 Web Worker 将拿到数据之后的耗时任务丢到子线程中,并行执行
调度任务 + 时间切片

给不同的任务分配优先级,然后将一段长任务切片
尽量保证重要任务优先执行,其他任务或者无依赖关系任务次要执行(播放主要任务之外的其他任务)
事件流采用虚拟列表技术,减少渲染消耗(不仅仅解决首次渲染问题,还解决后续滚动性能问题)
增加缓存,采用 Service Worker 技术,缓存接口请求,谷歌的 Workbox 类库,当前版本只缓存 sources 、paly/init 和 session_events 三个接口和部分静态资源,后续逐步展开至全站缓存
ps:现阶段缓存已经添加完毕,产出 前端缓存机制提升网站性能 - Service Worker

最终流程图

声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议