前端开发总结:如何优化网站性能?

2019-06-05 10:35:01 浏览数 (1)

本文是2018-10-10与blog.beifengtz.com发表的博客。

学前端这么久了,从一无所知到web网页的开发,自己也是踩了巨多的坑,自己也在不断的摸索中,短时间内可能不会再做前端了,毕竟java是我的主方向。总结一下web网站在性能提升方面前端能做些什么优化,其中有结合一些资料,也有自己的经验之谈,毕竟不是专门学前端的,有不对的地方敬请多多指教。

一、能用Class定义样式尽量不用ID

class和id是前端页面选择dom元素最常用的两种方式,对于如何合理的使用它们是一个问题。对于class类可以在页面多次定义,可以看作是一个数组,允许同一页面出现很多次,理论上浏览器查找起来比较慢;id是页面内唯一定义的,不可重复定义,理论上浏览器查找起来会比较快,比较只有一个。

但是在为dom元素赋予样式的时候能用class尽量不用id,两个原因:

  1. class重绘速度比id快;
  2. id不利于dom样式继承,并且id一般用于js脚本选择,class一般用于样式定义,分工明确便于管理。

有人做过这样的测试:在Windows系统上的Firefox 6上,测得了一个简单类选择器的(reflow figure)重绘速度为10.9ms,而ID选择器为12.5ms,所以事实上ID比类选择器重绘要慢一点点。

当然尽量少使用标签选择器,在一个标签选择器(a)的测试上显示,它比类或ID选择器的速度慢了很多。在一个嵌套很深的后代选择器的测试上,显示数据为440左右!从这里我们可以看出ID/类选择器 和 元素/后代选择器中间的差异较大,但是相互之间的差异较小。

效率上:

Class选择器 > ID选择器 > Tag选择器

优先级上:

ID选择器 > Class选择器 > Tag选择器

二、合理使用css选择器

本节参考自:https://www.w3cschool.cn/css/css-selector.html

1、避免使用class选择器限制id选择器

代码语言:javascript复制
避免
button#backButton {…}
避免
.menu-left#newMenuIcon {…}
优先
#backButton {…}
优先
#newMenuIcon {…}

2、避免使用标签限制 class 选择器

代码语言:javascript复制
避免
treecell.indented {…}
优先
.treecell-indented {…}
最好
.hierarchy-deep {…}

3、避免使用多层标签选择器。使用 class 选择器替换,减少css查找

代码语言:javascript复制
避免
treeitem[mailfolder="true"] > treerow > treecell {…}
优先
.treecell-mailfolder {…}

4、避免使用子选择器

代码语言:javascript复制
避免
treehead treerow treecell {…}
更好
treehead > treerow > treecell {…}
优先
.treecell-header {…}

5、使用继承

代码语言:javascript复制
避免
#bookmarkMenuItem > .menu-left { list-style-image: url(blah) }
优先
#bookmarkMenuItem { list-style-image: url(blah) }

在暑假做web前端培训的时候我就看过选择器的优化,当时我说的不是很清楚,这次稍微总结一下:

总的来说在选择器使用过程中有几个大忌做到就可以了

  1. 绝不用优先级低的选择器限制优先级高的选择器。比如说div .container这种,用标签选择器限制了类选择器,大大降低了浏览器查找的效率
  2. 尽量少使用层级关系。特别是子选择器的使用,当出现类似于这种*#box .container > .red p*情况,这样多层选择还不如干脆取一个新的class来代替它,虽然这样设置的优先级非常高但是效率会很低,一般层级有个两三层就可以了,最多不能超过三层。

三、不重复设置样式

CSS全名虽然是层叠样式表,意思是样式可以重复定义,但是我们在实际使用中尽量避免这种多层样式设置,比如说我要设置一个段落字体大小font-size:20px;,我们一般用一个外部样式表设置class给它定义就行了,只用class定义它的字体大小为20px我们对它字体大小的属性只层叠了两次,因为默认属性又一次。下面这种做法尽量避免:

html代码

