web前端性能优化

JavaScript文件加载

管理浏览器中的JavaScript代码是一个棘手的问题,因为代码执行会阻塞浏览器,比如界面绘制。每次遇到<script>标签,浏览器都会停下来等待代码下载并执行,然偶再继续处理其他部分。我们可以通过如下几种方法来减少JavaScript文件对性能的影响

将JS文件放在页面底部

将所有<script>标签放置在页面的底部,紧靠body关闭标签</body>的上方。这样可以保证页面在脚本运行之前完成解析

将JS文件打包

将JS文件打包,页面的<script>标签越少,页面的加载速度越快,响应也越迅速。无论外部脚本文件还是内敛代码都是如此

使用非阻塞方式下载

  1. <script>标签中添加defer属性
  2. 动态创建<script>元素,用它下载并执行代码
  3. 用XHR对象下载代码,并注入到页面中

数据访问

四种访问方式

在JavaScript中,数据存储位置可以对代码整体性能产生重要影响,有四种数据访问类型:直接量,变量,数组项,对象成员,这些访问方式性能不同

直接量和局部变量访问速度很快,而数组项和对象成员需要更长的时间

作用域链和闭包的访问速度

在函数内部访问变量时,会顺着作用域链向上查找,直到找到为止,这也意味着作用域链越长,平均的查找时间也就越长。而像with和try-catch会增加作用域链的长度,所以也会降低性能。由此可以得知,访问全局变量很慢,因为他们在作用域链的最后一环

原型链访问

访问对象的属性的时候,我们有时候会需要遍历原型链,这也意味着原型链越长,查找元素的平均效率就越低,变量所在的原型在原型链中越深,访问越慢

DOM

当浏览器下载完所有的HTML、JavaScript、CSS、图片之后,它会解析文件并创建两个内部数据结构:一棵DOM树和一棵渲染树

每个需要被显示的DOM树节点在渲染树中至少有一个节点(隐藏的DOM节点自然在渲染树中没有节点),渲染树上的点被称为框或者盒,根据CSS模型定义,将页面元素看做一个具有填充、边距、边框和位置的盒。一旦DOM树和渲染树构造完毕,浏览器就可以绘制页面上的元素了。

当DOM改变影响到元素的几何属性(宽和高),浏览器就需要重新计算元素的几何属性。如果这个元素的改变影响到其他元素,浏览器会使渲染树上收到影响的部分失效,然后重构渲染树。这就是回流(也叫重排版)。重排版完成时,浏览器在一个重绘进程中重新绘制屏幕上受影响的部分

当然并不是所有DOM改变都会影响到几何属性,比如改变背景颜色之类的,这种情况下就只会触发重绘

回流和重绘都是负担很重的操作,会导致浏览器阻塞,所以需要尽可能避免

何时触发回流

  1. 添加或删除可见的DOM元素
  2. 元素位置的改变
  3. 元素尺寸的改变(border、padding、margin、height、width)
  4. 内容改变
  5. 最初页面渲染
  6. 浏览器窗口改变尺寸

查询并刷新渲染树的改变

因为计算量与每次回流有关,大多数浏览器会通过一个渲染队列来进行优化。但是用JavaScript获取一些DOM属性时,会(不由自主地)强迫队列中的所有渲染事件前不完成。比如获取如下属性:
* offsetTop, offsetLeft, offsetWidth, offsetHeight
* scrollTop, scrollLeft, scrollWidth, scrollHeight
* clientTop, clientLeft, clientWidth, clientHeight
* getComputedStyle() (在IE中为currentStyle)

为了让这些属性返回正确的值,浏览器不得不运行渲染队列中所有的渲染事件,这样才能保证值的正确。所以尽量减少这些属性的访问

判断

if-else

多个if-else执行的时候,其会顺序检索每一个条件,直到所有条件检索完或检索到匹配的条件。所以我们可以通过树的形式组织if语句,如下面代码:

if(con === 1) {return result1;}
else if(con === 2) {return result2;}
else if(con === 3) {return result3;}
else if(con === 4) {return result4;}
else if(con === 5) {return result5;}
else if(con === 6) {return result6;}
else if(con === 7) {return result7;}
else if(con === 8) {return result8;}
else if(con === 9) {return result9;}

