参考:【前端工程师面试宝典】学习说明_互联网校招面试真题面经汇总_牛客网 (nowcoder.com)
方面:加载优化、执行优化、渲染优化、样式优化、脚本优化
加载优化:减少HTTP请求、缓存资源、压缩代码、无阻塞、首屏加载、按需加载、预加载、压缩图像、减少Cookie、避免重定向、异步加载第三方资源
执行优化:CSS写在头部,JS写在尾部并异步、避免img、iframe等的src为空、尽量避免重置图像大小、图像尽量避免使用DataURL
渲染优化:设置viewport、减少DOM节点、优化动画、优化高频事件、GPU加速
样式优化:避免在HTML中书写style、避免CSS表达式、移除CSS空规则、正确使用display:display、不滥用float等
脚本优化:减少重绘和回流、缓存DOM选择与计算、缓存.length的值、尽量使用事件代理、尽量使用id选择器、touch事件优化
(1)减少HTTP请求:
1.图片请求压缩:css精灵图(sprite)其实就是通过将多个图片融合到一张图里面,然后通过CSS background背景定位技术技巧布局网页背景。
2.页面组件初始化元素不要过多,合并css和js进行请求(单独合并)
页面的请求数(首次加载同时请求数不能超过4个),移动设备浏览器同时响应请求为4个请求(Android支持4个,iOS5+支持6个)
(2)缓存资源利用:
1.所有静态资源都要在服务器端设置缓存,并且尽量使用长缓存(使用时间戳更新缓存),缓存的命中机制–>浏览器章节(寻找访问内容报文结果)
(3)压缩代码:
1.代码体积优化,例如常见库包的min形式,减少不必要的注释段等。
(4)标签阻塞:
1.link引起的阻塞,预处理解析器解析到link标签,阻塞dom的渲染,阻塞的是cssdom的解析过程,但是会延缓render tree的生成,所以会阻塞渲染。
2.script标签内可以操纵dom元素,可以引起页面的回流重绘,所以script标签与GUI渲染线程是与js线程互斥的,也是减少重构的次数,script会阻塞dom解析(避免重复解析)和渲染。
3.link标签阻塞执行栈,js运行时,有可能会请求样式信息,如果此时还没有加载和解析样式,js就有可能会得到错误的回复,产生很多问题。因此浏览器在标签的加载和解析过程中,会禁止脚本运行。
(5)首屏加载优化,后台加载次要信息。
(6)按需加载:import处理
1懒加载实现(判断页面滚动调节自适应引入资源),对引入资源标签体的链接置空,防止预处理解析扫描到进行引入。
2media Query加载?
(7)图像的压缩,
- 使用TinyJpg和TinyPng压缩图像
- 使用CSS3、SVG、IconFont代替图像
- 使用img的srcset按需加载图像
- 选择合适的图像:webp优于jpg,png8优于gif
- 选择合适的大小:首次加载不大于1014kb、不宽于640px
- PS切图时D端图像保存质量为80,M端图像保存质量为60
(8)减少cookie 重定向和异步加载(服务崩溃阻塞)
(1)css头部,js尾部并且异步(js标签的阻塞性质)
(2)避免src为空,空src会重新加载当前页面,影响速度和效率
(3)减少重绘次数,减少相关引起重绘的操作。
(4)DataURL图像没有使用图像的压缩算法,文件会变大,并且要解码后再渲染,加载慢耗时长
(1)设置viewport:HTML的viewport可加速页面的渲染(定义视口,可以进行到视口处时候再渲染?)
(2)减少DOM节点:数据结构冗余,处理复杂度,基础属性继承。
(3)优化动画:(详解一下)
深入解析 EventLoop 和浏览器渲染、帧动画、空闲回调的关系 - 知乎 (zhihu.com)
每一轮的eventloop都会伴随渲染么?
首先浏览器会在合并两次临近的定时器任务
事件循环最后的渲染处理都会执行么–>不是(需要根据屏幕刷新率、页面性能、页面是否在后台运行来共同决定,通常来说这个渲染间隔是固定的)–>浏览器会保持帧率的稳定,调节页面的动画渲染帧
浏览器判断执行渲染后,对渲染的文档执行下列操作
- 对于需要渲染的文档,如果窗口的大小发生了变化,执行监听的
resize
方法。- 对于需要渲染的文档,如果页面发生了滚动,执行
scroll
方法。- 对于需要渲染的文档,执行帧动画回调,也就是
requestAnimationFrame
的回调。- 对于需要渲染的文档, 执行 IntersectionObserver 的回调。
- 对于需要渲染的文档,重新渲染绘制用户界面。
- 判断
task队列
和microTask
队列是否都为空,如果是的话,则进行Idle
空闲周期的算法,判断是否要执行**requestIdleCallback**
的回调函数。
requestAnimationFrame
在哪个阶段执行?
浏览器认定的最后渲染机会前加入回调队列
requestIdleCallback
在哪个阶段执行?
requestIdleCallback
是浏览器提供给我们的空闲调度算法,让我们把一些计算量较大但是又没那么紧急的任务放到空闲时间去执行。不要去影响浏览器中优先级较高的任务,比如动画绘制、用户输入等等。
它会分片执行,并且采用50ms等机制,防止阻塞用户的输入。
resize
、scroll
这些事件是何时去派发的?
resize
和scroll
事件其实自带节流,它只在 Event Loop 的渲染阶段去派发事件到 EventTarget
上。
(4)函数的节流防抖,减少渲染。
(5)GPU:
- GPU加速:使用某些HTML5标签和CSS3属性会触发GPU渲染,请合理使用(过渡使用会引发手机耗电量增加)
- HTML标签:video、canvas、webgl
- CSS属性:opacity、transform、transition
(1)避免在HTML中书写style(这一点是标签属性还是)
(2)避免CSS表达式:CSS表达式的执行需跳出CSS树的渲染(插入了js语句)
(3)移除CSS空规则:CSS空规则增加了css文件的大小,影响CSS树的执行
(4)正确使用display:display会影响页面的渲染
display:inline后不应该再使用float、margin、padding、width和height
display:inline-block后不应该再使用float
display:block后不应该再使用vertical-align
display:table-*后不应该再使用float和margin
(5)不滥用float在渲染时计算量比较大,尽量减少使用
(6)不滥用Web字体:Web字体需要下载、解析、重绘当前页面,尽量减少使用
(7)不声明过多的font-size:过多的font-size影响CSS树的效率
值为0时不需要任何单位:为了浏览器的兼容性和性能,值为0时不要带单位
(8)标准化各种浏览器前缀,无前缀属性应放在最后
(9)CSS动画属性只用-webkit-、无前缀两种
其它前缀为-webkit-、-moz-、-ms-、无前缀四种:Opera改用blink内核,-o-已淘汰
(9)避免让选择符看起来像正则表达式:高级选择符执行耗时长且不易读懂,避免使用
(1)减少不必要的DOM操作,class整体改变,减少重绘和回流,不要在大量重绘间进行访问。
(2)缓存DOM的选择与计算值。防止多次计算。
(3)缓存.length等计算值,get()函数的调用处理。
(4)避免绑定多个事件,多使用代理绑定的策略。
(5)使用id选择器(唯一性)
雅虎军规
2-5-8原则
- 用户在2秒内得到响应,会感觉页面的响应速度很快 Fast
- 用户在2~5秒间得到响应,会感觉页面的响应速度还行 Medium
- 用户在5~8秒间得到响应,会感觉页面的响应速度很慢,但还可以接受 Slow
- 用户在8秒后仍然无法得到响应,会感觉页面的响应速度垃圾死了
lighthouse性能检测
通过静态化、图片懒加载、图片压缩、异步加载(js和css)、优化代码等方式进行优化。
异步js:可以设置defer,async属性让其异步加载,而不会阻塞渲染。defer和async的区别在于async加载完就立即执行,没有考虑依赖,标签顺序等。而defer加载完后会等它前面引入的文件执行完再执行。一般defer用的比较多,async只能用在那些跟别的文件没有联系的孤儿脚本上。
异步css:通过异步css实现标签加入文档中,通过media媒体查询,在不同的环境中加载不同的
preconnect:预先建立访问地址链接:
dns-prefetch
域名预解析<``link` `rel``=``"dns-prefetch"` `href``=``"//example.com"``>
preconnet
预连接<``link` `rel``=``"preconnect"` `href``=``"//example.com"``>``<``link` `rel``=``"preconnect"` `href``=``"//cdn.example.com"` `crossorigin>
prefetch
预加载``
prerender
预渲染<``link` `rel``=``"prerender"` `href``=``"//example.com/next-page.html"``>
减少css选择器的嵌套。用sass,less这种css预处理器很容易造成多层嵌套。优化前代码里最多的有七八层嵌套,对性能有一定影响。重构后不超过三层
改变标签的内部调用html()方法,一次性加入多个元素
文档碎片:(fragment)。通过document.createDocumentFragment()可以新建一个fragment。向fragment中appendChild元素的时候是不会阻塞渲染进程的。最后将fragment替换掉页面上的元素。
js执行栈–>栈帧的减少
尾调用由于是函数的最后一步操作,所有不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了。
如果所有函数都是尾调用,那么完全可以做到每次执行时,调用帧只有一项,这将大大节省内存。这就是“尾调用优化”。注意,只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧,否则就无法进行“尾调用优化”。
减少栈(栈帧数量减少)负载