代码语言:javascript复制
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>css性能优化</title>
  <link rel="stylesheet" href="test.css">
  <style>
    .paragraph{
      font-size: 18px;
    }
</style>
</head>
<body>
  <p class="paragraph" style="font-size: 20px">这是一个测试段落</p>
</body>
</html>

test.css代码

代码语言:javascript复制
.paragraph{
  font-size:15px;
}

上面是对一个段落字体大小设置的错误做法,默认属性有对p标签字体大小定义默认属性,内联样式、嵌入式、外部样式分别对其字体大小定义了,总共层叠了四次,但是有效的只有内联样式一次。这样的重复定义是无意义的,而且会增大浏览器开销,小页面可能不能体现出来它的鸡肋之处,但是当页面dom变得复杂的时候就一目了然了。

四、具有动画的元素尽量不用相对定位

我平时比较喜欢使用动画,使用过程中发现在设置了动画的元素position属性使用absolute或fixed性能会好很多。这是因为流式布局的页面下,一旦前面动画元素在执行动画时影响到了其他的元素的定位,它后面的所有元素均会跟着动,这样页面渲染给浏览器增加了极大的负担,非常消耗cpu和gpu,所以尽量对具有动画效果的元素定位设置为absolutefixed

五、动画分步加载

所有的动画执行都会很消耗内存,如果在页面一开始就同时执行很多动画就很容易出现卡顿现象。所以尽量设置动画延时分步骤执行动画,让动画执行有一定先后顺序,减轻cpu压力。如果页面较长,可结合滚动监听分步执行动画,让浏览器窗口滚动到该页面才执行动画,这样不但提升了渲染性能还增加了用户体验。

六、图片懒加载

很让人头疼的就是在页面一打开的时候需要加载很多图片,导致页面显示图片很慢而且页面可能会变卡。一般在页面一打开默认是一次性加载所有静态资源,如果页面图片较多可使用懒加载减轻第一次加载图片的压力。

这里顺便说一下懒加载实现的方法:将需要加载的图片路径放到元素的data中,比如<div data-image="图片路径"></div>,然后结合滚动监听,监听页面即将滑到需要加载的元素时,用js将data-image的值赋给元素的css background-url属性,这样就达到了懒加载效果。

七、减少http请求

在整个页面显示的过程中,只有10%~20%的最终用户响应时间花在了下载HTML文档上。其余的80%~90%时间花在了下载页面中的所有组件上。而下载就得进行http请求,因此改善http请求远远比前面优化页面效果来的更好。

这样举一个例子,我现在有一个200KB的文件和两个100KB的文件,一次下载200KB的速度会比两次下载100KB的速度快很多。所以我们尽量将能合并的文件都合并,比如合并css样式表、合并js脚本、图片合并等等。特别是图片的合并,在一个页面一般都会有很多图标,而图标一般又是图片,比如页面有二十个图标那就要进行二十次http请求,如果我们把二十个图标合成一张图,用CSS Sprites或者图片地图来设置显示的图标,这样就只需一次http请求。

使用图片地图的缺点:指定坐标区域时,矩形或圆形比较容易指定,而其它形状手工指定比较难。

CSS Sprites直译过来就是CSS精灵,用background-position来定位图标。

八、尽量使用外联样式

前面虽然说了尽量减少http请求,但是在设置样式时尽量不使用内联样式或者嵌入式。首先我们一般写html代码和css代码是分开的,尽量不要融合到一起,这样会给你管理页面代码带来极大的负担。其次使用外联样式表可以运用浏览器缓存机制,从而在不同的请求内容之间重用,如果浏览器有该样式表的缓存就不会进行http请求。详细浏览器解析一次请求的过程请看下面减少DNS查找片段。

九、样式表放在头部

经样式表(css)放在网页的HEAD中会让网页显得加载速度更快,因为这样做可以使浏览器逐步加载已将下载的网页内容。这对内容比较多的网页尤其重要,用户不用一直等待在一个白屏上,而是可以先看已经下载的内容。如果将样式表放在底部,浏览器会拒绝渲染已经下载的网页,因为大多数浏览器在实现时都努力避免重绘,样式表中的内容是绘制网页的关键信息。

