浅谈前端性能优化

Iona ·
更新时间:2024-11-13
· 828 次阅读

文章目录前言一、资源的合并和压缩1.`html`压缩2.`css`代码压缩3.`js`压缩与混乱(丑化)4.文件合并二、图片相关的优化1.图片压缩2.`css`雪碧图3.网页内联图片(`Image inline`)4.矢量图`SVG`与`iconfont`5.`webp`三、浏览器渲染引擎与阻塞1.渲染的主要模块2.渲染过程3.`css`阻塞4.`js`阻塞5.总结四、懒加载和预加载1.懒加载2.预加载五、重绘与回流1.`CSS`图层2.创建图层的条件3.重绘(Repaint)4.回流(Reflow)5.触发回流的常见操作6.示例7.实战优化点8.请求动画帧(`requestAnimationFrame`)六、函数防抖与节流1.函数防抖2.函数节流七、浏览器存储1.`Cookie`2.`Web Storage``LocalStorage``SessionStorage`3.`IndexedDB`4.`PWA`5.`Service Worker`八、HTTP通用缓存策略1.缓存的简介2.缓存相关的header字段`Cache-Control`字段`max-age``s-maxage``private``public``no-cache``no-store``Expires`字段标识资源变化的字段`Last-Modified/If-Modified-Since``Etag/If-None-Match`3.缓存策略缓存分类强缓存与协商缓存的区别分级缓存策略用户行为对缓存的影响缓存策略过程分析九、服务端性能优化1.`CDN`服务器定义基本思路基础架构应用场景总结2.`SSR(Server Side Rendering)` 前言

当我们去面试的时候,很大概率会被面试官问这么一个问题:你有尝试过对项目做性能优化吗?或者你了解哪些性能优化的方法?听到这个问题的你可能是这样的:
在这里插入图片描述
似曾相识但又说不清楚,往往只能零散地说出那么几点,难以做到有条理的回答。那么,本文就带你简单了解前端性能优化的几个主要方面,旨在抛砖引玉。

一、资源的合并和压缩

web前端应用的开发与部署过程:

image-20200330221214962

输入url到页面显示出来的过程:

image-20200330221250064

请求过程中一些潜在的性能优化点:

dns是否可以通过缓存减少dns查询时间? 网络请求的过程如何走最近的网络环境? 相同的静态资源是否可以缓存? 能否减少http请求的大小和次数? 能否进行服务端渲染?

**总结:**深入理解http请求的过程是前端性能优化的核心。

优化核心

减少http请求数量; 减少请求资源的大小;

google首页案例学习

html压缩; css压缩; js的压缩和混乱; 文件合并; 开启gzip1.html压缩

HTML代码压缩就是压缩一些在文本文件中有意义,但是在HTML不显示的字符,包括空格制表符换行符等,还有一些其他意义的字符,如**HTML注释**也可以被压缩;

一个简单的计算:

google的流量,占到整个互联网的40%,预计2016年全球网络流量将达到1.3ZB(1ZB = 10^9TB),那么google2016年的流量就是1.3ZB * 40%,如果google1MB请求减少一个字节,**每年可以节省流量近500TB**流量。

如何进行html压缩

使用在线网站进行压缩; nodejs提供的html-minifier工具; 后端模板引擎渲染压缩; 2.css代码压缩

分为两部分:

无效代码的压缩; css语义合并;

如何进行css压缩

使用在线网站进行压缩; 使用html-minifierhtml中的css进行压缩; 使用clean-csscss进行压缩; 3.js压缩与混乱(丑化)

包括:

无效字符的删除(空格,回车等); 剔除注释; 代码语义的缩减和优化; 代码保护(如果代码不经处理,客户端可直接窥探代码漏洞);

JS压缩与混乱(丑化)

使用在线网站进行压缩:https://tool.oschina.net/jscompress/

image-20200330230443570

使用html-minifierhtml中的js进行压缩; 使用uglify.js2js进行压缩; 4.文件合并

文件合并的好处:

image-20200330224139300

左边的表示使用http长链接keep-alive但不合并请求的情况,需要分三次去获取a.jsb.jsc.js;右边是使用长链接并且合并请求的情况,只需要发送一次获取合并文件a-b-c.js的请求,就能将三个文件都请求回来。

不合并请求有下列缺点:

文件与文件之间有插入的上行请求,会增加N-1个网络延迟; 受丢包问题的影响更严重:因为每次请求都可能出现丢包的情况,减少请求能有效减少丢包情况; keep-alive本身也存在问题:经过代理服务器时可能会被断开;

文件合并存在的问题

首屏渲染问题:当请求js文件的时候,如果页面渲染只依赖a.js文件,由于文件合并,需要等待合并后的a-b-c.js文件请求回来才能继续渲染,这样就会导致页面渲染速度变慢。这种情况大多出现在现代化的前端框架,如Vue等的使用过程中; 缓存失效问题:合并后的文件a-b-c.js中只要其中一个文件(比如a.js)发生变化,那么整个合并文件都将失效,而不采用文件合并就不会出现这种情况;

