1、网站性能,刻不容缓!三大因素:CSS、IMAGES、JS,主讲人:侯彪,CSS也有性能开销华丽的外衣-IMAGESJS优化几则杂谈-DOM的加载也有SEO的技巧,CSS也有性能开销,网站性能一说让多数的人第一时间想起的是javascript,但对于一个WEB来说同样重要的CSS呢?你会考虑到他的性能开销吗?ul#my_list licolor:#f00理解一下CSS选择符的概念,把样式表放在标签中以提升页面的逐步渲染速度。不要在IE中使用CSS Expression,因为他会被执行到很多次,你想知道是多少吗?好的,以万为单位,甚至可以搞死你的IE!避免或杜绝使用行内样式,因为这会增加下载页面的
2、大小,并搞乱你的CSS权重。,在前辈们的指点下,关于CSS的最佳性能实践大致总结了如下几点:,CSS也有性能开销,怎么样这些很容易理解吧。不过以上几点已经不能满足我们追求性能的野心了。于是更多的CSS性能规则开始展现他们的力量!,CSS也有性能开销,ID选择符 #tocmargin-left:20px类选择符 .chapterfont-weight:bold类型选择符 atext-decoration:none相邻兄弟选择符 h1 + #tocmargin-top:40px子选择 #toc lifont-weight:bold后代选择符 #toc acolor:#333通配选择符 *font-
3、family:Arial属性选择符 href=#indexfont-style:italic伪类和伪元素 a:hovertext:decoration:underline,CSS也有性能开销,最右边优先!#toc lifont-weight:bold浏览器并不是从左到右的顺序来解析CSS选择符,所以,看看上面这个本以为很高效的选择符,浏览器先要遍历DOM中所有的li标签,并确定他的父级元素为#toc。#toc acolor:#333看看这个 岂不是更糟糕!,CSS也有性能开销,知道了选择符的匹配顺序,我们便有了如下的更具体的优化法则!避免使用通配规则 *不要限定ID选择符 div#wrap不要
4、限定类选择符 li.ele to .list_ele让规则越具体越好 ol li a to .list_link避免使用后代选择符 ul li避免使用子选择符 ul li a质疑子选择符的所有用途 尽可能使用具体的类依靠继承,华丽的外衣-IMAGES,搞清楚你要处理的图片类型图形 VS 图像(照片),华丽的外衣-IMAGES,图形:一般来说,网站的LOGO,草图,手稿图和部分ICON图标都属于图形,这些通常由连续的线条或其他尖锐的颜色过渡,颜色数量相对较少。,华丽的外衣-IMAGES,图像(照片):百万数量级的颜色,包含平滑的颜色过渡和渐变,比如网站的KV轮播图,数码相机拍出来的产品图。,华丽
5、的外衣-IMAGES,华丽的外衣-IMAGES,华丽的外衣-IMAGES,华丽的外衣-IMAGES,三种不同的图片格式JPEG GIF PNG(此处只考虑PNG8)相关的系统知识这里不再阐述,有兴趣的自己上百度GOOGLE一下以下介绍重点的属性!,华丽的外衣-IMAGES,JPEG:有损:它是一种有损的格式,用户可以设置自定义质量级别,这个级别决定了有多少图像信息会被抛弃。级别从0-100,但即便是100也同样会有一定程度的质量损耗。当我们要对一个图像反复编辑时,比如随时都会往里边继续加新图的CSS精灵,最好使用无损的格式来保存中间结果,然后在最终结果的时候存为JPEG格式,否则你将在每次保存
6、为JPEG的时候都损耗一些质量。,华丽的外衣-IMAGES,JPEG:但也有少数操作是无损的:以90的倍数进行旋转的时候裁剪水平翻转或垂直翻转从标准模式切换到渐进模式 反之亦然编辑图像的元数据此格式不支持透明和动画,华丽的外衣-IMAGES,GIF:GIF是无损的,也就是说你可以打开任意一个GIF文件,做一些修改,保存关闭时不会损失任何质量。透明,允许一个二进制透明,每个像素要么完全透明 要么完全不透明 这就意味着他不支持alpha透明。取而代之的是,调色板中的某个颜色可以被标记为表示透明,而透明像素则会被分配为这个颜色值,所以,如果你为GIF设置了透明像素,那么就会消耗一个调色板条目。,华丽
7、的外衣-IMAGES,GIF:动画,GIF支持“逐帧动画”。应用最广的当属LOADING256色限制。逐行扫描,当生成一个GIF文件时,会使用一个压缩算法LZW来减小文件的大小,压缩GIF文件时,会从上到下一行一行的对像素进行扫描,这种情况下,当图像在水平方向有很多重复颜色时,可以获得更好的压缩效果。如下图:,华丽的外衣-IMAGES,1.77 KB,华丽的外衣-IMAGES,3.84 KB,华丽的外衣-IMAGES,PNG:为了弥补GIF文件的缺点,并规避LZW算法的专利问题(2004年已经过了保护期)PNG文件诞生!你可以使用PNG8代替不需要动画的GIF并用PNG24或32来代替JPEG
8、!PNG是无损的图像格式。多次编辑不会降低其质量,这使得采用PNG格式来存储中间格式非常合适。比如我们网站的品牌旗舰馆的LOGO就是用PNG8来保存的,每次有新品牌的时候直接在PNG上操作,存储完毕不会有任何的消耗,如果是JPEG那保存几次就完了。如下图:,华丽的外衣-IMAGES,华丽的外衣-IMAGES,今天又添加了新的品牌 使用PNG无损直接在原图上进行添加,华丽的外衣-IMAGES,PNG:透明。PNG8支持和GIF一样的二进制透明,PNG24支持alpha透明逐行扫描。和GIF格式一样,但是压缩比更高!如下图,华丽的外衣-IMAGES,2.14 KB,华丽的外衣-IMAGES,就图像
9、格式而言,GIF通常用于显示图形,JPEG更适合显示图片,而PNG则两者都适合。和GIF相比:用PNG来代替GIF(不带动画)会更好,文件大小也更小,并且具备除动画以外的所有GIF特性,所以应该尽量以PNG8代替GIF。有一个例外就是颜色数很少的小图像,这时GIF的压缩比可能更高一些,但是这样的小图片应该被放在了CSS精灵中,因为HTTP的请求增加已经远远超过节省的那点带宽,而且用PNG来保存CSS精灵可以获得更高的压缩比。,华丽的外衣-IMAGES,和JPEG相比:当图像中的颜色超过256时,需要使用真彩色图像格式-PNG24或JPEG。JPEG的压缩比更高,而且一般来说JPEG也是照片存储
10、的实际标准。但由于JPEG是有损的而且在清晰的颜色过渡周围会有大色块,因此以下2种情况更适合PNG,华丽的外衣-IMAGES,和JPEG相比:当图像的颜色略超过256种,可以在不损耗任何可见质量的前提下,将图像转换为PNG8,虽然有时存储为PNG8会使得颜色数量减少,但是有时就算本身有1000种颜色,当你存储为256种颜色的时候也看不出什么特别明显的变化。如下图,华丽的外衣-IMAGES,JPEG 80品质190 KB,华丽的外衣-IMAGES,JPEG 100品质351 KB,华丽的外衣-IMAGES,PNG8 114 KB,华丽的外衣-IMAGES,和JPEG相比:当图片很容易因为压缩产生
11、大色块并且大色块变得不可接受时。如下图,华丽的外衣-IMAGES,JS优化几则,4种数据存储位置局部变量最快循环JS与DOM那扯不断理还乱的关系,JS优化几则,4种数据存储位置直接量 数字 字符串 boolen 对象 数组 函数 正则 null undefined变量 var.数组元素 数字为索引对象成员 字符串为索引直接量和局部变量的访问速度快于后边2个FF3是个例外,他优化了数组项的存取,但是通常的建议是,如果在乎速度 那么尽量使用直接量或局部变量 减少数组项和对象成员的使用。,JS优化几则,局部变量最快每个JS函数都是表示为一个对象,更确切的说是function对象的一个实例,funct
12、ion对象同其他对象一样,拥有可以编程访问的属性,和一些列不能通过代码仅供JS引擎存取的内部属性。其中一个内部属性是【scope】内部属性【scope】包含了一个 函数被创建的作用域中 对象的集合,这个集合被称为函数的作用域链,它决定哪些数据能被函数访问。当一个函数创建后,它的作用域链会被 创建此函数的作用域中 可访问的数据对象 所填充。function add(a,b) var sum = a + b; return sum; 函数add创建时,他的作用域链中填入了一个单独的可变对象,这个全局对象表示所有全局范围定义的变量。包含WINDOW,DOCUMENT,NAVIGATOR等等全局对象。
13、,JS优化几则,局部变量最快下面开始执行 var num = add(10,20);执行此函数会创建一个称为 运行期上下文 的内部对象。一个运行期上下文定义了一个函数执行时的环境。函数每次执行时对应的执行期上下文都是独一无二的,所以多次调用同一个函数就会导致创建多个运行期上下文。当函数执行完毕,对应的运行期上下文就被销毁。每个运行期上下文都有自己的作用域链,用于标识符解析。当运行期上下文被创建时,它的作用域链初始化为当前运行函数的scope属性中所包含的对象。这些值按照他们出现在函数中的顺序,被复制到运行期上下文的作用域链中。这个过程一旦OK,一个被称为 活动对象 的新对象就为运行期上下文创建
14、好了。活动对象作为函数运行期的可变对象,包含了所有局部变量,命名参数,参数集合 以及 this。然后此对象被推入作用域链的前端。当运行期上下文被销毁,活动对象也随之销毁。,JS优化几则,局部变量最快在函数执行过程中,每遇到一个变量,都会经历一次标识符解析过程以决定从哪里获取或存储数据。该过程搜索运行期上下文的作用域链,查找同名的表示符。搜索过程从作用域链顶部开始,也就是当前运行函数的活动对象。如果找到了,就使用这个标识符对应的变量;如果没找到,继续搜索作用域链中的下一个对象。搜索过程会持续进行,直到标识符被找到,或者没有可用于搜索的对象为止,这种情况下标识符被认定是为定义的。is not de
15、fined 。函数执行过程中,每个标识符都要经历这样的搜索过程。正是这个搜索过程影响着他的性能!如果名字相同的2个变量存在于作用域链的不同部分,那么标识符就是遍历作用域链时最先找到的那个,也可以说,第一个找到的遮蔽了第二个。,JS优化几则,局部变量最快在运行期上下文的作用域链中,一个标识符所在的位置越深,它的读写速度也就越慢。SO 函数中读取局部变量总是最快的,而全局变量是最慢的,因为全局的总是存在于运行期上下文作用域链的最末端!一个好的经验法则就是,如果一个跨作用域的值在函数中被引用一次以上,那么就把它存储到局部变量里。临时改变作用域链 with与try catch 但是不推荐使用,除非你的
16、项目实在有需求。,JS优化几则,局部变量最快说了以上这么多 就是想总结2个字!那就是,JS优化几则,局部变量最快,JS优化几则,循环着重介绍for while循环先来了解下2个循环的表现看下边代码及运行时间,JS优化几则,循环,JS优化几则,循环总结就是 在乎先后顺序 用for + 不在乎的话用for -,JS优化几则,循环其他优化法则减少迭代的工作量,很明显,如果一次循环迭代要花很长时间去执行,那么多次循环需要花更多时间,一个提升循环整体速度的好方法是限制循环中耗时操作的数量。for(var i = 0; i items.length; i+) /do something; 上述循环中看似很
17、正常,但是有很严重的性能问题!每次运行循环体时都会产生如下操作:一次控制条件中的属性查找(items.length)一次控制条件中的数值大小比较(i items.length)一次控制条件结果是否为true的比较(i items.length = true)一次自增操作,JS优化几则,循环前面我们说过 读取局部变量是最快的,这个循环体的每次运行运行都要查找items.length 这样做很耗时,这样做很耗时,因为这个值在循环过程中是不变的仅供比较,所以我们把它传递到局部变量会更快也更合情合理。如下:for(var i = 0, l = items.length; i l; i+) /do so
18、mething 重写后的循环只在循环运行前对长度进行一次属性查找,这使得控制条件可直接读取局部变量,所以速度更快。快多少?自己测试,肯定高于你的预期!,JS优化几则,循环另一个方法就是在 顺序不是很重要的情况下 可以使用倒序循环 比如循环为每个DOM元素addEventListener。倒序循环是编程语言中一种通用的优化方法。for(var i = items.length; i-;) /do something 现在每个控制条件只是简单的与0比较。控制条件与true值比较,任何非零数会自动转换为true,而零值等同于false。实际上控制条件从2次比较(迭代数少于总数吗?是否为true?)减
19、少到1次(它是处儿吗?)。这样就更进一步提高了循环速度。通过这2个很不起眼的小办法就可以看出 循环速度比最初的提高了50%以上!对比原始版本 现在每次运行循环体时只需要如下几步一次控制条件的比较(i = true)一次减法操作(i-),JS优化几则,JS与DOM那扯不断理还乱的关系用JS脚本来操作DOM的代价很昂贵,它是富WEB应用中最常见的性能瓶颈。关于JS与DOM的关系一说,微软有个很好的比喻,把DOM与JS各自想象为一个岛屿,他们之间用高速桥连接,JS每次访问DOM都要途径这座桥,并交纳过路费,访问DOM的次数越多,付出的代价越昂贵,因此,要尽可能的减少过桥的次数。访问DOM的代价已经这
20、么昂贵 那就更不用说修改DOM了,但是我们以往的以后的项目又不可完全避免产生这样的过路费,所以有必要掌握以下的常识!,JS优化几则,1.避免在循环体中对DOM元素反复操作! for( var i = 0; i 10; i+) document.getElementById(a).innerHTML += i; 这段简单的代码有很严重的性能问题!每次循环该元素都被访问2次,1次读取innerHTML内容,1次重写它。 转变! var content = ; for(var i = 0; i 10; i+) content += i; document.getElementById(a).inne
21、rHTML = content; 插一句 innerHTML对比document.createElement 性能相差无几,在最新版的webkit浏览器中 innerHTML略慢 其他情况下建议用innerHTML,JS优化几则,2.重绘重排 重绘重排都是代价昂贵的操作,他们会导致WEB应用程序和UI反应迟钝,因此应该尽量减少这类过程的发生。 当DOM的变化影响了元素的几何属性-widht height border,浏览器需要重新计算元素的几何属性,同样其他元素的几何属性和位置也会因此受到影响。浏览器会使渲染树中受到影响的部分失效,并重新构造渲染树。这个过程就是重排(Reflow)。完成重排
22、后浏览器会重新绘制受影响的部分到屏幕中,该过程为重绘(Repaint) 并不是所有的DOM变化都会影响几何属性。例如改变一个元素的background-color,这种情况下只会发生一次重绘而不需要重排。因为元素的布局并没有改变。 以下情况会触发重排:,JS优化几则,1.添加删除可见的DOM元素 2.元素位置改变 3.元素尺寸改变 border-width margin padding width height 4.内容改变,文字行数变化 图片大小尺寸变化 5.页面渲染器初始化 6.浏览器窗口尺寸改变resize(function(),JS优化几则,3.如何最大程度的减少重排和重绘 首先。使元
23、素脱离文档流 其次。对其应用多重改变 最后。把元素带回文档中 该过程会触发两次重排 第一步和第三步,如果你忽略这2个步骤,那么你在第二步里的每一个修改操作触发一次重排。 三种方法可以使DOM脱离文档 1.隐藏元素,应用修改,重新显示 display:none display:block 2.使用文档片段,在当前DOM之外构建一个子树,再把它拷贝回文档。 document.createDocumentFragment() 3.将原始元素拷贝到一个脱离文档的节点中,修改副本,完成后再替换原始元素。 cloneNode(true) replaceChild(clone,old),JS优化几则,当然JS的深度优化绝对要比我所介绍的这几个多的太多了。更多的优化经验也要视项目的实际情况,当然了,既然我们已经知道了一些可以优化并且值得优化的东西那就应该在书写的过程中严格要求自己,避免在代码一行一行添加的时候为自己的日后维护带来麻烦。,THANK YOU!,