这段代码就会依次判断con是否等于1,2,3,4,5,6,7,8,9,如果是9的话,就会判断9次,我们可以将其改为:

if (con <= 3){
    if(con === 1) {return result1;}
    else if(con === 2) {return result2;}
    else {return result3;}
} else if(con > 3 && con <= 6){
    if(con === 4) {return result4;}
    else if(con === 5) {return result5;}
    else {return result6;}
} else if(con <= 9){
    if(con === 7) {return result7;}
    else if(con === 8) {return result8;}
    else {return result9;}
}

这样我们通过三叉树的形式,就可以减少查找次数了,比如这里查找9次,分别判断 0~3,3~6,6~9,7,8,9,只需要6次

if-else除了通过这种树形组织编码以外,还有一个优化的地方。由于其顺序判断的逻辑,我们可以根据概率来,将概率比较大的判断放在前面,概率较小的放在后面,这样也可以减少平均查找长度

switch

事实证明,大多数情况下switch比if-else更快,但是只有条件题数量很大的时候,才能明显更快。if-else在条件增加时,所带来的性能负担要高于switch,因此建议使用switch。不过switch只是用来判断几个不同的离散值,并没有if-else能判断离散值或值域那样的灵活性

打表法

可以使用打表的形式来做,把所有的处理函数放在一个数组中,然后将条件作为键,这种方法比switch和if-else都要快,而且在新增条件时,不会带来额外的性能开销

递归

很多算法都是递归实现,由于递归会多次触发函数调用,而函数调用也是需要开销的(比如创建运行期上下文、压运行期栈、创建AO、复制作用域链、销毁AO、弾栈等等),所以尽量将递归转变为循环。而运行期栈在很多浏览器中也有深度限制,当到达运行期栈的最大深度时,浏览器有各自的处理方式,但都是按照错误进行处理

字符串

不同的拼接方法

字符串拼接有很多不同的方法:
1. 使用+直接拼接
2. 使用+=拼接
3. 使用Array.join()拼接
4. 使用String.concat()拼接

使用加号拼接

使用++=时,不同的浏览器会做不同程度的优化,如果在IE7和他之前的浏览器,优化做的不好,比如如下操作:

str += "one" + "two"

实际上会执行如下步骤:
1. 内存中创建一个临时变量
2. 将这个临时变量赋值成"onetow"
3. 临时字符串与str拼接
4. 将结果赋予str

而如果改成如下这样:

str += "one"
str += "two"

这样就可以避免创建临时字符串,可一定程度加快性能

或者使用如下方式:

str = str + "one" + "two"

但是如果使用下面这种方式:

str = "one" + str + "two"

则无法确定是否有优化。不同的浏览器分配内存方式不一样,IE以外的浏览器,会尝试扩展表达式左端字符串的内存,然后简单的将第二个字符串拷到它的尾部,这样就会创建一个临时字符串存放one{str原本内容},导致性能降低

浏览器优化

很多浏览器会在编译时对连续相加的字符串进行拼接,以此来对运行时优化,比如:

str += "one" + "two"

会被优化成

str += "onetwo"

IE7-中的字符串连接

IE7-中使用++=连接很慢,而使用Array.join()方式则快得多,这也是IE7-浏览器中唯一高效的连接大量字符串的途径

String.concat

这种方法很慢,尽量不要使用

正则表达式优化

正则表达式的工作原理

正则表达式处理经过如下几个步骤:
1. 编译
2. 设置起始位置
3. 匹配每个正则表达式的字元
4. 匹配成功或失败

正则表达式实现中,回溯是基本组成部分。它代价昂贵,且容易失控。回溯是正则表达式性能的唯一因素

正则表达式在匹配时,会在一个有多个分支的地方建立标记点,然后从左到右遍历所有的分支,如果分支符合,就会前进道下一个标记点,如果所有分支都不符合,就会回溯到上一个标记点,尝试上一标记点的其他分支。在尝试上一标记点的其它分支时,这一标记点如果需要,还会全部重新尝试

回溯失控

当一个正则表达式占用浏览器上秒,或者更长时间,很有肯那个就是回溯失控了,原因很有可能是出现了.*?这种非贪婪匹配,导致几乎每一个字符都会被作为标记点进行尝试

