1、改善用户体验 Web前端优化策略总结,刘锐 2015年7月,爱卡技术部 用车服务组,为什么要做优化?,一个网页从开始加载到完全载入 最长你能“忍”多久?,前端优化的价值,在互联网行业,要成为受人尊敬的互联网企业,产品的访问速度是一块基石,用户放弃一个产品只需要1秒,1秒关掉网页,1秒打开竞争对手的网站。加载时间每增加1秒,那都意味着金钱和用户的流失。,需要优化的部分,减少HTTP请求,一个正常HTTP请求的流程简述:如在浏览器中输入““并按下回车,浏览器再与这个URL指向的服务器建立连接,然后浏览器才能向服务器发送请求信息,服务器在接受到请求的信息后再返回相应的信息,浏览器接收到来自服务器的应
2、答信息后,对这些数据解释执行。 而当我们请求的网页文件中有很多图片、CSS、JS等信息时,将会频繁的与服务器建立连接,与释放连接,这必定会造成资源的浪费,且每个HTTP请求都会对服务器和浏览器产生性能负担。 网速相同的条件下,下载一个100KB的图片比下载两个50KB的图片要快。 80%的终端用户请求时间花费在前端。这些时间的大部分停滞在下载页面中的组件:图片,样式表,scripts,Flash等等。反过来减少组件的数量就可以减少页面的HTTP请求数量。这是加速页面的关键。,HTTP请求过程,减少HTTP请求 合并css、js,合并到尽可能少的文件可以减少网络往返时间,以及下载其他资源的延迟时
3、间。 举例:合并方法:手动、Minify、 Nginx concat模块,合并后,减少HTTP请求 内联样式表,作为大型网站来说,首页使用内联样式表,这样可以减少http请求数的同时,也可以防止裸奔。当然其他页面需要使用外联样式表,这样才可以方便维护。因为作为大型网站来说,首页访问量是非常的大的。,减少HTTP请求 图片地图,把一张图片分成多个区域,每个区域指向不同的URL地址。例如,将一幅中国地图的图像按照省市划分为若干个区域,这些区域就被称为热点,单击热点区域,就可以连接到与相应的省市有关的页面,这就是图片地图。 如果导航栏或者其他超链接中使用多个图片,将他们转换为图片地图是加速页面的最简
4、单的方式。如:以上效果,使用三个分开的图片来实现,如果使用图片地图则可以更有效率,因为三个http请求减少为只有一个http请求。响应时间将会降低,因为减少了http开销。,减少HTTP请求 CSS Sprites,CSS Sprites是减少图片请求数量的首选方式。把背景图片都整合到一张图片中,然后用CSS的background-image和background-position属性来定位要显示的部分。 如:,减少HTTP请求 CSS Sprites,如何生成css sprites? Ps、Compass、cssgaga【http:/ http:/.spritegen.website-per
5、formance.org/ http:/ 当你输入一个网址时,计算机并不知道它是什么,必须通过一种转换机制,转换成IP,计算机可以理解的地址。在进行DNS查询过程中,浏览器是得不到任何资源的;如果有很多DNS查找过程,必将影响我们网站的性能。 浏览器缓存机制,IE默认情况下缓存时间30分钟,Firefox、Chrome1分钟 由于DNS查找是需要时间的,所以应该尽可能地减少DNS查找的次数 减少DNS查找次数,最理想的方法就是将所有的内容资源都放在同一个域下面,这样访问整个网站就只需要进行一次DNS查找,这样可以提高性能;这么做会带来另外一个问题,就是由于这些资源都在同一个域,而HTTP 中推
6、荐客户端针对每个域只有一定数量的并行度(它的建议是2),那么就会出现下载资源时的排队现象,这样就会降低性能。 所以,建议在一个网站里面使用至少2个域,但不多于4个域来提供资源。,各浏览器并行连接数(同域名),DNS查找过程,避免重定向,重定向,就是将原始请求转向到了其它位置 网页被移到一个新地址,如果不做重定向,则用户收藏夹或搜索引擎数据库中旧地址只能让访问客户得到一个404页面错误信息,访问流量白白丧失。开发时应多考虑,能否延用原先的URL规则!,当客户端遇到这种情况的时候,用户只能等待客户端再次发送请求,有的网站甚至会一直跳n次,跳到他想带你去的地方当然在这个时候用户看不到任何页面内容,只
7、有浏览器的进度条一直在刷新。 用户多了一次或者几次获取新地址的过程,必然增加了请求,违背了我们减少HTTP请求的原则!,使AJAX可缓存,前端代码充斥着大量的Ajax请求,如果对于Ajax请求可以使用浏览器缓存,那么可以显著地减少网络请求,提高程序响应速度。 AJAX请求有两种方式GET、POST;AJAX请求使用GET方法。GET可以缓存,POST不可以缓存;两者差异:http:/ 为了改善性能,优化Ajax响应很重要。最重要的方法是利用响应缓存来改进Ajax的性能,如增加Expires头,Cache-Control头等。下面列举了另外一些方法: 使用Gzip模块 减少DNS查询 压缩Jav
8、aScript 避免重定向 配置ETags头,延迟加载、预加载,延迟加载 所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。如图片、js 在包含很多大图片长页面中,在浏览器可视区域外的图片不会被载入, 直到用户将页面滚动到它们所在的位置; 页面中一些点击才触发的特效,如弹出百度地图,用户登录注册窗口; 延迟加载图片的 jQuery 插件:jquery.lazyload.js、Echo.js 预加载 预加载其实就是在真正开始使用数据之前,先异步把数据加载好,等到需要使用时,就可以直接使用之前加载好的数据。这时,由于数据已经加载完成,而不用等待漫长的加载过程,所以程序的速度得到一个明
9、显的提升。,减少DOM,网页中元素过多对网页的加载和脚本的执行都是沉重的负担,100个元素和1000个元素在加载速度上会有很大差别。想知道你的网页中有多少元素,通过在浏览器中的一条简单命令就可以算出,document.getElementsByTagName(*).length,如何减少DOM,用script存放html代码 用textarea 存放 html 代码,爱卡首页热门车选项卡模块,被textarea包裹,鼠标滑动时,填充数据。,根据域名划分内容、最小化iframe数量、删除死链,根据域名划分内容 浏览器一般对同一个域的下载连接数有所限制,按照域名划分下载内容可以让浏览器增大并行下载
10、链接,但是要注意控制域名使用2-4个之间,不然dns查询也是个问题。 一般网站规划静态资源放在类似 。这样做有一个好处在静态的域名上避免使用cookie。后面cookie的规则介绍。 最小化iframe数量 iframe 提供了一个简单的方式把一个网站的内容嵌入到另一个网站中。但我们需要慎重的使用iframe。 Iframe内容为空也消耗加载时间 iframe会阻塞主页面的onload事件,onload 事件需要在所有 iframe 加载完毕后(包含里面的元素)才会触发 占用连接数,阻塞主页资源加载 删除死链 删除“死链接”,或返回404错误的请求,避免浪费请求。,将样式表放在页面的顶部,为什
11、么应用程序中出现进度条,因为它们让用户在安装或者使用的过程中知道系统没有崩溃,提升用户体验! 在很多浏览器(包括IE)中,把样式表放在HTML文档底部都会阻止页面渲染,导致空白 将CSS放到head中,主要是浏览器解析文件的时候是从上到下来进行的,提高渲染性能,避免页面空白或重绘,将样式表放在页面的顶部,在这里,HTML页面就是进度显示器。当浏览器逐步地加载页面时,页头、导航栏、顶端logo等,所有这些都会为等待页面的用户提供视觉反馈。让用户感觉到网站没有挂,改善用户体验!,避免使用CSS表达式,CSS表达式可以动态的设置CSS属性,CSS表达式的问题在于它被重新计算的次数远比我们想象的要多,
12、不仅在网页绘制或大小改变时计算,即使我们滚动屏幕或者移动鼠标的时候也在计算,因此我们还是尽量避免使用它来防止使用不当而造成的性能损耗。,用代替import、避免使用Filters,用代替import 在外部样式表使用CSS import会在网页加载中增加延迟。 CSS import允许把样式表导入到其他样式表里,当CSS import 在外部样式表中使用,浏览器是不能并行下载这些样式表的,这将增加网络请求的来回时间。例如,如果first.css包含如下内容:import url(“second.css“),浏览器必须先下载、分析和执行first.css,再去下载second.css。 避免使用
13、Filters CSS滤镜,不需要你使用任何做图软件,用纯CSS就会生成多种的滤镜效果,比如模糊效果,透明效果,色彩反差调整,色彩反相等等。滤镜的使用会导致图片在下载的时候阻塞网页绘制,另外使用这种滤镜会导致内存使用量的问题。,将脚本放到页面的底部,在页面的元素中包含js文件,意味着必须等到全部js代码都被下载、解析和执行完成以后,才能呈现页面的内容(浏览器在遇到标签时才开始呈现内容)。 对于那些需要很多js的页面来说或者页面出现死循环,这无疑会导致浏览器在呈现页面时出现明显的延迟,而延迟期间浏览器的窗口将是一片空白。,缩小JavaScript和CSS、删除重复的脚本,缩小JavaScript
14、和CSS 缩减资源大小是指删除不必要的字节(例如,注释、多余的空格、换行符和缩进)。压缩CSS和JavaScript,这样可以减少字节数,提高下载、解析和执行的速度。 统计表明精简后的文件大小平均减少了21%,即使在应用Gzip的文件也会减少5%。 对比下压缩过和未压缩的jquery: Js压缩工具:JSMin、Packer、 webkaka;Css压缩工具:CssCompressor、webkaka 删除重复的脚本 重复的脚本不仅浪费浏览器的下载时间,而且浪费解析和执行时间。,最小化DOM的访问,对DOM操作的代价是高昂的,这在网页应用中的通常是一个性能瓶颈。 天生就慢。在高性能JavaSc
15、ript中这么比喻:“把DOM看成一个岛屿,把JavaScript(ECMAScript)看成另一个岛屿,两者之间以一座收费桥连接”。所以每次访问DOM都会教一个过桥费,而访问的次数越多,交的费用也就越多。所以一般建议尽量减少过桥次数。 解决办法: 修改和访问DOM元素会造成页面的重绘和回流,循环对DOM操作更是罪恶的行为。所以请合理的使用JavaScript变量储存内容,考虑大量DOM元素中循环的性能开销,在循环结束时一次性写入。 减少对DOM元素的查询和修改,查询时可将其赋值给局部变量。,最小化DOM的访问,这个函数循环修改页面元素的内容。这段代码的问题在于,每次循环迭代,该元素都被访问两
16、次:一次读取innerHTML属性,另外一次重写它。访问DOM的次数越多,代码运行速度越慢。因此,在有其他方案可以代替的时候,我们要尽量减少访问DOM的次数。,优化图片大小、使用CSS Sprites优化图片,优化图片大小 不管是在网站页面还是在移动应用,图片都是至关重要的。它们可以让你的页面更好看,更引人注目。但是,高质量和漂亮的图片常常会很大,它们会让页面加载变慢并消耗更多带宽。 图像优化工具: Webkaka 、Online Image Optimizer、 Image Optimizer、 TinyPNG、Riot等 使用Css Sprites优化图片 【减少HTTP请求】中已提到,不
17、要在HTML中使用缩放图片、Favicon.ico压缩缓存,不要在HTML中使用缩放图片 不要通过图片缩放来适应页面,如果你需要小图片,就直接使用小图片吧。 比如:如果你打算使用100x100px的图片,不要试图使用一个500x500px的大图片来缩放,违背了【优化图片大小】这一原则! Favicon.ico压缩缓存 网站图标文件favicon.ico,不管你服务器有还是没有,浏览器都会去尝试请求这个图标。所以我们要确保这个图标存在,文件尽量小,最好小于1k,设置一个长的过期时间!,避免空的图片src,空的图片src仍然会使浏览器发送请求到服务器,这样完全是浪费时间,而且浪费服务器的资源。尤其
18、是你的网站每天被很多人访问的时候,这种空请求造成的伤害不容忽略。 所以注意我们的网页中是否存在这样的代码 HTML JavaScript var img = new Image(); img.src = “;,使用CDN,CDN的全称是Content Delivery Network,即内容分发网络。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最
19、近的服务节点上。其目的是使用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度。 在离你最近地方,放置一台副本服务器,让你能够以最近的距离、最快的速度获取内容;本质是一个缓存,将数据缓存在离用户最近的地方。 为什么要用CDN? 网站服务器在北京,运营商是电信,在山西的联通用户访问该网站时,因为跨地区,跨运营商的原因,网站打开速度就会比北京当地的电信客户访问速度慢很多,很容易造成客户流失。 新闻热点、重大事件带来的流量突发问题 虚拟主机,因为跟好几个网站共用一台服务器,每个网站所分带宽有限,带宽过小、流量稍微一多,网站打开速度就很慢,甚至打不开。,CDN原理,
20、让我们先看传统的未加缓存服务的访问过程,以便了解CDN缓存访问方式与未加缓存访问方式的差别:由上图可见,用户访问未使用CDN缓存网站的过程为: 用户向浏览器提供要访问的域名 浏览器调用域名解析函数库对域名进行解析,以得到此域名对应的IP地址 浏览器使用所得到的IP地址,域名的服务主机发出数据访问请求 浏览器根据域名主机返回的数据显示网页的内容。 通过以上四个步骤,浏览器完成从用户处接收用户要访问的域名到从域名服务主机处获取数据的整个过程。,CDN原理,CDN部署在网络运营商的机房,这些运营商又是终端用户的网络提供商,因此用户请求路由的第一跳就到到达了CDN服务器,当CDN中存在浏览器请求的资源
21、时,从CDN直接返回给浏览器,最短路径返回响应,加快用户访问速度,减少数据中心负载压力。,添加Expires或Cache-Control信息头,Expires 与Cache-Control的作用一致,都是指明当前资源的有效期,控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据,这就避免了额外的HTTP请求。 Expires 头使用一个特定的时间,它要求服务器和客户端的时钟严格同步。如: Expires Tue, 05 Jul 2016 08:36:13 GMT Cache-Control 使用 max-age 指令制定组件被缓存多久。它以秒为单位定义了一个更新点。 如: Cach
22、e-Control max-age=600 为什么在HTTP头里要设置同时设置Expires和Cache-control? 因为 Expires 是 HTTP 1.0 定义的字段,而 Cache-Control 是 HTTP 1.1 的字段,万一客户端只支持 HTTP 1.0,那么 Cache-Control 有可能就会不工作,所以一般为了兼容会都写上。如果同时设置的话,其优先级高于Expires。,设置Etag,Etag(实体标签)是Web服务器和浏览器用于确认缓存组件有效性的一种机制。 Etag和Last-Modified都是由服务器端生成,并且随着文件的改变而改变,这样浏览器重新请求时,
23、服务器会根据之前返回的Etag值和Last-Modified的值来判断请求的文件是否发生变化,如果没有变化的话,就给浏览器一个304的http头部,not modified的回应,告诉浏览器说你本地的文件还是新的,目前服务器还没有变更,你就拿本地的来用吧。从而减少浏览器端和客户端数据的流量(如果每次请求都返回完整的内容,当访问量大的时候,流量不改想像),加快浏览器的反应速度;另一方面,也减轻服务器端的压力,提高了服务器并发的能力。所以服务器端Etag和Last-Modified配置上都比较重要。 Etag和Last-Modified有什么区别? Last-Modified只能精确到秒,如果一个
24、文件1秒内被改了好些次,那么Last-Modified就没有用了;再或者分布式部署的时候,由于机子的时钟有可能不一样,用户从一台机子到另外一台机子的时候, Last-Modified就没有用了。于是Etag这个类似基于文件唯一标识的hash的就出现了,这样的话,不管你服务器怎么变化,不管你1秒变化多少次,只要唯一标识的hash不变,文件不变。 Etag是服务器自动生成或者由开发者生成的对应资源在服务器端的唯一标识符,能够更加准确的控制缓存。Last-Modified与ETag是可以一起使用的,服务器会优先验证ETag,一致的情况下,才会继续比对Last-Modified,最后才决定是否返回30
25、4。,两者区别,配置Etag/Last-Modified的情况下,浏览器再次访问资源时,还是会发送请求到服务器询问文件是否已经修改,如果没有,服务器会只发送一个304回给浏览器,告诉浏览器直接从自己本地的缓存取数据;如果修改过那就整个数据重新发给浏览器; Expires/Cache-Control则不同,如果检测到本地的缓存还是有效的时间范围内,浏览器直接使用本地副本,不会发送任何请求。两者一起使用时,Expires/Cache-Control的优先级要高于Etag/Last-Modified 。即当本地副本根据Expires/Cache-Control发现还在有效期内时,则不会再次发送请求去
26、服务器询问修改时间(Last-Modified)或实体标识(Etag)了。,启用gzip压缩,当用户点击你的网站时,由您的服务器提供所要求的文件,这些文件越大,到你的浏览器并显示在屏幕上所需的时间就越长。 由于CSS文件和HTML文件使用了大量重复的文字,并有空白的字符,而GZIP压缩公用字符串,这可以减少页面和样式表的高达70的大小! 当浏览器访问一个Web服务器时,会先检查看看是否有服务器启用GZIP,并请求该网页。如果启用,它就接收gzip文件,否则它会接收未压缩的版本,但这页面大小将大得多。,配置 gzip压缩,默认情况下,Nginx的gzip压缩是关闭的,也只对只对text/html
27、进行压缩,需要在编辑nginx.conf文件gzip用于设置开启或者关闭gzip模块,“gzip on”表示开启GZIP压缩,实时压缩输出数据流。 gzip_min_length设置允许压缩的页面最小字节数,页面字节数从header头的Content-Length中获取。默认值是0,不管页面多大都进行压缩。建议设置成大于1K的字节数,小于1K可能会越压越大。 gzip_buffers表示申请4个单位为16K的内存作为压缩结果流缓存,默认值是申请与原始数据大小相同的内存空间来存储gzip压缩结果。 gzip_http_version用于设置识别HTTP协议版本,默认是1.1,目前大部分浏览器已经
28、支持GZIP解压,使用默认即可。 gzip_comp_level用来指定GZIP压缩比,1 压缩比最小,处理速度最快;9 压缩比最大,传输速度快,但处理最慢,也比较消耗cpu资源。 gzip_types用来指定压缩的类型,无论是否指定,“text/html”类型总是会被压缩的。 gzip_vary选项可以让前端的缓存服务器缓存经过GZIP压缩的页面,例如用Squid缓存经过Nginx压缩的数据。,启用Keep-Alive,在http早期,每个http请求都要求打开一个连接,并且使用一次之后就断开这个连接。使用keep-alive可以改善这种状态,即在一次TCP连接中可以持续发送多份数据而不会断
29、开连接。通过使用keep-alive机制,可以减少tcp连接建立次数,以此提高性能和提高http服务器的吞吐率。【默认是开启的】 但是,keep-alive并不是免费的午餐,长时间的连接容易导致系统资源无效占用。配置不当的keep-alive,有时比重复利用连接带来的损失还更大。所以,正确地设置keep-alive timeout时间非常重要。,改善服务器响应时间,下面图例可清晰显示服务器的响应时间。有很多潜在因素都可能会延缓服务器响应,例如应用逻辑缓慢、慢SQL、路由缓慢、框架、库、资源CPU不足或内存不足等。一般都是程序逻辑、数据库!,减小cookie大小,Cookie是存储在客户端的一小
30、段文本信息,伴随着用户请求在浏览器和服务器之间传递。 浏览器对单个Cookie大小限制不超过4KB;对于同一域名下Cookie的数量也有限制,一般不允许超过50个。 由于Cookie在访问对应域名下的资源的时候都会通过Http请求发送到服务器,所以通过合理地设计Cookie,减少Cookie的体积,能够减少Http请求报文的大小,提高响应速度,降低网络流量。 删除无用或者重复的cookie字段 ,如果cookie被设置在一个域名的顶级路径下,比如”/”,那么这个域名下所有路径都会继承这个cookie。如果想要在不同的路径中使用同一个 cookie,那么可以在顶级路径下面设置这个cookie,不
31、需要在子路径下设置重复的cookie。如果只需要在子路径中使用一个cookie,那么 不要在顶级路径中设置这个cookie,因为这样会导致cookie会发送到不需要此cookie的路径中,造成不必要的流量。 用服务端存储来节省cookie带来的负载 ,在cookie里存储一个唯一的标识符,然后用这个ID和服务器端的数据进行关联,一些需要存在cookie里的内容,可以在服务器端找到,从而减小了cookie的大小。,页面内容使用无cookie域名,将站点的静态文件(如图片、js、css 等)放在一个专门的域名下访问,由于该域名与主站域名不同,所以浏览器就不会把主域名下的 Cookie 传给该域,减
32、少了网络开销,一定程度提高了页面加载速度。,网站速度分析工具,工欲善其事,必先利其器。速度优化相关的工作,我们可能做了多个维度的很多优化,网站打开的速度也感觉快了很多,抱怨的人少了一些,可是我们怎样来衡量网站速度的好坏?有没有方法来长期量化网站的速度,量化我们的优化方法贡献大小? 第三方质量监测:听云、博睿 第三方即时监测工具: webpagetest、 Page Speed Service【直接在线输入URL可以生成很全很详情的性能测试报告】 浏览器插件分析工具: HttpWatch、YSlow、 WEB前端助手(FeHelper) 应用程序:WebWatch、dynaTrace Ajax Edition,性能优化永无止境,最佳优化因地制宜 感谢聆听,