前台面试出现频率最高问题——前台性能优化

  • 时间:2020-11-08 01:40 作者:东西里 来源: 阅读:433
  • 扫一扫,手机访问
摘要:这是我第16篇 。整体思路:性能优化是什么?从客户角度来看,什么才是好的页面?一个是页面加载的很快,另一个是页面使用起来很流畅。那么对应到前台开发的角度,性能优化可以对应分为两个方向:页面加载时间跟页面运行效率。从浏览器打开到页面渲染完成,花费了多少时间?是的,这个问题有点熟习,面试官比较常问的是从

这是我第16篇简书。

整体思路:

性能优化是什么?
从客户角度来看,什么才是好的页面?一个是页面加载的很快,另一个是页面使用起来很流畅。
那么对应到前台开发的角度,性能优化可以对应分为两个方向:页面加载时间页面运行效率
从浏览器打开到页面渲染完成,花费了多少时间?
是的,这个问题有点熟习,面试官比较常问的是从浏览器打开到页面渲染完成,发生了什么事情。这个问题网上很多答复,我也不就重复的细说了。主要的过程是:
浏览器解析->查询缓存->dns查询->建立链接->服务器解决请求->服务器发送响应->用户端收到页面->解析HTML->构建渲染树->开始显示内容(白屏时间)->首屏内容加载完成(首屏时间)->客户可交互(DOMContentLoaded)->加载完成(load)
很显然,假如我们要进行加载时间的优化,我们需要从这里的每一个步骤都去思考,去总结,而避免东凑一点,西凑一点。

具体细节:

一、web性能优化

1、减少HTTP请求(这个优化是最显著的)

(1)图片地图,允许在一个图片上关联多个URL,目标URL取决于客户单击的图片上的位置。
(2)雪碧图(也叫CSS精灵, 是一种CSS图像合成技术,一般用于图标,将多张图片合并到一张图片,配合background-position来使用)
(3)合并JS和CSS文件
(4)减少http请求头
(5)配置多个域名和CDN加速
把网站内容分散到多个、处于不同地域位置的服务器上可以加快下载速度。
(6)使用缓存(HTTP缓存、浏览器缓存、应用缓存)
恰当的缓存设置可以大大的减少 HTTP请求,例如,很少变化的图片资源可以直接通过 HTTP Header中的Expires设置一个很长的过期头 ;变化不频繁而又可能会变的资源可以使用 Last-Modifed来做请求验证。尽可能的让资源能够在缓存中待得更久。
(7)优化cookie

2、避免坏请求

有时候页面中的html或者css会向服务器请求一个不存在的资源,比方图片或者者html文件,这会造成浏览器与服务器之间过多的往返请求。

3、避免使用document.write

在js中,可以使用document.write。在网页上显示内容或者者调用外部资源,而通过此方法,浏览器采取少量多余的步骤(下载资源,读取资源)。运行js来理解需要做什么,调用其余资源时,需要重新在执行一次这个过程。因为浏览器之前不知道要显示什么,所以会降低页面加载的速度。

要知道,任何能够被document.write调用的资源,都可以通过html调用。这样速度会更快

document.write('<scriptsrc="another.js"></script>');改为<scriptsrc="another.js"></script>
4、尽量减少dns查询次数

当浏览器和服务器建立链接时,它需要进行dns解析,将域名解析为ip地址,然而,一旦用户端需要执行dns lookup时,等待时间将会取决于域名服务器的有效响应速度。

尽管所有的isp的dns服务器都能缓存域名和ip地址映射表。但假如缓存的dns记录过期了而需要升级,则可能需要遍历多个dns节点,有时候需要通过全球范围内来找到可信任的域名服务器,一旦域名服务器工作繁忙,请求解析时,就需要排队则进一步延时等待时间。

所有减少dns查询次数很重要,页面加载就尽量避免额外耗时,为了减少dns查询次数,最好的处理方法就是在页面中减少不同的域名请求的机会、

可通过request checker工具来检测页面中存在多少请求后,进行优化。

5、尽量减少重定向

有时候为了特定需求,需要在网页中使用重定向。重定向的意思是,客户的原始请求(如请求A)被重定向到其余的请求(如请求B);