此类问题的解决办法是尽可能具体地指出分隔符之间的字符匹配形式,或者使用前瞻表达式

嵌套量词导致性能下降

嵌套量词可能极大的加大分支的数量,比如一个正则表达式A+A+B显然没有AA+B好,比如匹配AAAAAAAAAAAAAAB时,前者产生的分支要比后者多得多

一些建议

  1. 关注如何让匹配更快失败:正则表达式慢往往不是因为成功慢,而是失败慢,因为失败会查找所有的情况
  2. 以简单的,必须的字元开始:这样可以加快失败的匹配,如果这个开始字元都不匹配,后面的标记点就不会被匹配了
  3. 编写两次模板,使他们后面的资源互相排斥:当字元与邻近的子表达式能够重叠匹配时,路径将显著正价,所以需要将其具体化
  4. 减少分之数量,缩小它们的范围:直接使用正则表达式中已有的类(如\w,\d)比使用|要快
  5. 使用非捕获分组:捕获分组花费时间和内存用于记录后向引用,而使用后非捕获性分组则避免这种开销
  6. 将正则表达式分层,先捕获感兴趣的文字,然后再使用新的正则表达式处理
  7. 暴露所需的字元,尽量简单地判断出那些必须的字元
  8. 使用适当的量词,贪婪量词和懒惰量词在匹配同样字符串时过程是不同的,在确保正确的前提下,选择回溯次数更少的量词可以提高性能
  9. 将正则表达式赋给变量,以重用他们。正则表达式创建时,需要对他们进行编译,这个编译也会有额外的开销
  10. 将复杂的正则表达式拆分成简单的片段,避免使用一个表达式做太多的工作,可以通过两个或多个正则表达式来解决

UI线程相关

建议的一次JavaScript执行时间不超过100ms(最好在50ms)以内,可以通过setTimeout和setInterval来将任务进行分解,加入到UI线程中。其实这个思想和JavaScript引擎的垃圾回收器的迭代处理相似

AJAX

AJAX获取数据时,可以使用POST或者GET方法

如果请求不改变服务器状态指示返回数据,应该使用GET。GET请求会被缓存,如果多次提取相同的数据会提高性能

而当URL和请求的参数长度超过2048个字符的时候才使用POST提取数据

多部分XHR(MXHR)

多部分XHR允许使用一个HTTP请求获取多个资源,我们可以将资源打包成一个特定分隔符定界的大字符串,从服务器发送到客户端,JavaScript处理这些大字符串,然后根据它自身的类型和信息解析出每个资源

需要注意的是AJAX不会在浏览器中进行缓存,自然使用MXHR也不会缓存,在一些静态资源上使用这种方式其实并不太好。但是如果每次都确实需要去获取,分多个请求发送会更慢。

IMG灯标

我们可以通过创建一个Image对象,将src设为一个脚本文件的URL,img元素我们并不需要插入到DOM中,这种形式称为IMG灯标

这种方式适用于GET请求,且服务器获得数据后不必返回数据给浏览器的情况

同时我们可以在Image的load事件中监听服务端是否成功接受了数据

数据格式

  • XML:支持广泛但解析效率低,而且相当冗长
  • JSON: 小巧,轻便,解析速度较快
  • JSONP:使用JavaScript解释器进行解析,解析速度极快,数据量只比JSON多一点点(函数名称和括号)
  • HTML:无需解析,数据冗长
  • 自定义格式:自己解析,慢而易出错,数据长度可以很短

其他

    • 不要使用eval以及其类似
    • 使用字面量
    • 避免重复工作,检测浏览器时,保存首次检测结果即可
    • 考虑位操作
    • 使用原声方法,因为他们是C++写的
    • 文件预处理,压缩(gzip)、合并、uglify
    • 尝试CDN

 

 

1. 清理 HTML 文档

HTML,即超文本标记语言,几乎是所有网站的支柱。HTML 为网页带来标题、子标题、列表和其它一些文档结构的格式。在最近更新的 HTML5 中,甚至可以创建图表。

HTML 很容易被网络爬虫识别,因此搜索引擎可以根据网站的内容在一定程度上实时更新。在写 HTML 的时候,你应该尝试让它简洁而有效。此外,在 HTML 文档中引用外部资源的时候也需要遵循一些最佳实践方法。

恰当放置 CSS