十、脚本放在尾部

js的下载和执行会阻塞Dom树的构建(严谨地说是中断了Dom树的更新),所以script标签放在首屏范围内的HTML代码段里会截断首屏的内容。下载脚本时并行下载是被禁用的——即使使用了不同的主机名,也不会启用其他的下载。因为脚本可能修改页面内容,因此浏览器会等待;另外,也是为了保证脚本能够按照正确的顺序执行,因为后面的脚本可能与前面的脚本存在依赖关系,不按照顺序执行可能会产生错误。把脚本置底,这样可以让网页渲染所需要的内容尽快加载显示给用户。

十一、使用CDN加速

如果应用程序web服务器离用户更近,那么一个HTTP请求的响应时间将缩短。另一方面,如果组件web服务器离用户更近,则多个HTTP请求的响应时间将缩短。

CDN(内容发布网络)是一组分布在多个不同地理位置的Web服务器,用于更加有效地向用户发布内容。在优化性能时,向特定用户发布内容的服务器的选择基于对网络慕课拥堵的测量。例如,CDN可能选择网络阶跃数最小的服务器,或者具有最短响应时间的服务器。

CDN还可以进行数据备份、扩展存储能力,进行缓存,同时有助于缓和Web流量峰值压力。

但是CDN也会有缺点:

  1. 响应时间可能会受到其他网站流量的影响。CDN服务提供商在其所有客户之间共享Web服务器组。
  2. 如果CDN服务质量下降了,那么你的工作质量也将下降
  3. 无法直接控制组件服务器

十二、减少DNS查找

当我们在浏览器的地址栏输入网址 www.beifnegtz.com,然后回车,回车这一瞬间到看到页面到底发生了什么呢?

域名解析 --> 发起TCP的3次握手 --> 建立TCP连接后发起http请求 --> 服务器响应http请求,浏览器得到html代码 --> 浏览器解析html代码,并请求html代码中的资源(如js、css、图片等) --> 浏览器对页面进行渲染呈现给用户

域名解析是页面加载的第一步,那么域名是如何解析的呢?以Chrome为例:

  1. Chrome浏览器 会首先搜索浏览器自身的DNS缓存(缓存时间比较短,大概只有1分钟,且只能容纳1000条缓存),看自身的缓存中是否有 www.beifnegtz.com 对应的条目,而且没有过期,如果有且没有过期则解析到此结束。 注:我们怎么查看Chrome自身的缓存?可以使用 chrome://net-internals/#dns 来进行查看
  2. 如果浏览器自身的缓存里面没有找到对应的条目,那么Chrome会搜索操作系统自身的DNS缓存,如果找到且没有过期则停止搜索解析到此结束. 注:怎么查看操作系统自身的DNS缓存,以Windows系统为例,可以在命令行下使用 ipconfig /displaydns 来进行查看
  3. 如果在Windows系统的DNS缓存也没有找到,那么尝试读取hosts文件(位于C:WindowsSystem32driversetc),看看这里面有没有该域名对应的IP地址,如果有则解析成功。
  4. 如果在hosts文件中也没有找到对应的条目,浏览器就会发起一个DNS的系统调用,就会向本地配置的首选DNS服务器(一般是电信运营商提供的,也可以使用像Google提供的DNS服务器)发起域名解析请求(通过的是UDP协议向DNS的53端口发起请求,这个请求是递归的请求,也就是运营商的DNS服务器必须得提供给我们该域名的IP地址),运营商的DNS服务器首先查找自身的缓存,找到对应的条目,且没有过期,则解析成功。如果没有找到对应的条目,则有运营商的DNS代我们的浏览器发起迭代DNS解析请求,它首先是会找根域的DNS的IP地址(这个DNS服务器都内置13台根域的DNS的IP地址),找打根域的DNS地址,就会向其发起请求(请问www.beifnegtz.com这个域名的IP地址是多少啊?),根域发现这是一个顶级域com域的一个域名,于是就告诉运营商的DNS我不知道这个域名的IP地址,但是我知道com域的IP地址,你去找它去,于是运营商的DNS就得到了com域的IP地址,又向com域的IP地址发起了请求(请问www.beifnegtz.com这个域名的IP地址是多少?),com域这台服务器告诉运营商的DNS我不知道www.beifnegtz.com这个域名的IP地址,但是我知道beifnegtz.com这个域的DNS地址,你去找它去,于是运营商的DNS又向beifnegtz.com这个域名的DNS地址(这个一般就是由域名注册商提供的,像万网,新网等)发起请求(请问www.beifnegtz.com这个域名的IP地址是多少?),这个时候beifnegtz.com域的DNS服务器一查,诶,果真在我这里,于是就把找到的结果发送给运营商的DNS服务器,这个时候运营商的DNS服务器就拿到了www.beifnegtz.com这个域名对应的IP地址,并返回给Windows系统内核,内核又把结果返回给浏览器,终于浏览器拿到了www.beifnegtz.com对应的IP地址,该进行一步的动作了。