网页中使用重定向会造成网站性能和速度下降,由于浏览器访问网址是一连串的过程,假如访问到一半,而跳转到新的地址,就会重复发起一连串的过程,这将白费很多时间。所有我们尽量避免重定向。Google建议:

A、 不要链接到一个包含重定向的页面

B、 不要请求包含重定向的资源

6、优化样式表和脚步顺序

Style标签和样式表调用代码应该放置在js代码的前面,这样可以使页面的加载速度加快。

7、避免js阻塞渲染

浏览器在遇到一个引入外部js文件的<script>标签时,会停下所有工作下载并解析执行它。在这个过程中,页面渲染和客户交互完全被阻塞了。这是页面加载就会中止。

8、启用压缩/Gzip

使用gzip对html和css文件进行压缩,通常可以大约节省50%到70%,这样加载页面只要要更少的带宽和更少的时间。

使用gzip对html和css文件进行压缩,通常可以大约节省50%到70%,这样加载页面只要要更少的带宽和更少的时间。

Google建议删除干扰页面第一屏内容加载 的js,第一屏指的是客户在屏幕中最初看到的页面,无论桌面浏览器,还是手机

二、html

1、减少DOM元素数量

页面中存在大量DOM元素,会导致js遍历DOM的效率变慢。尤其要尽量少用iframe,它是耗能最大的dom元素,而且会阻塞onload事件(除了获取葵花码 扫码登录这些需要用到iframe,其余地方基本不使用了)

2、使用css+div代替table布局,去掉格式化控制标签如:strong,b,i等,使用css控制。

下面这个说给不怎样懂前台的技术总监听[滑稽]:
说前5点就行了

为什么不建议用table进行布局?
1)table比其它html标记占更多的字节。(造成下载时间推迟,占用服务器更多流量资源)
2)table会阻挡浏览器渲染引擎的渲染顺序。(会推迟页面的生成速度,让客户等待更久的时间)
3)table里显示图片时需要你把单个、有逻辑性的图片切成多个图。(添加设计的复杂度,添加页面加载时间,添加http会话数)

4)在某些浏览器中,table里的文字的拷贝会出现问题。(会让客户不悦)
5)table会影响其内部的某些布局属性的生效(比方<td>里的元素的height:100%) (限制页面设计的自由性)
6)一旦学了CSS的知识,你会发现使用table做页面布局会变得更麻烦。(先花时间学少量CSS知识,会省去你以后大量的时间)
7)‘table对’对于页面布局来说,从语义上看是不正确的。(它形容的是体现,而不是内容)
8)table代码可读性差。(不但无法利用CSS,而且会不知所云,尤其在进行页面改版或者内容抽取的时候)
9)table一旦设计完成就变成死的,很难通过CSS让它展示新的面貌

3、减少不必要的嵌套,尽量扁平化

由于当浏览器编译器遇到一个标签时就开始寻觅它的结束标签,直到它匹配上才能显示它的内容,所以当嵌套很多标签时会影响页面加载速度(当然这个影响微乎其微)。

三、CSS

是时候展示真正的技术了,什么叫专业前台。

1. 内联首屏关键CSS

性能优化中有一个重要的指标——初次有效绘制(First Meaningful Paint,简称FMP)即指页面的首要内容出现在屏幕上的时间。这一指标影响客户看到页面前所需等待的时间,FMP能减少这一时间。
这点对于电商网站特别重要,做后端管理的可以忽视。

将CSS直接内联到HTML文档中能使CSS更快速地下载。而使用外部CSS文件时,需要在HTML文档下载完成后才知道所要引用的CSS文件,而后才下载它们。所以说,内联CSS能够使浏览器开始页面渲染的时间提前,由于在HTML下载完成之后就能渲染了。所以不能一味的没有想法的通过link标签引用外部CSS文件。

但是,又不能内联所有的CSS,这种方式并不适用于内联较大的CSS文件。由于初始拥塞窗口存在限制(TCP相关概念,通常是 14.6kB,压缩后大小),假如内联CSS后的文件超出了这一限制,系统就需要在服务器和浏览器之间进行更屡次的往返,这样并不能提前页面渲染时间。因而,我们应当只将渲染首屏内容所需的关键CSS内联到HTML中