Web 设计者喜欢在网页建立起主要的 HTML 骨架之后再来创建样式表。这样一来,网页中的样式表往往会放在 HTML 的后面,接近文档结束的地方。然而推荐的做法是把 CSS 放在 HTML 的上面部分,文档头之内,这可以确保正常的渲染过程。

这个策略不能提高网站的加载速度,但它不会让访问者长时间看着空白屏幕或者无格式的文本(FOUT)等待。如果网页大部分可见元素已经加载出来了,访问者才更有可能等待加载整个页面,从而带来对前端的优化效果。这就是知觉性能

正确放置 Javascript

另一方面,如果将 JavaScript 放置在 head 标签内或 HTML 文档的上部,这会阻塞 HTML 和 CSS 元素的加载过程。这个错误会导致页面加载时间增长,增加用户等待时间,容易让人感到不耐烦而放弃对网站的访问。不过,您可以通过将 JavaScript 属性置于 HTML 底部来避免此问题。

此外,在使用 JavaScript 时,人们通常喜欢用异步脚本加载。这会阻止<script>标签在 HTML 中的呈现过程,如,在文档中间的情况。

虽然对于网页设计师来说, HTML 是最值得使用的工具之一,但它通常要与 CSS 和 JavaScript 一起使用,这可能会导致网页浏览速度减慢。 虽然 CSS 和 JavaScript 有利于网页优化,但使用时也要注意一些问题。使用 CSS 和 JavaScript 时,要避免嵌入代码。因为当您嵌入代码时,要将 CSS 放置在样式标记中,并在脚本标记中使用 JavaScript,这会增加每次刷新网页时必须加载的 HTML 代码量。

绑定文件? 不用担心

在过去,你可能会频繁绑定 CSS 脚本到单个文件,以在 HTML 代码中引用外部文件。在使用 HTTP1.1 协议时,这是一项合理的实践,然而这一协议不再是必需的

感谢 HTTP/2,现在你可以通过使用多路技术将单个 TCP 连接以异步方式收发 HTTP 请求和响应。

105818_lhun_2903254

这意味着你不再需要频繁地将多个脚本绑定到单个文件。

2. 优化 CSS 性能

CSS,即级联样式表,能从 HTML 描述的内容生成专业而又整洁的文件。很多 CSS 需要通过 HTTP 请求来引入(除非使用内联 CSS),所以你要努力去除累赘的 CSS 文件,但要注意保留其重要特征。

如果你的 Banner、插件和布局样式是使用 CSS 保存在不同的文件内,那么,访问者的浏览器每次访问都会加载很多文件。虽然现在 HTTP/2 的存在,减少了这种问题的发生,但是在外部资源加载的情况下,仍会花费较长时间。要了解如何减少 HTTP 请求以大幅度缩减加载时间,请阅读WordPress 性能

此外,不少网站管理员在网页中错误的使用 @import 指令 来引入外部样式表。这是一个过时的方法,它会阻止浏览并行下载。link 标签才是最好的选择,它也能提高网站的前端性能。多说一句,通过 link 标签请求加载的外部样式表不会阻止并行下载。

3.减少外部HTTP请求

在很多情况下,网站的大部分加载时间来自于外部的 Http 请求。外部资源的加载速度随着主机提供商的服务器架构、地点等不同而不同。减少外部请求要做的第一步就是简略地检查网站。研究你网站的每个组成部分,消除任何影响访问者体验不好的成分。这些成分可能是:

  • 不必要的图片
  • 没用的 JavaScript 代码
  • 过多的 css
  • 多余的插件

在你去掉这些多余的成分之后,再对剩下的内容进行整理,如,压缩工具、CDN 服务和预获取(prefetching)等,这些都是管理 HTTP 请求的最佳选择。除此之外,减少DNS路由查找教程会教你如何一步一步的减少外部 HTTP 请求。

4. 压缩 CSS, JS 和 HTML

105909_kgbn_2903254

压缩技术可以从文件中去掉多余的字符。你在编辑器中写代码的时候,会使用缩进和注释,这些方法无疑会让你的代码简洁而且易读,但它们也会在文档中添加多余的字节。

例如,这是一段压缩之前的代码。

 

 

 

 

 

 

 

 

把这段代码压缩后就成了这样。

 

