性能的计算⽅式
确认⾃⼰需要关注的指标
常⻅的指标有:
- ⻚⾯总加载时间 load
- ⾸屏时间
- ⽩屏时间
代码 尝试⽤⼀个指令, 挂载在重要元素上, 当此元素inserted就上报
各个属性所代表的含义
- connectStart, connectEnd
分别代表TCP建⽴连接和连接成功的时间节点。如果浏览器没有进⾏TCP连接(⽐如使⽤持久化连接webscoket),则两者都等于domainLookupEnd;- domComplete
Html⽂档完全解析完毕的时间节点;- domContentLoadedEventEnd
代表DOMContentLoaded事件触发的时间节点:⻚⾯⽂档完全加载并解析完毕之后,会触发DOMContentLoaded事件,HTML⽂档不会等待样式⽂件,图⽚⽂件,⼦框架⻚⾯的加载(load事件可以⽤来检测HTML⻚⾯是否完全加载完毕(fully-loaded))。- domContentLoadedEventStart代表DOMContentLoaded事件完成的时间节点,此刻⽤⼾可以对⻚⾯进⾏操作,也就是jQuery中的domready时间;
- domInteractive
代表浏览器解析html⽂档的状态为interactive时的时间节点。domInteractive并⾮DOMReady,它早于DOMReady触发,代表html⽂档解析完毕(即dom tree创建完成)但是内嵌资源(⽐如外链css、js等)还未加载的时间点;- domLoading
代表浏览器开始解析html⽂档的时间节点。我们知道IE浏览器下的document有readyState属性,domLoading的值就等于readyState改变为loading的时间节点;- domainLookupStart domainLookupEnd
分别代表DNS查询的开始和结束时间节点。如果浏览器没有进⾏DNS查询(⽐如使⽤了cache),则两者的值都等于fetchStart;- fetchStart
是指在浏览器发起任何请求之前的时间值。在fetchStart和domainLookupStart之间,浏览器会检查当前⽂档的缓存;- loadEventStart, loadEventEnd
分别代表onload事件触发和结束的时间节点- navigationStart
- redirectStart, redirectEnd
如果⻚⾯是由redirect⽽来,则redirectStart和redirectEnd分别代表redirect开始和结束的时间节点;- requestStart
代表浏览器发起请求的时间节点,请求的⽅式可以是请求服务器、缓存、本地资源等;- responseStart, responseEnd
分别代表浏览器收到从服务器端(或缓存、本地资源)响应回的第⼀个字节和最后⼀个字节数据的时刻;- ecureConnectionStart
可选。如果⻚⾯使⽤HTTPS,它的值是安全连接握⼿之前的时刻。如果该属性不可⽤,则返回undefined。如果该属性可⽤,但没有使⽤HTTPS,则返回0;- unloadEventStart, unloadEventEnd
如果前⼀个⽂档和请求的⽂档是同⼀个域的,则unloadEventStart和unloadEventEnd分别代表浏览器unload前⼀个⽂档的开始和结束时间节点。否则两者都等于0;
性能优化
- 加速或减少HTTP请求损耗:使⽤CDN加载公⽤库,使⽤强缓存和协商缓存,⼩图⽚使⽤Base64代替,⻚⾯内跳转其他域名或请求其他域名的资源时使⽤浏览器prefetch预解析等;
- 延迟加载:⾮重要的库、⾮⾸屏图⽚延迟加载,SPA的组件懒加载等;
- 减少请求内容的体积:开启服务器Gzip压缩,JS、CSS⽂件压缩合并,减少cookies⼤⼩,SSR直接输出渲染后的HTML等;
- 浏览器渲染原理:优化关键渲染路径,尽可能减少阻塞渲染的JS、CSS;
- 优化⽤⼾等待体验:⽩屏使⽤加载进度条、菊花图、⻣架屏代替等;
本地如何查看⻚⾯性能?
chrome performance
chrome-extension的请求混杂在⻚⾯请求中,难以分析filter中填写 -scheme:chrome-extension 即可过滤掉插件请求
webpack打包分析
webpack-bundle-analyzer插件,代码:vue.config.js + package.json
如何监控性能变化?
⽇志上报 阿⾥云演⽰
pagespeed https://developers.google.com/speed/pagespeed/insights/?hl=zh-cn
数据分析 90分位 50分位 TP50、TP90和TP99等指标常⽤于系统性能监控场景,指⾼于50%、90%、99%等百分线的情况。
具体的优化⽅式
浏览器渲染原理
和渲染息息相关的主要有
- GUI渲染线程
GUI渲染线程负责渲染浏览器界⾯HTML元素,当界⾯需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执⾏。
在Javascript引擎运⾏脚本期间,GUI渲染线程都是处于挂起状态的,也就是说被冻结了.- Js引擎线程
JS为处理⻚⾯中⽤⼾的交互,以及操作DOM树、CSS样式树来给⽤⼾呈现⼀份动态⽽丰富的交互体验和服务器逻辑的交互处理。
GUI渲染线程与JS引擎线程互斥的,是由于JavaScript是可操纵DOM的,如果在修改这些元素属性同时渲染界⾯(即JavaScript线程和UI线程同时运⾏),那么渲染线程前后获得的元素数据就可能不⼀致。
当JavaScript引擎执⾏时GUI线程会被挂起,GUI更新会被保存在⼀个队列中等到引擎线程空闲时⽴即被执⾏。
由于GUI渲染线程与JS执⾏线程是互斥的关系,当浏览器在执⾏JS程序的时候,GUI渲染线程会被保存在⼀个队列中,直到JS程序执⾏完成,才会接着执⾏。
因此如果JS执⾏的时间过⻓,这样就会造成⻚⾯的渲染不连贯,导致⻚⾯渲染加载阻塞的感觉。- tips:
回流(reflow):当浏览器发现某个部分发⽣了点变化影响了布局,需要倒回去重新渲染。reflow 会从这个 root frame 开始递归往下,依次计算所有的结点⼏何尺⼨和位置。reflow ⼏乎是⽆法避免的。现在界⾯上流⾏的⼀些效果,⽐如树状⽬录的折叠、展开(实质上是元素的显⽰与隐藏)等,都将引起浏览器的 reflow。⿏标滑过、点击……只要这些⾏为引起了⻚⾯上某些元素的占位⾯积、定位⽅式、边距等属性的变化,都会引起它内部、周围甚⾄整个⻚⾯的重新渲染。
重绘(repaint):改变某个元素的背景⾊、⽂字颜⾊、边框颜⾊等等不影响它周围或内部布局的属性时,屏幕的⼀部分要重画,但是元素的⼏何尺⼨没有变。
渲染流程主要分为以下4步:
- 解析HTML⽣成DOM树 - 渲染引擎⾸先解析HTML⽂档,⽣成DOM树
- 构建Render树 - 接下来不管是内联式,外联式还是嵌⼊式引⼊的CSS样式会被解析⽣成CSSOM树,根据DOM树与CSSOM树⽣成另外⼀棵⽤于渲染的树-渲染树(Render tree),
- 布局Render树 - 然后对渲染树的每个节点进⾏布局处理,确定其在屏幕上的显⽰位置
- 绘制Render树 - 最后遍历渲染树并⽤UI后端层将每⼀个节点绘制出来现代浏览器总是并⾏加载资源,例如,当 HTML 解析器(HTML Parser)被脚本阻塞时,解析器虽然会停⽌构建 DOM,但仍会识别该脚本后⾯的资源,并进⾏预加载。
同时,由于下⾯两点:- CSS 被视为渲染阻塞资源(包括JS),这意味着浏览器将不会渲染任何已处理的内容,直⾄CSSOM 构建完毕,才会进⾏下⼀阶段。
- JavaScript 被认为是解释器阻塞资源,HTML解析会被JS阻塞,它不仅可以读取和修改 DOM 属性,还可以读取和修改 CSSOM 属性。
所以存在阻塞的 CSS 资源时,浏览器会延迟 JavaScript 的执⾏和 DOM 构建。另外:- 当浏览器遇到⼀个 script 标记时,DOM 构建将暂停,直⾄脚本完成执⾏。
- JavaScript 可以查询和修改 DOM 与 CSSOM。
- CSSOM 构建时,JavaScript 执⾏将暂停,直⾄ CSSOM 就绪。css资源和js资源的放置位置
所以,script 标签的位置很重要。实际使⽤时,可以遵循下⾯两个原则:- CSS 优先:引⼊顺序上,CSS 资源先于 JavaScript 资源。
- JavaScript 应尽量少影响 DOM 的构建。
js的异步执⾏
defer async
都是异步加载js资源, 但是区别是async加载完资源后会⽴即开始执⾏, ⽽defer会在整个document解析完成后执⾏其他
⼀、图⽚资源优化
- 懒加载
图⽚懒加载
tips: 如何判断元素是否在可视区域内?
原理:默认给图⽚设置⼀个兜底图, 监听⻚⾯滚动, 判断图⽚进⼊可视区域内后, 则给图⽚设置真实的src- 预加载图⽚等资源
const img = new Image();
img.src= 'xxxxxx';
<img src="http://pic26.nipic.com/20121213/6168183 0044449030002.jpg" style="display:none"/>
- webp, 渐进式加载
https://help.aliyun.com/document_detail/171050.html?
spm=5176.10695662.1996646101.searchclickresult.2b9b75d6NiTHLQ
https://help.aliyun.com/document_detail/44704.html?
spm=a2c4g.11186623.6.1428.4acb1ecfHEUVE6
⼆、其他静态资源加载优化
- quickLink
https://github.com/GoogleChromeLabs/quicklink - prefetch
link(rel=“dns-prefetch”, href=“//www.baidu.com”)
link(rel=“preconnect”, href=“//www.baidu.com”)
link(rel=“preload”, as=“script”, href=xxxxx)
link(rel=“preload”, as=“image”, href=xxxxxx) - 静态资源压缩
gzip, Brotli https://help.aliyun.com/document_detail/27127.html?spm=a2c4g.11186623.6.645.70471769PeSCe6 - webpack
打包优化 https://webpack.docschina.org/configuration/optimization/ - 服务端渲染
同构渲染
pug模板渲染 - 动态polyfill
为了兼容低版本浏览器, 我们⼀般会引⼊polyfill。但是@babel/polyfill这个包⾮常⼤, gzip之后都有27.7k, ⾮常影响体积.
https://polyfill.io/v3/api/
阿⾥云cdn: https://polyfill.alicdn.com/polyfill.min.js?features=es6,es7,es2017,es2018&flags=gated
<script src="https://polyfill.io/v3/polyfill.js?features=es5,es6,es7,es2017,es2018&flags=gated&callback=invokeMain">
function invokeMain() {
var scriptElement = document.createElement('script');
scriptElement.type = 'text/javascript';
scriptElement.async = true;
scriptElement.src = 'xxxxxxxxx';
document.body.appendChild(scriptElement);
}
- 不重要的资源延迟加载
可以放到window.addEventListener(‘load’, () => {}) - 节流 防抖
节流函数:当持续触发事件时,保证⼀定时间段内只调⽤⼀次事件处理函数。
防抖函数:当持续触发事件时,⼀定时间段内没有再触发事件,事件处理函数才会执⾏⼀次,如果设定的时间到来之前,⼜⼀次触发了事件,就重新开始延时。
函数节流不管事件触发有多频繁,都会保证在规定时间内⼀定会执⾏⼀次真正的事件处理函数
函数防抖只是在最后⼀次事件后才触发⼀次函数。
⽐如在⻚⾯的⽆限加载场景下,我们需要⽤⼾在滚动⻚⾯时,每隔⼀段时间发⼀次 Ajax 请求,⽽不是在⽤⼾停下滚动⻚⾯操作时才去请求数据。这样的场景,就适合⽤节流技术来实现。
⽽⽐如搜索框, 输⼊结束后才发起请求, 就适合⽤防抖