如何确定首屏关键CSS呢?显然,我们不需要手动确定哪些内容是首屏关键CSS。推荐Github上一个开源项目Critical CSS,可以将属于首屏的关键样式提取出来。

不过内联CSS有一个缺点,内联之后的CSS不会进行缓存,每次都会重新下载。所以上面的说到的初始拥塞窗口将内联后的文件大小控制在了14.6kb以内,每次重新下载14.6kb可以接受。

2.异步加载css

CSS会阻塞渲染,在CSS文件请求、下载、解析完成之前,浏览器将不会渲染任何已解决的内容。有时,这种阻塞是必需的,由于我们需要在css加载后再开始渲染页面。那么将首屏关键CSS内联后,剩余的CSS内容的阻塞渲染就不是必须的了,可以使用外部CSS,并且异步加载。

如何实现CSS的异步加载呢?
(前3点看看就好,重点用第4点)
1)使用JavaScript动态创立样式表link元素,并插入到DOM中。

// 创立link标签const myCSS = document.createElement( "link" );myCSS.rel = "stylesheet";myCSS.href = "mystyles.css";// 插入到header的最后位置document.head.insertBefore( myCSS, document.head.childNodes[ document.head.childNodes.length - 1 ].nextSibling );

2)将link元素的media属性设置为客户浏览器不匹配的媒体类型(或者媒体查询),如media="print",甚至可以是完全不存在的类型media="noexist"。对浏览器来说,假如样式表不适用于当前媒体类型,其优先级会被放低,会在不阻塞页面渲染的情况下再进行下载。

<link rel="stylesheet" href="mystyles.css" media="noexist" onload="this.media='all'">

当然,这么做只是为了实现CSS的异步加载,别忘了在文件加载完成之后,将media的值设为screen或者all,从而让浏览器开始解析CSS。

3)我们还可以通过rel属性将link元素标记为alternate可选样式表,也能实现浏览器异步加载。同样别忘了加载完成之后,将rel改回去。

<link rel="alternate stylesheet" href="mystyles.css" onload="this.rel='stylesheet'">

4)然而,上述的三种方法已过时。现在,rel="preload"这一Web标准指出了如何异步加载资源,包括CSS类资源。

<link rel="preload" href="mystyles.css" as="style" onload="this.rel='stylesheet'">

注意,as是必需的。忽略as属性,或者者错误的as属性会使preload等同于XHR请求,浏览器不知道加载的是什么内容,因而此类资源加载优先级会非常低。

看起来,rel="preload"的用法和上面两种没什么区别,都是通过更改某些属性,使得浏览器异步加载CSS文件但不解析,直到加载完成并将修改复原,而后开始解析

但是它们之间其实有一个很重要的不同点,那就是使用preload,比使用不匹配的media方法能够更早地开始加载CSS

3. 文件压缩(效果明显)

文件的大小会直接影响浏览器的加载速度,这一点在网络较差时体现地尤为显著。相信大家都早已习惯对CSS进行压缩,现在的构建工具,如webpack、gulp/grunt、rollup等也都支持CSS压缩功能。压缩后的文件大小显著减小,可以大大降低了浏览器的加载时间。

4. 去除无用CSS

尽管文件压缩能够降低文件大小。但CSS文件压缩通常只会去除无用的空格。而去除无用CSS会进一步减小CSS文件的大小。(文件压缩是个前台都知道,而去除无用css相信很多人没用到过,还有前面异步加载的黑科技,面试就是各种知识细节堆积,加起来就能给人一种靠谱专业的冲击感)

一般情况下,会存在这两种无用的CSS代码:一种是不同元素或者者其余情况下的重复代码,一种是整个页面内没有生效的CSS代码。对于前者,在编写的代码时候,我们应该尽可能地提取公共类,减少重复。对于后者,在不同开发者进行代码维护的过程中,总会产生不再使用的CSS的代码,当然一个人编写时也有可能出现这一问题。而这些无用的CSS代码不仅会添加浏览器的下载量,还会添加浏览器的解析时间,这对性能来说是很大的消耗。所以我们需要找到并去除这些无用代码。