使用建议

公共库合并:将不经常发生变化的公共组件库文件进行合并; 将不同页面的js文件单独合并:比如在单页面应用SPA中,当路由跳转到具体的页面时才请求该页面需要的js文件;

如何进行文件合并

使用在线网站进行文件合并; 使用nodejs实现文件合并; 使用webpack等前端构件化工具也可以很好地实现; 二、图片相关的优化

有损压缩过程:

image-20200330232432166

一张JPG图片的解析分别要进行:

颜色空间的转换:从RGB的颜色空间转到其他的颜色空间 ; 进行重采样:区分高频和低频的颜色变换; 进行DCT过程:对高频的颜色采样结果进行压缩,这样压缩的收益会比较大; 再对数据进行量化; 最后进行编码(encoding);

最终得到JPEG-Compressed Image Data,即真正显示出来的JPG图片。虽然这是一种有损压缩,但是很多情况下,这些损失的数据并不影响显示;

png8/png24/png32之间的区别

png8256+ 支持透明; png242^24+ 不支持透明; png322^32+ 支持透明;

不同格式图片常用的业务场景

jpg有损压缩,压缩率高,支持透明;应用:大部分不需要透明图片的业务场景; png支持透明,浏览器兼容好;应用:大部分需要透明图片的业务场景; webp2010年由谷歌推出)压缩程度更好,在ios webview中有兼容性问题;应用:安卓全部; svg矢量图,代码内嵌,相对较小,用于图片样式相对简单的场景;应用:比如logoiconfont1.图片压缩

针对真实图片情况,舍弃一些相对无关紧要的色彩信息,对图片进行压缩;比如在线压缩网站:https://tinypng.com/

image-20200331102612277

2.css雪碧图

将网站上用到的一些图片整合到一张单独的图片中,从而减少网站HTTP请求数量。原理为:设定整张雪碧图可示区域,将想要显示的图标定位到该处(左上角);**缺点:**整合图片比较大时,一次加载比较慢。

如天猫的雪碧图:

image-20200331100812635

很多情况下,并不是所有的小图标都放在一张雪碧图中,而是会适当进行拆分。现在使用雪碧图的场景比较少了。

自动生成雪碧图样式的网站:http://www.spritecow.com/

image-20200331111632363

选中雪碧图中对应的图标,就会生成对应的样式。