注:一般情况下是不会进行以下步骤的 如果经过以上的4个步骤,还没有解析成功,那么会进行如下步骤:

  1. 操作系统就会查找NetBIOS name Cache(NetBIOS名称缓存,就存在客户端电脑中的),那这个缓存有什么东西呢?凡是最近一段时间内和我成功通讯的计算机的计算机名和Ip地址,就都会存在这个缓存里面。什么情况下该步能解析成功呢?就是YUI Compressor工具压缩,这个工具是目前使用最多的js、css文件压缩工具,大部分在线网站的压缩后台其实也是用的它,它是用java写的,所以如果你想在本地压缩的话需要配置java环境,当然对于java开发者强推该工具,使用它需要输入一些命令,如果嫌麻烦可以像我一样写一个程序或者批处理文件,每次直接选择文件就可以了哈哈哈。该名称正好是几分钟前和我成功通信过,那么这一步就可以成功解析。
  2. 如果第5步也没有成功,那会查询WINS 服务器(是NETBIOS名称和IP地址对应的服务器)
  3. 如果第6步也没有查询成功,那么客户端就要进行广播查找
  4. 如果第7步也没有成功,那么客户端就读取LMHOSTS文件(和HOSTS文件同一个目录下,写法也一样)

如果第八步还没有解析成功,那么就宣告这次解析失败,那就无法跟目标计算机进行通信。只要这八步中有一步可以解析成功,那就可以成功和目标计算机进行通信。

十三、压缩js、css、图片

减小请求资源的体积大小无疑是最直接的办法,我一般在做网页时都会将这些资源进行压缩,压缩js和css文件推荐使用Yahoo的YUI Compressor工具压缩,这个工具是目前使用最多的js、css文件压缩工具,大部分在线网站的压缩后台其实也是用的它,它是用java写的,所以如果你想在本地压缩的话需要配置java环境,当然对于java开发者强推该工具,使用它需要输入一些命令,如果嫌麻烦可以像我一样写一个程序或者批处理文件,每次直接选择文件就可以了哈哈哈。

压缩js和css不但可以起到减小文件大小的作用,在安全方面还有一定作用,可以混淆其中的变量、注释等,至少增大了第三者翻译源代码的难度。

十四、使用Ajax缓存

Ajax是我平时使用最多一个工具,其全面是“Asynchronous JavaScript and XML”(异步的JavaScript与XML),Ajax的目地是为突破web本质的开始—停止交互方式,向用户显示一个白屏后重绘整个页面不是一种好的用户体验。

POST的请求,是不可以在客户端缓存的,每次请求都需要发送给服务器进行处理,每次都会返回状态码200。(可以在服务器端对数据进行缓存,以便提高处理速度)

GET的请求,是可以(而且默认)在客户端进行缓存的,除非指定了不同的地址,否则同一个地址的AJAX请求,不会重复在服务器执行,而是返回304。

提高Ajax性能的措施中最重要的方法就是使响应具有可缓存性(为文件头指定ExpiresCache-Control )

0 人点赞