其他优化手段
# 如何根据chrome
的timing
优化重要
性能优化API
Performance
。performance.now()
与new Date()
区别,它是高精度的,且是相对时间,相对于页面加载的那一刻。但是不一定适合单页面场景window.addEventListener("load", "");
window.addEventListener("domContentLoaded", "");
Img
的onload
事件,监听首屏内的图片是否加载完成,判断首屏事件RequestAnmationFrame
和RequestIdleCallback
IntersectionObserver
、MutationObserver
,PostMessage
Web Worker
,耗时任务放在里面执行
检测工具
Chrome Dev Tools
Page Speed
Jspref
window.onload = function(){
setTimeout(function(){
let t = performance.timing
console.log('DNS查询耗时 :' + (t.domainLookupEnd - t.domainLookupStart).toFixed(0))
console.log('TCP链接耗时 :' + (t.connectEnd - t.connectStart).toFixed(0))
console.log('request请求耗时 :' + (t.responseEnd - t.responseStart).toFixed(0))
console.log('解析dom树耗时 :' + (t.domComplete - t.domInteractive).toFixed(0))
console.log('白屏时间 :' + (t.responseStart - t.navigationStart).toFixed(0))
console.log('domready时间 :' + (t.domContentLoadedEventEnd - t.navigationStart).toFixed(0))
console.log('onload时间 :' + (t.loadEventEnd - t.navigationStart).toFixed(0))
if(t = performance.memory){
console.log('js内存使用占比 :' + (t.usedJSHeapSize / t.totalJSHeapSize * 100).toFixed(2) + '%')
}
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 懒加载
概念:
懒加载也叫做延迟加载、按需加载,指的是在长网页中延迟加载图片数据,是一种较好的网页性能优化的方式。在比较长的网页或应用中,如果图片很多,所有的图片都被加载出来,而用户只能看到可视窗口的那一部分图片数据,这样就浪费了性能
如果使用图片的懒加载就可以解决以上问题。在滚动屏幕之前,可视化区域之外的图片不会进行加载,在滚动屏幕时才加载。这样使得网页的加载速度更快,减少了服务器的负载。懒加载适用于图片较多,页面列表较长(长列表)的场景中。
特点:
减少无用资源的加载,减少服务器的压力和流量
提升用户体验,减少用户等待时间
防止加载过多图片而影响其他资源文件的加载
实现原理:
图片的加载是由 src
引起的,当对src
赋值时,浏览器就会请求图片资源。根据这个原理,我们使用HTML5
的 data-xxx
属性来储存图片的路径,在需要加载图片的时候,将data-xxx
中图片的路径赋值给src
,这样就实现了图片的按需加载,即懒加载。
注意:data-xxx
中的 xxx
可以自定义,这里我们使用 data-src
来定义。
懒加载的实现重点在于确定用户需要加载哪张图片,在浏览器中,可视区域内的资源就是用户需要的资源。所以当图片出现在可视区域时,获取图片的真实地址并赋值给图片即可。
使用原生JavaScript
实现懒加载:
知识点:
(1)window.innerHeight
是浏览器可视区的高度
(2)document.body.scrollTop || document.documentElement.scrollTop
是浏览器滚动的过的距离
(3)imgs.offsetTop
是元素顶部距离文档顶部的高度(包括滚动条的距离)
(4)图片加载条件:img.offsetTop < window.innerHeight + document.body.scrollTop
<div class="container">
<img src="loading.gif" data-src="pic.png">
<img src="loading.gif" data-src="pic.png">
<img src="loading.gif" data-src="pic.png">
<img src="loading.gif" data-src="pic.png">
<img src="loading.gif" data-src="pic.png">
<img src="loading.gif" data-src="pic.png">
</div>
<script>
var imgs = document.querySelectorAll('img');
function lozyLoad(){
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
var winHeight= window.innerHeight;
for(var i = 0; i < imgs.length; i++){
if(imgs[i].offsetTop < scrollTop + winHeight){
imgs[i].src = imgs[i].getAttribute('data-src');
}
}
}
window.onscroll = lozyLoad();
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 懒加载和预加载的区别
这两种方式都是提高网页性能的方式,两者主要区别是一个是提前加载,一个是迟缓甚至不加载。懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。
- 懒加载也叫延迟加载,指的是在长网页中延迟加载图片的时机,当用户需要访问时,再去加载,这样可以提高网站的首屏加载速度,提升用户的体验,并且可以减少服务器的压力。它适用于图片很多,页面很长的电商网站的场景。懒加载的实现原理是,将页面上的图片的
src
属性设置为空字符串,将图片的真实路径保存在一个自定义属性中,当页面滚动的时候,进行判断,如果图片进入页面可视区域内,则从自定义属性中取出真实路径赋值给图片的src
属性,以此来实现图片的延迟加载。 - 预加载指的是将所需的资源提前请求加载到本地,这样后面在需要用到时就直接从缓存取资源。 通过预加载能够减少用户的等待时间,提高用户的体验。我了解的预加载的最常用的方式是使用
js
中的image
对象,通过为image
对象来设置src
属性,来实现图片的预加载。
# link
标签的预处理重要
dns-prefetch
。当 link
标签的 rel
属性值为“dns-prefetch
”时,浏览器会对某个域名预先进行 DNS 解析并缓存
。这样,当浏览器在请求同域名资源的时候,能省去从域名查询 IP
的过程,从而减少时间损耗
。下图是淘宝网设置的 DNS
预解析
preconnect
。让浏览器在一个HTTP
请求正式发给服务器前预先执行一些操作,这包括DNS 解析、TLS 协商、TCP 握手
,通过消除往返延迟来为用户节省时间prefetch/preload
。两个值都是让浏览器预先下载并缓存某个资源
,但不同的是,prefetch 可能会在浏览器忙时被忽略
,而preload 则是一定会被预先下载
。prerender
。浏览器不仅会加载资源,还会解析执行页面,进行预渲染
# 介绍一下什么是回流和重绘特别重要
回流
当渲染树中部分或者全部元素的尺寸、结构或者属性发生变化时,浏览器会重新渲染部分或者全部文档的过程就称为回流。
下面这些操作会导致回流:
页面的首次渲染
浏览器的窗口大小发生变化
元素的内容发生变化
元素的尺寸或者位置发生变化
元素的字体大小发生变化
激活
CSS
伪类查询某些属性或者调用某些方法
添加或者删除可见的
DOM
元素
在触发回流(重排)的时候,由于浏览器渲染页面是基于流式布局的,所以当触发回流时,会导致周围的DOM
元素重新排列,它的影响范围有两种:
- 全局范围:从根节点开始,对整个渲染树进行重新布局
- 局部范围:对渲染树的某部分或者一个渲染对象进行重新布局
重绘
当页面中某些元素的样式发生变化,但是不会影响其在文档流中的位置时,浏览器就会对元素进行重新绘制,这个过程就是重绘。
下面这些操作会导致回流:
color
、background
相关属性:background-color
、background-image
等outline
相关属性:outline-color
、outline-width
、text-decoration
border-radius
、visibility
、box-shadow
注意: 当触发回流时,一定会触发重绘,但是重绘不一定会引发回流。
什么是流式布局
页面元素的宽度按照屏幕分辨率进行适配调整,但整体布局不变。通常宽度是以%
来表示,但是设置了最大宽度max-width
和最小宽度min-width
,分辨率发生变化的话元素大小会变,但是整体布局不变,缺点很明显是屏幕尺寸跨度大不能正常显示出来(页面元素被拉得很长,但是高度和文字大小不变)
# 如何避免回流和重绘重要
减少回流与重绘的措施:
操作
DOM
时,尽量在低层级的DOM
节点进行操作不要使用
table
布局, 一个小的改动可能会使整个table
进行重新布局使用
CSS
的表达式不要频繁操作元素的样式,对于静态页面,可以修改类名,而不是样式。
使用
absolute
或者fixed
,使元素脱离文档流,这样他们发生变化就不会影响其他元素避免频繁操作
DOM
,可以创建一个文档片段documentFragment
,在它上面应用所有DOM
操作,最后再把它添加到文档中将元素先设置
display: none
,操作结束后再把它显示出来。因为在display
属性为none
的元素上进行的DOM
操作不会引发回流和重绘。将
DOM
的多个读操作(或者写操作)放在一起,而不是读写操作穿插着写。这得益于浏览器的渲染队列机制。
浏览器针对页面的回流与重绘,进行了自身的优化——渲染队列
浏览器会将所有的回流、重绘的操作放在一个队列中,当队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会对队列进行批处理。这样就会让多次的回流、重绘变成一次回流重绘。
上面,将多个读操作(或者写操作)放在一起,就会等所有的读操作进入队列之后执行,这样,原本应该是触发多次回流,变成了只触发一次回流。
# 如何优化动画
对于如何优化动画,我们知道,一般情况下,动画需要频繁的操作DOM
,就就会导致页面的性能问题,我们可以将动画的 position
属性设置为 absolute
或者 fixed
,将动画脱离文档流,这样他的回流就不会影响到页面了。
# documentFragment
是什么?用它跟直接操作dom
有什么区别重要
概念
DocumentFragment
,文档片段接口,一个没有父对象的最小文档对象。它被作为一个轻量版的 Document
使用,就像标准的document
一样,存储由节点(nodes
)组成的文档结构。与document
相比,最大的区别是DocumentFragment
不是真实 DOM
树的一部分,它的变化不会触发 DOM
树的重新渲染,且不会导致性能等问题。
当我们把一个 DocumentFragment
节点插入文档树时,插入的不是 DocumentFragment
自身,他只是相当于一个占位符,而是它的所有子孙节点。在频繁的DOM
操作时,我们就可以将DOM
元素插入DocumentFragment
,之后一次性的将所有的子孙节点插入文档中。和直接操作DOM
相比,将DocumentFragment
节点插入DOM
树时,不会引起DOM
树的重新渲染(reflow
),从而减少回流(一次append
只回流一次)
# 介绍一下防抖和节流重要
- 函数防抖是指在事件被触发
n
秒后再执行回调,如果在这n
秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。 - 函数节流是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。节流可以使用在
scroll
函数的事件监听上,通过事件节流来降低事件调用的频率
防抖函数的应用场景:
按钮提交场景:防⽌多次提交按钮,只执⾏最后提交的⼀次
服务端验证场景:表单验证需要服务端配合,只执⾏⼀段连续的输⼊事件的最后⼀次,还有搜索联想词功能类似⽣存环境请⽤
lodash.debounce
节流函数的适⽤场景:
拖拽场景:固定时间内只执⾏⼀次,防⽌超⾼频次触发位置变动
缩放场景:监控浏览器
resize
动画场景:避免短时间内多次触发动画引起性能问题
# 图片优化
不用图片。很多时候会使用到很多修饰类图片,其实这类修饰图片完全可以用
CSS
去代替。对于移动端来说,屏幕宽度就那么点,完全没有必要去加载原图浪费带宽。一般图片都用
CDN
加载,可以计算出适配屏幕的宽度,然后去请求相应裁剪好的图片。小图使用
base64
格式将多个图标文件整合到一张图片中(雪碧图)
选择正确的图片格式:
- 对于能够显示
WebP
格式的浏览器尽量使用WebP
格式。因为WebP
格式具有更好的图像数据压缩算法,能带来更小的图片体积,而且拥有肉眼识别无差异的图像质量,缺点就是兼容性并不好 - 小图使用
PNG
,其实对于大部分图标这类图片,完全可以使用SVG
代替 - 照片使用
JPEG
- 对于能够显示
# 移动端优化
# 面试回答性能优化怎么显得有条理
分点作答
让文件加载更快
- 让传输的数据包更小(压缩文件/图片):图片压缩和文件压缩
- 减少网络请求的次数:雪碧图/精灵图、节流防抖
- 减少渲染的次数:缓存(
HTTP
缓存、本地缓存、Vue
的keep-alive
缓存等)
让文件渲染更快
- 提前渲染:
ssr
服务器端渲染 - 避免渲染阻塞:
CSS
放在HTML
的head
中JS
放在HTML
的body
底部 - 避免无用渲染:懒加载
- 减少渲染次数:对
dom
查询进行缓存、将dom
操作合并、使用减少重排的标签
或者可以这样
其实可以参照那道输入
url
到显示全过程的问题
DNS
的预解析(dns-prefetch
)(尽量放在网页的前面)- 缓存可以避免重复加载资源
etc
不变的资源用强缓存(expires
,cache-control:max-age
)... 顺序cc>expire
- 后面唠唠协商缓存
Last-Modified IF-Modified-Since
Etag IF-None-Match
- 后面唠唠协商缓存
这里要注意一下
大多数候选人都只答概念,我们可以从什么文件出发谈一下选型
- 对于某些不需要缓存的资源,可以使用
Cache-control: no-store
,表示该资源不需要缓存 - 对于频繁变动的资源,可以使用
Cache-Control: no-cache
并配合ETag
使用,表示该资源已被缓存,但是每次都会发送请求询问资源是否更新。 - 对于代码文件来说,通常使用
Cache-Control: max-age=31536000
并配合策略缓存使用,然后对文件进行指纹处理,一旦文件名变动就会立刻下载新的文件
讲完缓存可以
HTTP2
的多路复用,让多个请求复用同一个TCP
连接,还有2.0
的头信息压缩预加载:
preload
可以对当前页面所需的脚本、样式等资源进行预加载预加载:有些资源不需要马上用到,但是希望尽早获取,降低首屏加载时间
<!-- 强制浏览器请求资源,并且不会阻塞 onload 事件 --> <link rel="preload" href="http://example.com">
1
2这里可以顺便提一嘴
可以在当前页加载下一页的资源,提升速度
预渲染:将下载的文件预先在后台渲染,可以使用以下代码开启预渲染
<!-- 虽然可以提高页面的加载速度,但是要确保该页面百分百会被用户在之后打开,否则就白白浪费资源去渲染 --> <link rel="prerender" href="http://crucials.com">
1
2图片懒加载(监听
scroll
、图片data-src
)图片加载
cdn
加载- 小图用
base64
- 多图标用雪碧图
- 支持
webp
的浏览器用webp
- 用
svg
代替png
- 照片用
jpeg
其他文件优化
css
放head
- 服务端开启
gzip
script
的defer
和async
- 使用
webworker
另开线程执行脚本不阻塞渲染 - 静态资源用
cdn
加载,避免cdn
的域名和主站相同致携带cookies
运行时优化
content
方面:减少http
请求,css
精灵图减少dns
查询css
选择器...(尽量不嵌套..css3
开启GPU
,使用link
代替@import
)- 重排重绘...
- 动画优化(使用
requestAnimationFrame
代替setTimeout
)
webpack
优化webpack4+
打包自动开启代码压缩- 使用
es6
模块开启tree shaking
- 路由拆分代码,按需加载
- 打包的文件添加
hash
配合ETag
监控(待完善)
对于代码运行错误,通常的办法是使用
window.onerror
拦截报错。该方法能拦截到大部分的详细报错信息,但是也有例外- 对于跨域的代码运行错误会显示
Script error
. 对于这种情况我们需要给script
标签添加crossorigin
属性 - 对于某些浏览器可能不会显示调用栈信息,这种情况可以通过
arguments.callee.caller
来做栈递归 - 对于异步代码来说,可以使用
catch
的方式捕获错误。比如Promise
可以直接使用catch
函数,async await
可以使用try catch
- 但是要注意线上运行的代码都是压缩过的,需要在打包时生成
sourceMap
文件便于debug
。 - 对于捕获的错误需要上传给服务器,通常可以通过
img
标签的src
发起一个请求⬇️
- 对于跨域的代码运行错误会显示