3.网页内联图片(Image inline

将图片的内容内嵌到html当中,减少网站的HTTP请求数量,常用于处理小图标和背景图片。网页内联图片写法为:

浏览器上的表现形式为:

image-20200331105927649

这里提供一个将:imageDataUrI的网址:http://tool.c7sky.com/datauri/

image-20200331105719573

缺点:

浏览器不会缓存内联图片资源; 兼容性较差,只支持ie8以上浏览器; 超过1000kb的图片,base64编码会使图片大小增大,导致网页整体下载速度减慢;

所以要根据场景使用,不过内联图片减少HTTP请求的优点还是很显著的。比如,在开发中小于4KB8KB的图片都会通过构建工具自动inlineHTML中,这种情况下Image inline带来的图片大小增长其实是比增加HTTP请求次数更优的。

4.矢量图SVGiconfont

使用iconfont解决icon问题

应尽量使用该方式,比如可以采用阿里巴巴矢量图库:

image-20200331102136261

可以选择格式进行下载:

image-20200331102259285

可以看到它们的大小有着明显的差异:

image-20200331102353635

使用SVG进行矢量图的控制

SVG意为可缩放矢量图形(Scalable Vector Graphics)。SVG 使用 XML 格式定义图像。下为示例:

image-20200331112542540

在线转换网站:http://www.bejson.com/convert/image_to_svg/

image-20200331112802316

5.webp

webp的优势体现在它具有更优的图像压缩算法,能带来更小的图片体积,而且拥有肉眼识别无差异的图像质量;同时具备了无损和有损的压缩模式、Alpha透明以及动画的特性。在JPEGPNG上的转化效果都非常优秀、稳定和统一。安卓上不存在兼容性问题,推荐安卓下使用。

以下为淘宝网首页请求的图片:
image-20200331101234068

可以看到,图片中大量地添加了webp格式的选择。.jpg_.webp表示当浏览器支持webp时采用webp格式,否则采用jpg格式。

下面为B站首页的图片,可以看到基本都采用了webp格式:

image-20200401110811561

同一张图片jpg格式和webp格式压缩率有着明显的差异:

image-20200331101633934

可以通过在线网站将图片转换为webp:https://zhitu.isux.us/

image-20200331103230221

像图片这样的静态文件可以存放在CDN服务器上,让CDN服务器批量地将图片转换成Webp格式;

三、浏览器渲染引擎与阻塞 1.渲染的主要模块

版本一:

image-20200331131925229

版本二:

image-20200331113305758

一个渲染引擎主要包括:HTML解析器,CSS解析器,javascript引擎,布局layout模块,绘图模块:

HTML解析器:解释HTML文档的解析器,主要作用是将HTML文本解释成DOM树; CSS解析器:它的作用是为DOM中的各个元素对象计算出样式信息,为布局提供基础设施; Javascript引擎:使用Javascript代码可以修改网页的内容,也能修改css的信息,javascript引擎能够解释javascript代码,并通过DOM接口和CSS树接口来修改网页内容和样式信息,从而改变渲染的结果; 布局(layout):在DOM创建之后,Webkit需要将其中的元素对象同样式信息结合起来,计算他们的大小位置等布局信息,形成一个能表达这所有信息的内部表示模型; 绘图模块(paint):使用图形库将布局计算后的各个网页的节点绘制成图像结果; 2.渲染过程

浏览器渲染页面的整个过程:浏览器会从上到下解析文档。

浏览器解析时遇见 HTML 标记,就会调用HTML解析器解析为对应的 token (一个token就是一个标签文本的序列化)并构建 DOM 树(就是一块内存,保存着tokens,建立它们之间的关系)。在生成DOM的最开始阶段(应该是Bytescharacters后),并行发起css、图片、js的请求,无论他们是否在HEAD标签中。

注意:发起js文件的下载请求(request)并不需要DOM处理到那个script节点;

遇见 style/link 标记 调用解析器 处理 CSS 标记并构建 CSS样式树;

遇见 script 标记 调用 javascript解析器处理script标记,绑定事件、修改DOM树/CSS树等;

DOM树 与 CSS树 合并成一棵渲染树(Render Tree)。

布局(Layout):根据渲染树中各节点的样式和依赖关系,计算出每个节点在屏幕中的位置;

绘图(Painting):按照计算出来的结果:要显示的节点、节点的CSS与位置信息,通过显卡,把内容画到屏幕上;

经过第一次Painting之后DOMCSSOMRender Tree都可能会被多次更新,比如JS修改了DOM或者CSS属性时,LayoutPainting就会被重复执行。除了DOMCSSOM更新的原因外,图片下载完成后也需要调用LayoutPainting来更新网页。

补充:

HTML中可能会引入很多的css、js这样的外部资源,这些外部资源在浏览器端是并发加载的。但是浏览器会对同一域名进行并发数量(度)的限制,即单个域名的并发度是有限的; 所以,经常将大部分的资源托管到CDN服务器上,并且设置3~4CDN域名。防止只有一个CDN域名的情况下,达到了浏览器外部资源并发请求数目的上限,导致很多资源无法做到并发请求。所以,应设置多个CDN域名; 3.css阻塞

只有通过link引入的外部css才会产生阻塞:

style标签中的样式:

html解析器进行解析; 不阻塞浏览器渲染(可能会产生“闪屏现象”); 不阻塞DOM解析;

link引入的外部css样式(推荐使用的方式):

CSS解析器进行解析; 阻塞浏览器渲染:由于css已经加载完毕,所以整个渲染过程是带样式的,所以这种阻塞可以避免“闪屏现象”; 阻塞其后面的js语句的执行:这个不难理解,js文件中经常会出现DOM操作,操作过程中有可能涉及到css样式的修改。实际上,这些修改往往是依赖于之前引入的css设定的样式的,所以css会阻塞js的执行; 不阻塞DOM的解析;

优化核心理念:尽可能快的提高外部css加载速度:

使用CDN节点进行外部资源加速; 对css进行压缩(利用打包工具,比如webpackgulp等); 减少http请求数,将多个css文件合并; 优化样式表的代码; 4.js阻塞

阻塞DOM解析:

原因:浏览器不知道后续脚本的内容,如果先去解析了下面的DOM,而随后的js删除了后面所有的DOM,那么浏览器就做了无用功,浏览器无法预估脚本里面具体做了什么操作,例如像document.write这种操作,索性全部停住,等脚本执行完了,浏览器再继续向下解析DOM;可以通过给script标签添加deferasync属性,异步引入js文件,以此来解决这一问题。

阻塞页面渲染:

原因:js中也可以给DOM设置样式,浏览器同样等该脚本执行完毕,再继续干活,避免做无用功;

阻塞后续js的执行:

原因:js是按顺序执行的,这样可以维护依赖关系,例如:必须先引入jQuery再引入bootstrap

不阻塞资源的加载:

这并不与上面矛盾,因为不可能由于加载一个js文件就把其他资源的加载都阻塞了。针对这种常见的情况,浏览器会通过预加载的方式加载后续的资源;

5.总结

css的解析和js的执行是互斥的(互相排斥),css解析的时候js停止执行,js执行的时候css停止解析;

无论css阻塞,还是js阻塞,都不会阻塞浏览器加载外部资源(图片、视频、样式、脚本等);

因为览器始终处于一种:“先把请求发出去”的工作模式,只要是涉及到网络请求的内容,无论是:图片、样式、脚本,都会先发送请求去获取资源,至于资源到本地之后什么时候用,由浏览器自己协调。显然这种做法效率很高;

WebKitFirefox 都进行了【预解析】这项优化。在执行js脚本时,浏览器的其他线程会解析文档的其余部分,找出并加载需要通过网络加载的其他资源。通过这种方式,资源可以在并行连接上加载,从而提高总体速度。请注意,预解析器不会修改 DOM

四、懒加载和预加载 1.懒加载

图片进入可视区域之后再请求图片资源的方式称为图片懒加载。适用于图片很多,页面很长的业务场景,比如电商;

懒加载的作用:

减少无效资源的加载:

比如一个网站有十页图片,用户只查看了第一页的图片,这就没必要将十页图片全都加载出来;

并发加载的资源过多会阻塞js的加载,影响网站正常的使用:

由于浏览器对某一个host name是有并发度上限的,如果图片资源所在的CDN和静态资源所在的CDN是同一个的话,过多图片的并发加载就会阻塞后续js文件的并发加载。

懒加载实现的原理:

监听onscroll事件,判断可视区域位置:

图片的加载是依赖于src路径的,首先可以为所有懒加载的静态资源添加自定义属性字段,用于存储真实的url。比如是图片的话,可以定义data-src属性存储真实的图片地址,src指向loading的图片或占位符。然后当资源进入视口的时候,才将src属性值替换成data-src中存放的真实url

懒加载实例

可以使用元素的getBoundingRect().top来判断当前位置是否在视口内,也可以使用元素距离文档顶部的距离offsetTopscrollTop是否小于视口高度来判断:

image-20200331153831469

举例

比如手机淘宝首页:

image-20200331145210486

当快要滚动到需要展示的图片时才进行图片的请求,可以看到图片上有一个lazyload的属性:

image-20200331145756313

2.预加载

预加载与懒加载正好是相反的过程:懒加载实际上是延迟加载,将我们所需的静态资源加载时间延后;而预加载是将图片等静态资源在使用之前的提前请求,这样资源在使用到时能从缓存中直接加载,从而提升用户体验;

预加载的作用:

提前请求资源,提升加载速度:使用时只需要读取浏览器缓存中提前请求到的资源即可;

维护页面的依赖关系:比如WebGL页面,会依赖一些3D模型,这些都是页面渲染所必须的资源。如果资源都没有加载完毕就进行页面的渲染,就会造成非常不好的体验。

所以时常使用预加载的方式维护页面渲染的依赖关系,比如将WebGL页面依赖的3D模型加载完之后才进行页面渲染。这样渲染的过程就不会有任何阻碍,具有较好的用户体验;

预加载的实例

例如九宫格抽奖业务,每个奖品都有一个选中态和非选中态,实际上这是由两张图片组合而成的。由于每个奖品的选中过程都是一瞬间,这就对图片的选中态和非选中态切换效率要求很高,如果选中态的图片没有预加载的话显然是来不及的。

image-20200331150725347

所以,实际上对于九宫格中所有图片选中态的样式和对应的图片都需要进行预加载,从而让我们在抽奖的过程中,能够瞬间从缓存中读取到选中态的图片,从而不影响抽奖效果的展示。

除此之外还有网站登录或活动时需要用到的动画,这是在动画需要的每帧图片都完全预加载完之后才会进行显示的。

五、重绘与回流 1.CSS图层

浏览器在渲染一个页面时,会将页面分为很多个图层,图层有大有小,每个图层上有一个或多个节点。在渲染 DOM的时候,浏览器所做的工作实际上是:

​ 1、获取DOM后分割为多个图层;

​ 2、对每个图层的节点计算样式结果(Recalculate style–样式重计算);

​ 3、为每个节点生成图形和位置(Layout–回流和重布局);

​ 4、将每个节点绘制填充到图层位图中(Paint SetupPaint–重绘);

​ 5、图层作为纹理上传至GUI

​ 6、复合多个图层到页面上生成最终屏幕图像(Composive Layers–图层重组);

2.创建图层的条件 拥有3D或透视变换的css属性(prespective transform ); 使用加速视频解码的节点; 拥有3D(WebGL)上下文或加速的2D上下文的节点; CSS3动画的插件(如Flash); 拥有加速css过滤器的元素; transform:如translateZ(0) opacity filter will-change:哪一个属性即将发生变化,进而进行优化。 3.重绘(Repaint)

重绘是一个元素外观的改变所触发的浏览器行为,比如background-coloroutline等属性。这些属性不影响布局,只影响元素的外观风格,会造成DOM元素的重新渲染,这个过程称为重绘。

需要注意的是:重绘是以图层为单位,如果图层中某个元素需要重绘,那么整个图层都需要重绘。比如一个图层包含很多节点,其中有个gif图,gif图的每一帧,都会重回整个图层的其他节点,然后生成最终的图层位图。

因此,可以通过特殊的方式来强制gif图单独为一个图层(translateZ(0)或者translate3d(0,0,0)CSS3的动画也是一样(好在绝大部分情况浏览器自己会为CSS3动画的节点创建图层);

所以:频繁重绘回流DOM元素作为一个独立图层,那么这个DOM元素的重绘和回流只会该图层;原则上是要尽量避免新建图层的,因为这会导致图层重组(Composive Layers)时候的计算量增大。所以,只有当某些DOM元素频繁重绘回流时,才新建一个独立图层放置它们;

只会触发重绘的属性

//部分属性 color border-style border-radius visibility text-decoration background background-image background-position background-repeat background-size outline-color outline outline-style outline-width box-shadow 4.回流(Reflow)

render tree中的一部分(或全部)因为元素的规模尺寸布局隐藏等改变而需要重新构建。这就称为回流(reflow);

当页面布局和几何属性改变时就需要回流;

回流必将引起重绘,而重绘不一定会引起回流;

触发页面重布局(回流)的属性

盒子模型相关属性 定位及浮动属性 文字结构属性
width top text-align
height bottom overflow-y
padding left font-weight
margin right overflow
display position font-family
border-width float line-height
border clear vertical-align
min-height * white-space
* * font-size

频繁触发重绘回流,会导致UI频繁渲染。在渲染的过程中由于阻塞了js线程的执行,最终导致js执行变慢。

5.触发回流的常见操作

增加、删除、修改 DOM 结点;

移动 DOM 的位置;

修改 CSS 样式;

Resize 窗口;移动端没有这个问题,因为移动端的缩放没有影响布局视口(vw/vh);

修改网页的默认字体;

获取某些DOM元素的属性(widthheight等);

display:none 会触发 Reflow,而visibility:hidden 只会触发 Repaint,因为没有发生位置变化;

6.示例

案例一:淘宝轮播图

可以使用Chrome浏览器调试工具的Performance来观察淘宝首页一个轮播图引起的重绘回流过程:

image-20200331161654830

Update Layer Tree回流和重布局:

image-20200331163602611

Paint重绘:

image-20200331163618288

Composite Layers图层重组:

image-20200331163626477

案例二:播放器

通过Chrome调试工具的Layers选项查看图层,及新增图层的原因:

image-20200331162710936

视频播放的过程中,video标签的DOM元素会一直重绘,所以把它限制在一个图层上是非常好的,这样只会涉及到这个图层的重绘,而不会影响其他图层的元素。

图层不能滥用,否则会在图层重组的过程中严重消耗性能!

比如可以将淘宝首页的所有的DOM元素都变为一个图层:在html标签中的全局样式(*)中添加transform:translateZ(0)来触发新建图层:

还可以通过添加:will-change: transform属性新建图层;

image-20200331165429542

再次查看此时的图层情况,可以看到此时首页的图层非常之多,十分地卡:

image-20200331165818801

7.实战优化点

如果我们需要使得动画或其他节点渲染的性能提高,需要做的就是减少浏览器在运行时所需要做的下列工作:

计算需要被加载到节点上的样式结果(Recalculate style–样式重计算); 为每个节点生成图形和位置(Layout–回流和重布局); 将每个节点填充到图层中(Paint SetupPaint–重绘); 组合图层到页面上(Composite Layers–图层重组);

1、使用translate替代top等属性来改变位置;

Document #box{ /*方法1*/ position: relative; top: 0; /*方法2*/ /* transform: translateY(0); */ width: 200px; height: 200px; background-color: pink; }
setTimeout(() => { document.getElementById("box").style.top = '100px' //document.getElementById("box").style.transform = 'translateY(100px)' }, 2000);

使用top属性改变正方形位置时,存在重绘和回流Layout

image-20200331173140790

而使用translate属性改变正方形位置时,并不会引起重绘和回流:
image-20200331173755614

比如有的网站会有一些左右飘动的浮窗,由于这些浮窗是采用定时器来实现的,如果每经过100ms就改变浮窗的位置。这种时候使用transform来替代top/left的话1s内就减少了十次回流,十分有利于网页速度的提升。

2、使用opacity替代visibility

使用visibility不触发重排,但是依然重绘;

直接使用opacity既触发重绘,又触发重排(GPU底层设计如此!);

opacity配合图层使用,既不触发重绘也不触发重排;

原因:透明度的改变时,GPU在绘画时只是简单的降低之前已经画好的纹理的alpha值来达到效果,并不需要整体的重绘。不过这个前提是这个被修改opacity本身必须是一个单独的图层。

3、将多次改变DOM元素样式属性的操作合并成一次操作:

预先定义好class,然后通过修改DOMclassName来添加样式;

4、把DOM离线后再修改:

由于display属性为none的元素不在渲染树中,对隐藏的元素操作不会引发其他元素的重排。如果要对一个元素进行复杂的操作时,可以先隐藏它,操作完成后再显示。这样只在隐藏和显示时触发2次回流;

5、不要把获取某些DOM节点的属性值放在一个循环里当成循环的变量

当向浏览器请求某些 style信息的时候,浏览器就会清空(flush)队列,比如:

ffsetTopoffsetLeftoffsetWidthoffsetHeight

scrollTop/Left/Width/Height

clientTop/Left/Width/Height

widthheight

浏览器为了获取最精确的值,需要刷新内部队列。因为队列中可能存在影响到这些值的操作,即使没有,浏览器也会强行刷新渲染队列。这样就无法利用渲染队列的缓存来避免回流过于频繁了,所以在使用到DOM元素这些相关的属性时,可以将获取到的属性值存在一个变量中,而不是每次都去重新获取。

6、不要使用table布局:

因为很小的一个改动都会造成整个table的重新布局;所以尽量使用div布局;

7、启用GPU硬件加速:

原理为:浏览器会检测一些特定的css属性,当DOM元素拥有这些css属性的时候,浏览器就会对该DOM元素启动GPU硬件加速;比如:transform: translateZ(0)transform: translate3d(0, 0, 0)这两个属性都可以启动硬件加速;硬件加速同样不能滥用,否则会导致图层过多,导致合并图层时消耗大量性能。

8、动画实现速度的选择:

因为动画的每次变化都会引起重绘和回流,所以要根据业务场景适当地在动画帧数(顺畅程度)和回流次数中进行平衡;

9、为动画元素新建图层,提高动画元素的z-index

10、利用文档碎片(documentFragment)------vue使用了该种方式提升性能

如果我们要在一个ul中添加10000li,如果不使用文档碎片,那么我们就需要使用append进行10000次的追加,这会导致页面不停地回流,非常地消耗资源:

var oUl = document.createElement("ul"); for(var i=0;i<10000;i++) { var oLi = document.createElement("li"); oUl.appendChild(oLi); } document.body.appendChild(oUl);

我们可以引入createDocumentFragment()方法,它的作用是创建一个文档碎片。先将要插入10000li添加到文档碎片里,然后再一次性添加到document中。即文档碎片相当于一个临时仓库,这样能够大量减少DOM操作:

//先创建文档碎片 var oFragment = document.createDocumentFragment(); //再创建ul标签 var oUl = document.createElement("ul"); for(var i=0;i<10000;i++) { //创建li标签 var oLi = document.createElement("li"); //先附加在文档碎片中 oFragment.appendChild(oLi); } //将文档碎片添加到ul标签中 oUl.appendChild(oFragment); //将ul标签添加到body标签中 document.body.appendChild(oUl);

**10、使用requestAnimationFrame制作动画:**详细内容如下。

8.请求动画帧(requestAnimationFrame

**window.requestAnimationFrame() :**该方法会告诉浏览器在重绘之前调用指定的函数:

**参数:**该方法以一个回调函数作为参数,这个回调函数会在浏览器重绘之前被调用;

回调函数会被自动传入一个参数:DOMHighResTimeStamp,标识requestAnimationFrame()开始触发回调函数的当前时间;

返回值: 一个非零的整数,也称为请求ID,是回调列表中唯一的标识,没有其他意义;

**window.cancelAnimationFrame(requestID):**该方法取消一个先前通过调用window.requestAnimationFrame()方法添加到计划中的动画帧请求。requestID是先前调用window.requestAnimationFrame()方法时返回的ID

用途

当无法使用CSS3制作动画的情况下,使用这种方法替代定时器制作动画; 由于重绘就调用的机制,制作的动画频率与浏览器的刷新频率一致,不会出现闪动,保证了动画的流畅;

示例

Document #box{ height: 200px; width: 200px; background-color: pink; }
let i = 0 //获取请求ID let id = requestAnimationFrame(move) function move(){ i++ document.getElementById('box').style.transform = `translateX(${i}px)` //递归调用requestAnimationFrame,更新请求ID,实现动画效果 id = requestAnimationFrame(move) } setTimeout(() => { //2s后停止动画 cancelAnimationFrame(id) }, 2000);
六、函数防抖与节流 1.函数防抖 **概念:**不断触发一个函数,在规定时间内只让最后一次生效,前面都不生效; **实现:**定时器; **应用:**搜索时等用户完整输入内容后再发送查询请求;

代码实现

function debounce(fn,delay){ var timer = null // 清除上一次延时器 return function(){ clearTimeout(timer) // 重新设置一个新的延时器 timer = setTimeout(() => { fn.call(this) }, delay); } }

使用函数防抖可以减少事件触发的次数和频率,在某些情况下可以起到优化的作用。比如:搜索框,对于核心业务非搜索的网站,一般都是等待用户完整输入内容后才发送查询请求,一次来减少服务器的压力。像百度这样的核心业务为搜索的网站,服务器性能足够强大,所以不进行函数防抖处理;

2.函数节流

**概念:**不断触发一个函数后,执行第一次,只有大于设定的执行周期后才会执行第二次,以此控制函数执行频率;

**实现:**定时器,标识;

**应用:**在游戏中,可以设定人物攻击动作的最快频率,无论手速多快也无法超越这一频率;

代码实现

/* 节流函数:fn:要被节流的函数,delay:规定的时间 */ function throttle(fn, delay){ // 记录上一次函数出发的时间 var lastTime = 0 return function(){ // 记录当前函数触发的时间 var nowTime = new Date().getTime() // 当当前时间减去上一次执行时间大于这个指定间隔时间才让他触发这个函数 if(nowTime - lastTime > delay){ // 绑定this指向 fn.call(this) //同步时间 lastTime = nowTime } } } 七、浏览器存储 1.Cookie Cookie翻译过来是小甜饼的意思,是网景公司的前雇员 Lou Montulli19933月发明的; Cookie是纯文本格式,不包含任何可执行的代码信息,伴随着用户请求在 Web 服务器和浏览器之间传递; Cookie本质上属于http的范畴,因为http协议本身是无状态的,服务端是没有办法区分请求来自于哪个客户端,即便是来自于同一个客户端的多次请求,服务端也无法进行区分。所以引入了Cookie去维持客户端的状态(比如每个账号的购物车状态都不一样)。

Cookie的生成方式

客户端生成:

JavaScript 中通过 document.cookie 属性,你可以创建、维护和删除 Cookie;设置 document.cookie 属性的值并不会删除存储在页面中的所有 Cookie,它只简单的创建或修改字符串中指定的 Cookie

服务端生成:

Web 服务器通过在HTTP响应头中添加 Set-Cookie字段来创建一个 Cookie,可以在该字段中添加HttpOnly属性禁止JavaScript脚本访问Cookie,以此来避免跨域脚本 (XSS) 攻击。

Cookie的缺陷

**安全性:**由于CookieHTTP中是明文传递的,其中包含的数据都可以被他人访问,出现篡改、盗用等问题; 大小限制:Cookie的大小限制在4KB左右,若要做大量存储显然不是理想的选择; **增加流量:**因为Cookie是绑定域名对应的服务器的,所以对同一个域名的每次请求都会在Request Header中带上Cookie。 一方面:增加对服务器的请求时间; 另一方面:导致大部分不需要用到Cookie信息的场合下流量的浪费;这样浏览器对同一域名的每一次请求都会多出4KB流量,对于大型网站来说这是很大的损耗。

因此要慎用Cookie,不要在Cookie中存储重要和敏感的数据。

Cookie性能优化的方法

将存放静态资源的CDN服务器域名与主站的域名独立开来。这样每次请求静态文件的时候就不需要携带Cookie,从而可以节省很多流量。

举例

比如在百度进行登录的时候,请求头里面就会有Set-Cookie字段,其中的BDUSS就是标识用户登录状态的字符串:

image-20200331213122597

Set-Cookie中的httponly属性表示的是禁止js脚本访问cookie,这样能够一定程度防范XSS攻击;

Chrome调试工具的Application选项中查看Cookies信息,可以发现该Cookie已经被网站“种”到Domain:.baidu.com这个域名下了,并且该Cookie也设置了HttpOnly属性:

image-20200331213850374

此后浏览器的每次请求都会在请求头Request Headers中携带这一Cookie信息。刷新页面后可以看到,请求头中携带了Cookie信息BDUSS

image-20200331214126035

这样服务器就知道这是已经登录的用户了。

但是不是所有的请求都需要携带Cookie信息,比如优酷:

image-20200331214741967

可以看到请求index.css文件时也携带了Cookie,但是这是不必要的,这就会导致流量的浪费。

解决方法就是上面所说的:将**CDN域名主域名**独立出来;

百度就是这样解决的:

image-20200331215055075

可以看到请求这个静态资源的url并不是.baidu.com,而是静态资源服务器CDN;并且该请求的请求头中不会携带Cookie信息:

image-20200331215256387

设置和获取Cookie

设置Cookie的方式很简单,keyvalue值通过等号连接:

document.cookie = "userName=zhangsan"

image-20200331232648641

打开Application选项查看当前Cookie,可以看到Cookie已被改变:

image-20200331232745709

获取Cookie

document.cookie

image-20200331233517456

备注:

静态资源是不会携带Cookie的; Cookie一般都是后台种的,很少让前端来直接写; Cookie分:持久级别、session级别; Cookie一般用于存放session ID与服务器端进行通信; 2.Web Storage

Web Storage分为SessionStorageLocalStorage专门用于客户端浏览器的本地存储,同时空间比Cookie大很多,一般支持5-10M

浏览器端通过 Window.sessionStorageWindow.localStorage 属性来实现本地存储机制;

LocalStorage

LocalStorageHTML5设计出来专门用于存储浏览器信息的:

大小为5~10M左右; 仅在客户端中使用,不和服务端进行通信; 接口封装较好,提供了js进行读写等操作的API; 采用浏览器本地缓存方案,可直接使用浏览器本地缓存,提升网页渲染的速度;

举例

比如通过Chrome调试工具的Application选项可以查看淘宝中LocalStorage存储的数据:

image-20200331220258429

这些数据只要不手动清除,即使关闭页面也都会存在。当需要使用图片、js/css文件等资源时就不用重新向服务器发出请求,而是可以直接使用LocalStorage中的缓存,这就是LocalStorage缓存的优势;

Cookie就不一样了,里面存储的数据都是要带到服务器端的,例如用户登录状态,统计信息等数据:
image-20200331220548331

设置和获取LocalStorage

LocalStorage提供了相对简单的API,采用的也是keyvalue的形式。

设置时通过:

localStorage.setItem("key", "value")

image-20200331233022902

查看LocalStorage,同样设置成功了:

image-20200331233059670

获取时通过:

localStorage.getItem("key")

image-20200331233226621

其他方法

//该方法接受一个键名作为参数,并把该键名从存储中删除。 localStorage.removeItem('key'); //调用该方法会清空存储中的所有键名 localStorage.clear(); SessionStorage

SessionStorage用于存储浏览器的会话信息,标签页关闭之后它存储的数据就会被清空,而LocalStorage的数据不会被清空,这是二者的区别:

大小为5~10M左右; 仅在客户端使用,不和服务端进行通信; 接口封装较好; 可对表单信息进行维护;比如添加表单过程中进行了刷新,可以将刷新前填写的信息写入SessionStorage中,这样即使刷新后数据也不会丢失;还有一种场景:分页的表单在进行前进或后退时,如果将信息保存在SessionStorage中就不会丢失;

设置和获取SessionStorage

设置SessionStorage的方法与设置LocalStorage的方法类似:

//设置 sessionStorage.setItem("key", "value") //获取 sessionStorage.getItem("key")

image-20200331233922185

通过Application选项查看SessionStorage,可见已成功修改:

image-20200331234022971

其他方法

//该方法接受一个键名作为参数,并把该键名从存储中删除。 sessionStorage.removeItem('key'); //调用该方法会清空存储中的所有键名 sessionStorage.clear(); 3.IndexedDB

IndexedDB是浏览器提供的一种API,用于存储客户端中大量的结构化数据。该API使用索引来实现对数据的高性能搜索。虽然WebStorage对于存储较少量的数据时很有用(采用key/value的方式),但对于存储更大量的结构化数据来说,还是IndexedDB表现更加优异。

IndexedDB的应用

为应用创建离线版本;

可以在浏览器中打印indexedDB对象:

image-20200331234812475

4.PWA

PWAProgressive Web Apps)是一种Web App新模型(标准),并不是具体指某一种前沿的技术或者某一个单一的知识点。从英文缩写就能看出,这是一个渐进式的Web App,是通过一系列新的Web特性,配合优秀的UI交互设计,逐步增强用户的体验;

PWA的要求

**可靠:**在没有网络的环境中也能提供基本的页面访问,而不会出现"未连接到互联网"的情况; **快速:**针对网页渲染及网络数据访问有较好优化; **融入(Engaging):**应用可以被增加到手机桌面,并且和普通应用一样有全屏、推送等特性; 5.Service Worker

Service Worker是一个脚本,可以使浏览器独立于当前网页,在后台运行。为实现一些不依赖页面或者用户交互的特性打开了一扇大门。在未来这些特性将包括推送信息,背景后台同步,geofencing(地理围栏定位)等它将推出的第一个首要特性,就是拦截和处理网络请求的能力,包括以编程方式来管理被缓存的响应。

Service Worker可以帮助浏览器执行大规模的运算而不阻碍主线程的执行。

Service Worker的应用

使用拦截和处理网络请求的能力,实现一个离线应用; 使用Service Worker在后台运行的同时能和页面通信的能力,去实现大规模后台数据的处理;

Service Worker应用过程

image-20200331235052759

示例

通过Chrome调试工具的Application选项可以查看淘宝的Service Workers信息:

image-20200331230014013

当我们刷新淘宝网页的时候,查看Network选项,可以从请求文件的size栏发现大量的文件都是从Service Worker缓存中请求回来的:

image-20200331230221993

这样的话就可以利用Service Worker的缓存进行网站的性能优化。

以下列淘宝请求同一js文件为例,从Service Worker中加载使用了7ms

image-20200331230805709

使用Ctrl + F5强制刷新后,向服务器请求同一文件花了100ms

image-20200331230936902

这就是使用Service Worker性能上带来的优势。由于是从本地缓存中读取的资源,所以资源读取的速度和整体的性能都会有一个明显的提升。

八、HTTP通用缓存策略 1.缓存的简介

缓存定义:

浏览器在本地磁盘上将用户之前请求的数据存储起来,当访问者再次需要改数据的时候无需再次发送请求,直接从浏览器本地获取数据

缓存的好处:

减少请求的个数; 节省带宽,避免浪费不必要的网络资源; 减轻服务器压力; 提高浏览器网页的加载速度,提高用户体验; 2.缓存相关的header字段

可以通过Chrome浏览器调试工具中的Network选项查看浏览器请求资源的情况:


性能 前端性能优化 性能优化 优化 前端性能 前端

需要 登录 后方可回复, 如果你还没有账号请 注册新账号