性能优化
首先一些web指标,例如fcp,lcp等能帮助我们找到哪方面的不足,并且在后面的优化过后有一个对比的效果.并且性能优化的维度很广,对于不同场景应该具体分析
从打包上来看,拿webpack来说,使用webpack-bundle-analyse插件分析打包产物,对比较大的文件进行拆包,目的是保证不要太小或者太大,因为太大稍微修改之后整个文件就过期了,太小的话http请求会多发送,尽可能利用http缓存.最后使用gzip或者brotile压缩,当然依赖也可以直接放在cdn上
预操作,例如我们有使用的到图床,可以使用preconnect提前建立连接,如果有多个域名需要连接,则可以改用dns-prefetch.还可以使用preload或prefetch进行资源预加载
懒加载,他既能优化资源下载又能优化页面解析.懒加载在首次加载只展示最基础的元素,所以他能减少资源下载的数量,并且如果出现重拍重绘只涉及展示的元素
减少重排重绘,页面默认采用流式布局,这意味着任何元素大小位置变动都会对后面元素和内部元素产生影响,所以性能开销很大.所以不要频繁的读取布局信息,因为每读取一次就会重排一次.批量进行dom操作,可以将父元素display:none,修改结束后在将父元素置为可见,这样就只会触发一次重排.使用transform修改属性跳过重排.也可以使用虚拟列表来减少重排重绘
图片方面可以考虑webp或者avlf格式.对于比较小的图片可以内嵌base64.为了降低cls,图片尽可能设置固定宽高,
数据请求方面可以使用http2代替http1.1
代码上要写成支持tree-shaking的格式以及提高复用率变相降低打包体积,框架上都会提供一些性能优化相关的api
从指标inp来看,我们应该减少用户ui交互响应时间,这通常是因为长任务引起的阻塞,所以可以将长任务放到web woker里执行,甚至web worker里又可以创建其他线程,来加速任务的执行.充分利用现代计算机多核的优势.另一方面对于cpu密集型任务,js是一门解释型语言,我们可以利用web assembly直接提供二进制代码交给计算机执行
cdn分为边缘节点也叫pop节点,用于直接向用户提供服务,还有骨干节点,如果边缘节点没命中,就会向骨干节点请求,如果同样没有命中,则会触发回源.面对高并发可以提前进行cdn预热
从后端的角度上看,利用nginx中间层分发请求,并且中间层还有一个作用是负载均衡
还有一个比较通用的概念是缓存,不管是vue里的computed还是后端的三级缓存,cdn的二级缓存
实践
瀑布流组件
- 首先是图片方面,为了降低FCP,LCP
- 首先禁止上传gif格式。图片格式可以转成webp格式,按照华为云obs文档,在url上添加请求参数可以返回webp格式图片,但是这么请求的图片不会被http缓存,并且每请求一次都会重新进行格式转化,成本会大幅上升。所有需要上传的时候就将图片转成webp格式存放在桶里,返回的时候返回webp即可
- 按照屏幕宽度请求合适尺寸的图片,但会回到之前那个问题也就是不会被http缓存,这样虽然可以加速首屏,但是之后每次都需要重新获取。我是通过service worker来解决的。通过拦截指定的图片域名请求,如果没有命中会放入cache中,命中则从cache中取。当然也可以给缓存设置过期时间,类似于强缓存的效果
- 可以使用preconnect提前建立与obs域名的连接,当然如果需要与多个第三方域建立连接,全部preconnect可能会适得其反,可以替换为dns-prefetch
- 我观察到现在的协议是http1.1,可以使用http2解决队头阻塞的问题,当然http2也只是解决了http层的阻塞,tcp层的阻塞没有解决
2.代码或者框架层面
- 我是通过fetch拿到图片信息,之前是在for循环了一个一个await拿到,但是可以并发发送请求.因为每个网站都有最大连接数,需要写一个限制最大并发数的函数
- 懒加载我是通过自定义指令和intersectionobserve实现的,每个图片里都会new 一个intersectionobserve,我想使用一个单例实现,并且我在mdn看了文档,发现它可以observe某个元素,不需要可以unobserve.证明这是可行的,所以我封装了一个hook用来创建intersectionobserve实例.这样只需要一个实例既可完成监听功能
- 对于resizeObserve,因为他的触发频率会很高,所以使用了节流,并且将回调放在requestAnimationFrame里,还有就是我只关心它的宽度,如果宽度不变,高度改变,不会触发回调,这在初始化的时候高度会变化
- 框架上则是使用computed缓存了一些计算结果,有使用到watch,但是这个watch只是第一次有用,使用了vue一个比较新的配置项once.来实现一次监听即销毁
- 最后在umounted生命周期释放之前那两个web api实例的内存占用
用户体验上
- ui上的要求是滚动时隐藏搜索条件,使用了vue的内置组件transion实现了v-if的动画。
- 对于瀑布流组件需要先获取数据才能渲染,为了视觉不会太突兀,这里准备了一个骨架屏
性能指标 整体得分是从93到96,fcp提升12.5%,lcp提升18.75%,speed index提升了27.27%
问题,重复接口请求 如果第一页的数据没有铺满屏幕,就会请求两次接口
首先第一次接口拿到数据,然后渲染出来,这个时候intersectionobserve监听到,发送下一次请求,所以我根据第一次接口返回的数据判断是否需要请求下一页(根据total和record.length < pagesize.length),将这个属性传递给瀑布流组件,是否需要请求下一页