使用压缩工具可以非常简单地把无用的字节从你的 CSS、JS 和 HTML 文件修剪掉。关于压缩的相关信息,可以参阅如何压缩 CSS、JS 和 HTML

5. 使用预先获取

105945_d1e0_2903254

预先获取可以在真正需要之前通过取得必需的资源和相关数据来改善访问用户的浏览体验,主要有3类预先获取:

  • 链接预先获取
  • DNS 预先获取
  • 预先渲染

在你离开当前 web 页面之前,使用预先获取方式,对应每个链接的 URL 地址,CSS,图片和脚本都会被预先获取。这保证了访问者能在最短时间内使用链接在画面间切换。

幸运的是,预先获取很容易实现。根据你想要使用的预先获取形式,你只需在网站 HTML 中的链接属性上增加 rel=”prefetch”,rel=”dns-prefetch”,或者 rel=”prerender” 标记。

6. 使用 CDN 和缓存提高速度

内容分发网络能显著提高网站的速度和性能。使用 CDN 时,您可以将网站的静态内容链接到全球各地的服务器扩展网络。如果您的网站观众遍布全球,这项功能十分有用。 CDN 允许您的网站访问者从最近的服务器加载数据。如果您使用 CDN,您网站内的文件将自动压缩,以便在全球范围内快速分发。

110009_78zj_2903254

CDN 是一种缓存方法,可极大改善资源的分发时间,同时,它还能实现一些其他的缓存技术,如,利用浏览器缓存

合理地设置浏览器缓存,能让浏览器自动存储某些文件,以便加快传输速度。此方法的配置可以直接在源服务器的配置文件中完成。

了解更多有关缓存和不同类型的缓存方法,请参阅缓存定义

7. 压缩文件

110032_qz4n_2903254

虽然许多 CDN 服务可以压缩文件,但如果不使用 CDN,您也可以考虑在源服务器上使用文件压缩方法来改进前端优化。 文件压缩能使网站的内容轻量化,更易于管理。 最常用的文件压缩方法之一是 Gzip。 这是缩小文档、音频文件、PNG图像和等其他大文件的绝佳方法。

Brotli 是一个比较新的文件压缩算法,目前正变得越来越受欢迎。 此开放源代码算法由来自 Google 和其他组织的软件工程师定期更新,现已被证明比其他现有压缩方法更好用。 这种算法的支持目前还比较少,但作为后起之秀指日可待。

了解更多信息,请阅读我们有关 Brotli 压缩的完整文章。

对于那些不懂得前端优化的人来说,图片可能会是一个“网站杀手”。大量的写真集和庞大的高清图片会阻塞网页渲染速度。没有优化的高清图片可能会有几兆字节(mb)。因此适当地对它们进行优化可以改善网页的前端性能。

每个图像文件都包含了一些与纯照片或图片无关的信息。比如 JPEG 图片,它包含了日期、地点、相机型号和一些其他不相关的信息。你可以用一些如 Optimus 的优化工具来删除这些多余的图像数据来精简图像的冗长的加载过程。因为 Optimus 是一个无损的图片压缩工具,它不会影响图像画质,只是压缩图片体积。

另外,如果你想进一步的优化一张图片,你可以使用有损压缩,它会删除一些图片里的数据,因此质量会受损。

110055_nysg_2903254110118_s2nf_2903254

进一步的学习有损和无损压缩之间的区别,请阅读我们完整的教程。

9. 使用轻量级框架

除非你只用现有的编码知识构建网站,不然,你可以尝试使用一个好的前端框架来避免许多不必要的前端优化错误。虽然有一些更大,更知名的框架能提供更多功能和选项,但它们不一定适合你的 Web 项目。

所以说,不仅确定项目所需功能很重要,选择合适的框架也很重要——它要在提供所需功能的同时保持轻量。最近许多框架都使用简洁的 HTML,CSS 和 JavaScript 代码。

以下是几项可以加快读取的轻量级框架:

框架并不能代替网页设计,编程和维护。举个简单的例子,我们假设框架是一个新房子。房子干净整洁,但它是空的。在你添加家具,家电和装饰品时,你有责任确保房子不会变得凌乱。同样地,当您使用了一个框架,您就有责任确保它不会被冗余的代码,大图片和过多的 HTTP 请求破坏。

 

