背景是在某项目中的其中一个页面,接入现场真实数据后,加载时间很长,长达10几秒,严重影响用户体验。
在chrome
的DevTools
中,清缓存硬加载,可模拟用户第一次访问页面的场景。根据Network
中的若干指标,对性能瓶颈初步判断:前端
requests
:在HTTP / 1.0
或HTTP / 1.1
链接上,Chrome
每一个主机最多容许六个同时TCP
链接,因此请求数过多会致使TTFB
等待时间过长。xhr
:ajax
接口时间过长,瓶颈在于后端接口。Finish
:Finish
时间远远大于DOMContentLoaded
和Load
时间,说明页面中的请求资源很大HTTP 2
的多路复用特性,合并请求;服务端渲染。ajax
接口时间过长,主要在后端接口的优化,Nodejs(BFF层)基于微服务的能力,因此要加快服务调用速度,减小服务调用数量,在业务上进行优化(缓存、批量接口、非必要数据异步请求...),固然自身代码层的运行效率也要考虑。对比 PNG 原图、PNG 无损压缩、PNG 转 WebP(无损)、PNG 转 WebP(有损)的压缩效果图
loading
动画、图片的渐进式渲染(浏览器对一张图片的加载顺序基本上是下载了多少展现多少,让用户感受很刻板生硬,渐进式渲染就是图片的内容从模糊到清晰的过程)、预加载(预见性的加载一些不可见区域的资源,提升用户在快速滚动浏览器时候的体验)。上述优化方向中,做者优化了Nodejs
层的api
接口时间,缓存了部分业务逻辑,剥离了部分底层接口使其异步获取;同时选择懒加载优化方向,对前端组件及图片资源的加载进行优化。优化结果,肉眼可见。
懒加载并非一个新鲜的名词,顾名思义,就是懒,如今不加载,稍后再加载,换个词说就是,按需加载。由于不少场景下,暂时看不见用不到的资源是不须要同时加载的,浪费时间开支同时,也消耗了没必要要的CPU
、IO
等资源。例如图片懒加载在jQuery
时代就已经十分普及,在react
、vue
等前端MV*
框架的出现后,组件懒加载,SPA
单页应用中的路由懒加载,webpack
中对初始化不须要加载的代码块进行懒加载,从而优化性能...如下做者重点介绍下图片懒加载及vue
组件懒加载。vue
对于一些视频图片web应用,图片懒加载几乎是必需要作的,能够大大提高用户体验。
img
标签的src
设置缩略图或者不设置src
,这里的占位图能够是缺省图,loading图;做者认为这是懒加载最重要的环节
MDN中的定义:
Element.getBoundingClientRect()
方法返回元素的大小及其相对于视口的位置。
// 获取元素的getBoundingClientRect属性 const rect = Element.getBoundingClientRect(); if(rect.top < document.documentElement.clientHeight) { // 将top值与页面的clientHeight进行对比,若小于则为可视区域 ... }
PS:该方案须要监听scroll
事件,注意节流处理。react
MDN中的定义:IntersectionObserver
接口 (从属于Intersection Observer API
) 提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport
)交叉状态的方法。Intersection Observer API 容许你配置一个回调函数,每当目标(target)元素与设备视窗或者其余指定元素发生交集的时候执行。设备视窗或者其余元素咱们称它为根元素或根(root)。
var options = { root: document.querySelector('#scrollArea'), rootMargin: '0px', threshold: 1.0 // 目标(target)元素与根(root)元素之间的交叉度是交叉比(intersection ratio), 取值在0.0和1.0之间 } var observer = new IntersectionObserver(() => { // 回调函数,当目标元素和根元素交叉时触发 ... }, options); var target = document.querySelector('#listItem'); // 添加目标元素,与根元素进行交叉状态比对 observer.observe(target);
PS:该方案较前者的优势就是不须要监听,其实兼容性在chrome中还不错。webpack
这里的懒加载判断依据和图片相似,一样是要判断可视或者即将可视的时机,来控制组件的加载与否。当加载条件为false
时,不作渲染,为true
时则渲染,这里用v-if
指令就能够实现。git
在条件切换的同时,最好加入相似骨架屏的页面,来过渡用户体验。github
社区里这样的方案有不少,评估后决定采用 vue-lazyload,star 5.7k,recent updates is 2 months ago,很稳。
npm i vue-lazyload -S // 在入口js中引入依赖,注册在vue实例上 import VueLazyload from 'vue-lazyload' Vue.use(VueLazyload, { lazyComponent: true });
这里简单提一下该方案的组件懒加载方案,在源码L11-L16,利用render
来生成组件的内容this.$slots.default
,十分巧妙。web
render (h) { if (this.show === false) { return h(this.tag) } return h(this.tag, null, this.$slots.default) }
// 原代码 <div class="camera-card-img" :style="{'backgroundImage': 'url(' + data._thumbnails + ')'}"> // 加入图片懒加载逻辑 <div class="camera-card-img" v-lazy:background-image="data._thumbnails"> <lazy-component> // 须要懒加载的组件 ... </lazy-component>
52 requests, 19 imgajax
38 requests, 12 imgchrome
当浏览器继续滚动的时候,图片依次加载,能够看到network中的request逐步增长至52,说明加载了剩余图片。npm
组件懒加载也是一样的效果,数据量小可能页面finish时间差感知不明显,能够加大模拟量至上千:
Finish
时间10s,
Finish
时间4s,速度提高显著。
PS:这里先后请求数不变的缘由,是做者在模拟数据的时候重复了若干次真实数据,致使资源地址都是重复的,浏览器会缓存请求,因此致使请求数不变。
其实能够看到,数据量大的时候,加载速度依旧很慢,还须要继续优化,能够从如下几个方向:
优化无止境,经常是花了大力气,收效甚微。须要考虑时间和资源成本,优先解决投入产出比高的优化方向。以上是做者在实际项目中遇到的优化问题,仅供你们参考。