当然,假如手动删除这些无用CSS是很低效的。我们可以借助Uncss库来进行。Uncss可以用来移除样式表中的无用CSS,并且支持多文件和JavaScript注入的CSS。

5.有选择地使用选择器

由于CSS选择器的匹配是从右向左进行的,所以尽量保持以下准则:

  • 保持简单,不要使用嵌套过多过于复杂的选择器。
  • 通配符和属性选择器效率最低,需要匹配的元素最多,尽量避免使用。 (子元素选择器它不香吗)
  • 不要使用类选择器和ID选择器修饰元素标签,这样多此一举,还会降低效率。
  • 不要为了追求速度而放弃代码的可读性与可维护性,没有绝对的优化。

一般开发团队都会定好css编写规范,假如你的团队还没定,那么你肯定要普及大家用同一规范(为什么呢,难道看别人代码时不难受吗?),或者者引用借鉴少量:如BEM、OOCSS、SUIT、SMACSS、ITCSS等作为CSS编写规范。使用统一的方法论能够帮助大家形成统一的风格,减少命名冲突,也能避免上述的问题。

6.减少使用昂贵的属性

在浏览器绘制屏幕时,所有需要浏览器进行操作或者计算的属性相对而言都需要花费更大的代价。当页面发生重绘时,它们会降低浏览器的渲染性能。所以在编写CSS时,我们应该尽量减少使用昂贵属性,如box-shadow、border-radius、filter、透明度、:nth-child等。

当然,并不是让大家不要使用这些属性(比方border-radius的确不得不用),由于这些应该都是我们经常使用的属性。只是当有两种方案可以选择的时候,可以优先选择没有昂贵属性或者昂贵属性更少的方案,假如每次都做这样的决策,网站的性能会在不知不觉中得到肯定的提升。

7. 优化重排与重绘(划重点)

在网站的使用过程中,某些操作会导致样式的改变,这时浏览器需要检测这些改变并重新渲染,其中有些操作所耗费的性能更多。我们都知道,当FPS为60时,客户使用网站时才会感到流畅。这也就是说,我们需要在16.67ms内完成每次渲染相关的所有操作,所以我们要尽量减少耗费更多的操作。

(1)减少重排

重排会导致浏览器重新计算整个文档,重新构建渲染树,这一过程会降低浏览器的渲染速度。如下所示,有很多操作会触发重排,我们应该避免频繁触发这些操作:

  • 改变font-sizefont-family
  • 改变元素的内外边距
  • 通过JS改变CSS类
  • 通过JS获取DOM元素的位置相关属性(如width/height/left等)
  • CSS伪类激活
  • 滚动滚动条或者者改变窗口大小

此外,我们还可以通过CSS Trigger查询哪些属性会触发重排与重绘。

值得一提的是,某些CSS属性具备更好的重排性能。如使用Flex时,比使用inline-blockfloat时重排更快,所以在布局时可以优先考虑Flex

(2)避免不必要的重绘

当元素的外观(如color,background,visibility等属性)发生改变时,会触发重绘。在网站的使用过程中,重绘是无法避免的。不过,浏览器对此做了优化,它会将屡次的重排、重绘操作合并为一次执行。不过我们仍需要避免不必要的重绘,如页面滚动时触发的hover事件,可以在滚动的时候禁用hover事件,这样页面在滚动时会更加流畅。

此外,我们编写的CSS中动画相关的代码越来越多,我们已经习惯于使用动画来提升客户体验。我们在编写动画时,也应当参考上述内容,减少重绘重排的触发。除此之外我们还可以通过硬件加速will-change来提升动画性能,感兴趣可百度。

最后需要注意的是,客户的设施可能并没有想象中的那么好,至少不会有我们的开发机器那么好。我们可以借助Chrome的开发者工具进行CPU降速,而后再进行相关的测试,降速方法如下图所示。假如需要在手机端访问的,最好将速度限制更低,由于手机端的性能往往更差。