网站的划分一般为二:前端和后台。我们可以理解成后台是用来实现网站的功能的,比如:实现用户注册,用户能够为文章发表评论等等。而前端呢?其实应该是属于功能的表现。并且影响用户访问体验的绝大部分来自前端页面。

而我们建设网站的目的是什么呢?不就是为了让目标人群来访问吗?所以我们可以理解成前端才是真正和用户接触的。除了后台需要在性能上做优化外,其实前端的页面更需要在性能优化上下功夫,只有这样才能给我们的用户带来更好的用户体验。就好像,好多人问,男人在找女朋友的时候是不是只看外表,一些智慧的男人给出了这样的回答:脸蛋和身材决定了我是否想去了解她的思想,思想决定了我是否会一票否决她的脸蛋和身材。同理,网站也是这样,网站前端的用户体验决定了用户是否想要去使用网站的功能,而网站的功能决定了用户是否会一票否决前端体验

不仅仅如此,如果前端优化得好,他不仅可以为企业节约成本,他还能给用户带来更多的用户,因为增强的用户体验。所以说,网站和女人一样,都要内外兼修啊( ̄▽ ̄)”。说了这么多,那么我们应该如何对我们前端的页面进行性能优化呢?

一般说来,web前端指网站业务逻辑之前的部分,包括浏览器加载、网站视图模型、图片服务、CDN服务等,主要优化手段有浏览器访问、使用反向代理才、CDN等。

浏览器访问优化

浏览器请求处理流程如下图:

这里写图片描述

1、减少http请求,合理设置 HTTP缓存

http协议是无状态的应用层协议,意味着每次http请求都需要建立通信链路、进行数据传输,而在服务器端,每个http都需要启动独立的线程去处理。这些通信和服务的开销都很昂贵,减少http请求的数目可有效提高访问性能。

减少http的主要手段是合并CSS、合并javascript、合并图片。将浏览器一次访问需要的javascript和CSS合并成一个文件,这样浏览器就只需要一次请求。图片也可以合并,多张图片合并成一张,如果每张图片都有不同的超链接,可通过CSS偏移响应鼠标点击操作,构造不同的URL。
缓存的力量是强大的,恰当的缓存设置可以大大的减少 HTTP请求。假设某网站首页,当浏览器没有缓存的时候访问一共会发出 78个请求,共 600多 K数据,而当第二次访问即浏览器已缓存之后访问则仅有 10个请求,共 20多 K数据。 (这里需要说明的是,如果直接 F5刷新页面的话效果是不一样的,这种情况下请求数还是一样,不过被缓存资源的请求服务器是 304响应,只有 Header没有Body ,可以节省带宽 )

  怎样才算合理设置 ?原则很简单,能缓存越多越好,能缓存越久越好。例如,很少变化的图片资源可以直接通过 HTTP Header中的Expires设置一个很长的过期头 ;变化不频繁而又可能会变的资源可以使用 Last-Modifed来做请求验证。尽可能的让资源能够在缓存中待得更久。关于 HTTP缓存的具体设置和原理此处就不再详述了。

2、使用浏览器缓存

对一个网站而言,CSS、javascript、logo、图标这些静态资源文件更新的频率都比较低,而这些文件又几乎是每次http请求都需要的,如果将这些文件缓存在浏览器中,可以极好的改善性能。通过设置http头中的cache-control和expires的属性,可设定浏览器缓存,缓存时间可以是数天,甚至是几个月。

在某些时候,静态资源文件变化需要及时应用到客户端浏览器,这种情况,可通过改变文件名实现,即更新javascript文件并不是更新javascript文件内容,而是生成一个新的JS文件并更新HTML文件中的引用。
使用浏览器缓存策略的网站在更新静态资源时,应采用逐量更新的方法,比如需要更新10个图标文件,不宜把10个文件一次全部更新,而是应该一个文件一个文件逐步更新,并有一定的间隔时间,以免用户浏览器忽然大量缓存失效,集中更新缓存,造成服务器负载骤增、网络堵塞的情况。

3、启用压缩