先点击最右边的设置图标,而后勾选画圆圈的方框,最后将CPU调制到4倍降速,手机端调成8倍降速

8. 不要使用@import引入CSS

最后提一下,不要使用@import引入CSS,相信大家也很少使用。

不建议使用@import主要有以下两点起因:

首先,使用@import引入CSS会影响浏览器的并行下载。使用@import引用的CSS文件只有在引用它的那个css文件被下载、解析之后,浏览器才会知道还有另外一个css需要下载,这时才去下载,而后下载后开始解析、构建render tree等一系列操作。这就导致浏览器无法并行下载所需的样式文件。

其次,多个@import会导致下载顺序紊乱。在IE中,@import会引发资源文件的下载顺序被打乱,即排列在@import后面的js文件先于@import下载,并且打乱甚至破坏@import自身的并行下载。

所以不要使用这一方法,使用link标签就行了。

四、js

(假如说css优化是锦上添花,那么css是花,js优化才是锦)

1.减少全局变量的查找。

由于全局变量在作用域链的最顶端,频繁查找很耗性能。所以在一个函数中尽量将全局对象存储为局部变量来查找,由于访问局部变量的数要更快少量。

function(){alert(window.location.href+window.location.host);}修改为:funciton(){var location=window.location;alert(location.href+location.host);}

2.慎用 with

with会创立自已的作用域,因而会添加其中执行代码的作用域的长度。(我如同素来没用过with,也没见别人代码里用过)
with(obj){ p = 1};代码块的行为实际上是修改了代码块中的 执行环境,将obj放在了其作用域链的最前台,在 with代码块中访问非局部变量是都是先从 obj上开始查找,假如没有再依次按作用域链向上查找,因而使用 with相当于添加了作用域链长度。而每次查找作用域链都是要消耗时间的,过长的作用域链会导致查找性能下降。
  因而,除非你能一定在 with代码中只访问 obj中的属性,否则慎用 with,可以使用局部变量缓存需要访问的属性。

with(a,b,c,d){      property1=1;      property2=2;}修改为:var obj=a.b.c.d;obj.property1=1;obj.property2=2;

3.慎用 定时器

(1)假如针对的是不断运行的代码,不应该使用setTimeout,而应该使用setInterval。由于setTimeout每一次都会初始化一个定时器。而setInterval只会在开始的时候初始化一个定时器。
(2)不要给setTimeout或者setInterval传递字符串参数。

4 避免使用 eval和 Function

每次 eval 或者 Function 构造函数作用于字符串表示的源代码时,脚本引擎都需要将源代码转换成可执行代码。这是很消耗资源的操作 —— 通常比简单的函数调用慢 100倍以上。
  eval 函数效率特别低,因为事前无法知晓传给 eval 的字符串中的内容,eval在其上下文中解释要解决的代码,也就是说编译器无法优化上下文,因而只能有浏览器在运行时解释代码。这对性能影响很大。
  Function 构造函数比 eval略好,由于使用此代码不会影响附近代码 ;但其速度仍很慢。
  此外,使用 eval和 Function也不利于Javascript 压缩工具执行压缩。

5.优化循环

循环是编程中最常见的结构,优化循环是性能优化过程中很重要的一部分。一个循环的基本优化步骤如下:

1)减值迭代——大多数循环使用一个从0开始,添加到某个特定值的迭代器。在很多情况下,从最大值开始,在循环中不断减值的迭代器更加有效。
2)简化终止条件——因为每次循环过程都会计算终止条件,故必需保证它尽可能快,即避免属性查找或者其它操作。最好是将循环控制量保存到局部变量中,也就是说对数组或者列表对象遍历的时候,提前将length保存到局部变量中,避免循环的每一步重复取值。
3)简化循环体——循环体是执行最多的,故要确保其被最大限度地优化。确保没有某些可以被很容易移出循环的密集计算。
4)使用后测试循环—— 在js中,我们常用的循环中:
for(in)的效率极差,由于他需要查询散列键,只需可以,就应该尽量少用。
for和while循环,while优于for,可能for结构问题,需要经常的跳转。do..while更好。
最常用的for和while循环都是前测试循环,然后测试循环如do-while循环可以避免最初终止条件的计算,因些计算更快。