在服务器端对文件进行压缩,在浏览器端对文件解压缩,可有效减少通信传输的数据量。如果可以的话,尽可能的将外部的脚本、样式进行合并,多个合为一个。文本文件的压缩效率可达到80%以上,因此HTML、CSS、javascript文件启用GZip压缩可达到较好的效果。但是压缩对服务器和浏览器产生一定的压力,在通信带宽良好,而服务器资源不足的情况下要权衡考虑。

4、 CSS Sprites

  合并 CSS图片,减少请求数的又一个好办法。

5、Lazy Load Images(自己对这一块的内容还是不了解)

  这条策略实际上并不一定能减少 HTTP请求数,但是却能在某些条件下或者页面刚加载时减少 HTTP请求数。对于图片而言,在页面刚加载的时候可以只加载第一屏,当用户继续往后滚屏的时候才加载后续的图片。这样一来,假如用户只对第一屏的内容感兴趣时,那剩余的图片请求就都节省了。

6、CSS放在页面最上部,javascript放在页面最下面

浏览器会在下载完成全部CSS之后才对整个页面进行渲染,因此最好的做法是将CSS放在页面最上面,让浏览器尽快下载CSS。如果将 CSS放在其他地方比如 BODY中,则浏览器有可能还未下载和解析到 CSS就已经开始渲染页面了,这就导致页面由无 CSS状态跳转到 CSS状态,用户体验比较糟糕,所以可以考虑将CSS放在HEAD中。

Javascript则相反,浏览器在加载javascript后立即执行,有可能会阻塞整个页面,造成页面显示缓慢,因此javascript最好放在页面最下面。但如果页面解析时就需要用到javascript,这时放到底部就不合适了。

Lazy Load Javascript(只有在需要加载的时候加载,在一般情况下并不加载信息内容。)随着 Javascript框架的流行,越来越多的站点也使用起了框架。不过,一个框架往往包括了很多的功能实现,这些功能并不是每一个页面都需要的,如果下载了不需要的脚本则算得上是一种资源浪费 -既浪费了带宽又浪费了执行花费的时间。目前的做法大概有两种,一种是为那些流量特别大的页面专门定制一个专用的 mini版框架,另一种则是 Lazy Load。

7、异步请求 Callback(就是将一些行为样式提取出来,慢慢的加载信息的内容)

  
在某些页面中可能存在这样一种需求,需要使用 script标签来异步的请求数据。类似:
 

1
2
3
4
5
6
7
8
9
<code class="hljs javascript"> Javascript:
    /*Callback 函数*/
    function myCallback(info){
        //do something here
    }
 
 HTML:
  Callback返回的内容 :
   myCallback('Hello world!');</code>

像以上这种方式直接在页面上写 <script> 对页面的性能也是有影响的,即增加了页面首次加载的负担,推迟了 DOMLoaded和window.onload 事件的触发时机。如果时效性允许的话,可以考虑在 DOMLoaded事件触发的时候加载,或者使用 setTimeout方式来灵活的控制加载的时机。

8、减少cookie传输

一方面,cookie包含在每次请求和响应中,太大的cookie会严重影响数据传输,因此哪些数据需要写入cookie需要慎重考虑,尽量减少cookie中传输的数据量。另一方面,对于某些静态资源的访问,如CSS、script等,发送cookie没有意义,可以考虑静态资源使用独立域名访问,避免请求静态资源时发送cookie,减少cookie传输次数。

9、Javascript代码优化

(1). DOM  

  a. HTML Collection(HTML收集器,返回的是一个数组内容信息) 
  在脚本中 document.images、document.forms 、getElementsByTagName()返回的都是 HTMLCollection类型的集合,在平时使用的时候大多将它作为数组来使用,因为它有 length属性,也可以使用索引访问每一个元素。不过在访问性能上则比数组要差很多,原因是这个集合并不是一个静态的结果,它表示的仅仅是一个特定的查询,每次访问该集合时都会重新执行这个查询从而更新查询结果。所谓的 “访问集合” 包括读取集合的 length属性、访问集合中的元素。 
  因此,当你需要遍历 HTML Collection的时候,尽量将它转为数组后再访问,以提高性能。即使不转换为数组,也请尽可能少的访问它,例如在遍历的时候可以将 length属性、成员保存到局部变量后再使用局部变量。   
  b. Reflow & Repaint   
  除了上面一点之外, DOM操作还需要考虑浏览器的 Reflow和Repaint ,因为这些都是需要消耗资源的。

(2). 慎用 with  

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

(3). 避免使用 eval和 Function

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

(4). 减少作用域链查找

  前文谈到了作用域链查找问题,这一点在循环中是尤其需要注意的问题。如果在循环中需要访问非本作用域下的变量时请在遍历之前用局部变量缓存该变量,并在遍历结束后再重写那个变量,这一点对全局变量尤其重要,因为全局变量处于作用域链的最顶端,访问时的查找次数是最多的。 
   
  低效率的写法:

1
2
3
4
5
6
7
8
9
<code class=" hljs javascript">// 全局变量
var globalVar = 1;
function myCallback(info){
    for( var i = 100000; i--;){
        //每次访问 globalVar 都需要查找到作用域链最顶端,本例中需要访问 100000 次
        globalVar += i;
    }
}
</code>

更高效的写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
<code class=" hljs javascript">// 全局变量
var globalVar = 1;
function myCallback(info){
    //局部变量缓存全局变量
    var localVar = globalVar;
    for( var i = 100000; i--;){
    //访问局部变量是最快的
    localVar += i;
    }
    //本例中只需要访问 2次全局变量
    在函数中只需要将 globalVar中内容的值赋给localVar 中
    globalVar = localVar;
}</code>

此外,要减少作用域链查找还应该减少闭包的使用。

(5). 数据访问

  Javascript中的数据访问包括直接量 (字符串、正则表达式 )、变量、对象属性以及数组,其中对直接量和局部变量的访问是最快的,对对象属性以及数组的访问需要更大的开销。当出现以下情况时,建议将数据放入局部变量: 
   
  a. 对任何对象属性的访问超过 1次 
  b. 对任何数组成员的访问次数超过 1次 
  另外,还应当尽可能的减少对对象以及数组深度查找。

(6). 字符串拼接

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

10、CSS选择符优化

  在大多数人的观念中,都觉得浏览器对 CSS选择符的解析式从左往右进行的,例如 
#toc A { color: #444; }这样一个选择符,如果是从右往左解析则效率会很高,因为第一个 ID选择基本上就把查找的范围限定了,但实际上浏览器对选择符的解析是从右往左进行的。如上面的选择符,浏览器必须遍历查找每一个 A标签的祖先节点,效率并不像之前想象的那样高。根据浏览器的这一行为特点,在写选择符的时候需要注意很多事项,有兴趣的童鞋可以去了解一下。

CDN加速

CDN(content distribute network,内容分发网络)的本质仍然是一个缓存,而且将数据缓存在离用户最近的地方,使用户以最快速度获取数据,即所谓网络访问第一跳,如下图。

这里写图片描述vcWxvqGivrLMrM340rO1yKOstavKx9Xi0KnOxLz+t8POysa1tsi63Ljfo6y9q8bku7q05tTaQ0ROv8m8q7TzuMTJxs340rO1xLTyv6rL2bbIoaM8L3A+DQoNCg0KDQo8aDIgaWQ9"反向代理">反向代理

传统代理服务器位于浏览器一侧,代理浏览器将http请求发送到互联网上,而反向代理服务器位于网站机房一侧,代理网站web服务器接收http请求。如下图所示:

这里写图片描述网站安全的作用,来自互联网的访问请求必须经过代理服务器,相当于web服务器和可能的网络攻击之间建立了一个屏障。

除了安全功能代理服务器也可以通过配置缓存功能加速web请求。当用户第一次访问静态内容的时候,静态内容就被缓存在反向代理服务器上,这样当其他用户访问该静态内容的时候,就可以直接从反向代理服务器返回,加速web请求响应速度,减轻web服务器负载压力。事实上,有些网站会把动态内容也缓存在代理服务器上,比如维基百科及某些博客论坛网站,把热门词条、帖子、博客缓存在反向代理服务器上加速用户访问速度,当这些动态内容有变化时,通过内部通知机制通知反向代理缓存失效,反向代理会重新加载最新的动态内容再次缓存起来。

此外,反向代理也可以实现负载均衡的功能,而通过负载均衡构建的应用集群可以提高系统总体处理能力,进而改善网站高并发情况下的性能。

posted @ 2017-12-01 10:25  乐逍遥_1992  阅读(399)  评论(0编辑  收藏  举报