for(var i = 0; i < values.length; i++) {    process(values[i]);}优化1:简化终止条件 for(var i = 0, len = values.length; i < len; i++) {    process(values[i]);}优化2:使用后测试循环(注意:使用后测试循环需要确保要解决的值至少有一个) var i values.length - 1;if(i > -1) {    do {        process(values[i]);    }while(--i >= 0);}

6.字符串拼接

在 Javascript中使用"+" 号来拼接字符串效率是比较低的,由于每次运行都会开拓新的内存并生成新的字符串变量,而后将拼接结果赋值给新变量。与之相比更为高效的做法是使用数组的 join方法,即将需要拼接的字符串放在数组中最后调用其 join方法得到结果。不过因为使用数组也有肯定的开销,因而当需要拼接的字符串较多的时候可以考虑用此方法。

1)假如需要连接多个字符串,应该少使用+=:

如s+=a;s+=b;s+=c;修改为:s+=a+b+c;

2)而假如是收集字符串,比方屡次对同一个字符进行+=操作的话,最好使用一个缓存,使用js数组来收集,最后join方法连接起来。

var buf=[];for(var i=0;i<100;i++){      buf.push(i.toString());}var all=buf.join("");

7.少操作DOM

DOM操作应该是脚本中最耗性能的一类操作,例如添加、修改、删除 DOM元素或者者对 DOM集合进行操作。

(1)最小化现场升级

一旦你需要访问的DOM部分是已经显示的页面的一部分,那么你就是在进行一个现场升级。之所以叫现场升级,是由于需要立即(现场)对页面对客户的显示进行升级,每一个更改,不论是插入单个字符还是移除整个片段,都有一个性能惩罚,由于浏览器需要重新计算无数尺寸以进行升级。现场升级进行的越多,代码完成执行所花的时间也越长。

(2)多使用innerHTML

使用一次innerHTML复制代替构建dom元素。
有两种在页面上创立DOM节点的方法:使用诸如createElement()和appendChild()之类的DOM方法,以及使用innerHTML。对于小的DOM更改,两者效率差不多,但对于大的DOM更改,innerHTML要比标准的DOM方法创立同样的DOM结构快得多。

当使用innerHTML设置为某个值时,后端会创立一个HTML解释器,而后使用内部的DOM调用来创立DOM结构,而非基于JS的DOM调用。而内部方法是编译好的而非解释执行,故执行的更快。

varfrag=document.createDocumentFragment();for(var i=0;i<1000;i++){      varel=document.createElement('p');      el.innerHTML=i;      frag.appendChild(el);}document.body.appendChild(frag);应该改为:var html=[];for(var i=0;i<1000;i++){      html.push('<p>'+i+"</p>");}document.body.innerHTML=html.join("");

五、客户体验角度

(1)预加载,懒加载
(2)浏览器缓存,应用缓存
(3)细节设计(全选/反全选/自动补全……所有你能想到的)

假如觉得干货很多,请给一个赞。

  • 全部评论(0)
最新发布的资讯信息
【系统环境|】2FA验证器 验证码如何登录(2024-04-01 20:18)
【系统环境|】怎么做才能建设好外贸网站?(2023-12-20 10:05)
【系统环境|软件环境】梦幻仙域游戏攻略(2023-12-19 10:02)
【系统环境|软件环境】梦幻仙域游戏攻略(2023-12-19 10:02)
【系统环境|】卡帕部落揭秘潮玩新宠,探究玩法(2023-12-14 09:45)
【系统环境|数据库】 潮玩宇宙游戏道具收集方法(2023-12-12 16:13)
【系统环境|】如何开发搭建卡帕部落模式源码(2023-12-12 10:44)
【系统环境|】遥遥领先!青否数字人直播系统5.0发布,支持真人接管实时驱动!(2023-10-12 17:31)
【系统环境|服务器应用】克隆自己的数字人形象需要几步?(2023-09-20 17:13)
【系统环境|】Tiktok登录教程(2023-02-13 14:17)
手机二维码手机访问领取大礼包
返回顶部