# 前端性能优化方案 前端资源比较庞大,包括`HTML`、`CSS`、`JavaScript`、`Image`、`Flash`、`Media`、`Font`、`Doc`等等,前端优化相对比较复杂,对于各种资源的优化都有不同的方式.按粒度大致可以分为两类,第一类是页面级别的优化,例如减小`HTTP`请求数、脚本的无阻塞加载、内联脚本的位置优化等,第二类则是代码级别的优化,例如`JavaScript`中的`DOM`操作优化、图片优化以及`HTML`结构优化等等。在用户角度前端优化可以让页面加载得更快,对用户的操作响应得更及时,能够给用户提供更为友好的体验,在服务商角度前端优化能够减少页面请求数,减小请求所占带宽,能够节省服务器资源。 ## 减少HTTP请求 加载前端的大部分时间在于下载各种资源,浏览器对于同一个服务器的`HTTP`请求连接池数量也是有限的,对于过多的请求需要排队等候。最小化`HTTP`请求减少请求次数可以防止`HTTP`连接池被占满,同时也能避免过多`HTTP`链接时的`TCP`握手造成的时间消耗。 ### CSS Sprite `CSS Sprite`也就是俗称的雪碧图,将多张图片合并到一张图片中,可以减少图片的数量,此外由于合并图片相对分开的图片减少了存储信息的开销如颜色表和格式信息等,合并图片后的大小比分开的图片的大小的总和要趋于更小。 当然如果合并图片时有大量空白来分隔原来的单个图片那么其大小会趋于更大。使用雪碧图,需要使用`CSS`的`background-image`和`background-position`属性显示所需的图像段。 ### Image maps 假如网站有很多带链接的图片例如地图应用等,那么图片映射`image maps`将是一个很好的选择,`image maps`允许在单张图片上有很多带链接的图片,通过``与``来将一张完整的图片映射分割为多个区域来制作不同的链接,同样是可以减少图片的`HTTP`请求链接数量。 ### Inline images 通过使用`data:URL`方案来直接将图像数据嵌入到页面或者`CSS`中,虽然这会增加文档或者是`CSS`文件的大小。但同样这确实是一个减少`HTTP`请求数量的方案,对于`data:URL`的格式为`data:[][;base64],`。 ### Font icon 使用字体图标来代替图标,将多个图标合成为字体图标不仅可以减少对于图片的`HTTP`请求数量与图标大小,还作为矢量图对于放大缩小等操作不会失真。此外字体图标的优点还包括其很容易改变颜色、产生阴影、透明效果等,可以得到`CSS`的很好的支持从而制作各种样式、旋转和动画效果等。 ### Combined files `Combined files`也就是合并文件,将多个`CSS`文件或者`JavaScript`文件合并成一个`CSS`文件或者`JavaScript`文件,可以有效减少`HTTP`请求数量,并且可以通过压缩算法减小文件的大小。当脚本和样式表在页面之间变化时,组合文件可能会变得难以阅读和修改,但是将其作为发布过程的一部分可以缩短响应时间。 ## 利用缓存机制 ### 缓存控制 通过服务器端设置响应头的`Expires`与`Cache-Control`来设置资源组件过期时间以及过期策略,对于静态资源可以通过设置`Expires`为一个长期时间来实现永不过期策略,对于动态组件通过`Cache-Control`指定缓存机制来辅助浏览器处理条件请求。 ### 外部引用 将`JavaScript`与`CSS`设置为外部文件引入而不是直接嵌入到`HTML`中,由于浏览器的缓存机制,外部文件可以通过浏览器的缓存引入而不需要每次请求重复请求同一个资源文件,这样就使得浏览器在第二次打开页面的速度会快得多,当然全部由外部文件引入的方式会增加`HTTP`请求数量。 所以外部引用的关键问题就在于如何权衡相对于`HTML`文档数量而言,缓存外部`JavaScript`与`CSS`文件的数量,尽管难以量化,但可以使用各种度量标准来衡量此因素,网站上的用户每个会话具有多个页面视图,并且许多页面都重复使用相同的脚本和样式表,则缓存的外部文件会带来更大的潜在利益。 ## 优化资源加载 ### 样式表位置 根据浏览器渲染的顺序,将`CSS`在``中引入或者嵌入,相对于将`CSS`放到``或者页面底部来说,可以使页面渲染速度加快,这对于页面内容比较丰富的网站或者网络链接较慢时相当重要。 假如将样式表放置于底部,就会导致浏览器还未加载样式表就开始渲染页面,无法渐进式渲染页面而直接从无样式状态立即跳转到有样式状态,用户体验较差。此外有些浏览器可能会在`CSS`下载完成后才开始渲染页面,样式表放在下方会导致页面渲染推迟。 ### 脚本位置 浏览器是可以并发请求的,这一特点使得其能够更快的加载资源,然而外部引入`JavaScript`脚本在加载时却会阻塞其他资源,例如在脚本加载完成之前,它后面的图片、样式以及其他脚本都处于阻塞状态,直到脚本加载完成后才会开始加载,原因之一是`Js`可能会改变页面或者改变`Js`间的依赖关系,例如`A.js`中用`document.write`改变页面,`B.js`依赖`A.js`。 因此要严格保证顺序,不能并行下载。如果将脚本放在比较靠前的位置,则会影响整个页面的加载速度从而影响用户体验。此外当浏览器发现`Js`脚本时浏览器会立即开始解析脚本,并停止解析文档,因为脚本有可能会改动`DOM`与`CSS`,继续解析会浪费资源。解决这些问题的方法有很多例如异步加载脚本等,而最简单可依赖的方法就是将脚本尽可能的往后挪,减少对并发下载与页面渲染的影响。 ## 优化代码方案 ### 避免CSS表达式 `CSS`表达式通过`expression`方法来接受`JavaScript`表达式,是一种动态设置`CSS`的强大的方式,但同样也是非常危险的方式,`CSS`表达式的问题在于其会进行频繁的计算,`CSS`计算的频率要远远超出我们的想象,不仅在页面显示和缩放时会进行计算,在页面滚动或者移动鼠标都会重新计算一次,从而影响到页面的性能。 可以通过使用`Js`将属性进行一次算来并赋值给样式属性,也就是一次性表达式,如果必须在页面的整个生命周期中动态设置样式属性,则可以使用事件处理程序代替`CSS`表达式。如果必须使用`CSS`表达式,需要注意它们可能会被计算数千次,并且很可能会影响页面的性能。 ### 避免重定向 尽量避免使用重定向,当页面发生了重定向,就会延迟整个`HTML`文档的传输。在`HTML`文档到达之前,页面中不会呈现任何东西,也没有任何组件会被下载,降低了用户体验。如果一定要使用重定向,如`http`重定向到`https`,要使用`301`永久重定向,而不是`302`临时重定向。因为如果使用`302`,则每一次访问`http`,都会被重定向到`https`的页面,而永久重定向,在第一次从`http`重定向到`https`之后就会被浏览器记住,每次访问`http`,会直接返回`https`的页面。 ### 最小化操作DOM `JavaScript`操作`DOM`无可避免的会触发浏览器的重绘或者回流,由于重绘和回流可能代价比较昂贵,因此最好就是可以减少它的发生次数,为了减少发生次数,我们可以合并多次对`DOM`和样式的修改,然后一次处理掉,或者将样式事先设计好,动态去改变`class`。 或者采用离线修改`DOM`的方案,使用`documentFragment`对象在内存里操作`DOM`,在内存中的`DOM`修改就是让元素脱离文档流,当然是不会触发重绘的,将对`DOM`的所有修改批量完成,想怎么改就怎么改,然后将节点再放入文档流中,只触发一次回流。 ## 压缩资源文件 ### Gzip 从`HTTP / 1.1`开始,客户端可以通过使用`HTTP`请求中的`Accept-Encoding: gzip, deflate`来指示对压缩的支持。如果服务器在请求中看到此标头,则可以使用客户端列出的方法之一压缩响应,服务器通过响应中的`Content-Encoding: gzip`通知客户端采用`gzip`压缩。`Gzip`的压缩率很高,是目前最流行,最有效的压缩方法,它由`GNU`项目开发,并由`RFC 1952`标准化。 ### 压缩外部文件 压缩`JavaScript`和`CSS`文件,从代码中删除不必要的字符以减小其大小,从而缩短加载时间,当代码最小化时,所有注释以及不需要的空白字符都将被删除,由于减小了下载文件的大小,因此可以提高响应时间性能。 ## 优化网络请求 ### CDN `CDN`的全称是`Content Delivery Network`,即内容分发网络,`CDN`是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,降低网络拥塞,提高用户访问响应速度和命中率。 当用户处于跨地域的多个位置时,对于服务器响应速度的感知是有差别的,用户访问网站的绝大部分时间都是处于下载静态资源状态的,将这些静态资源首先分发到`CDN`各服务器,可以大大缩短响应时间,`CDN`可以根据用户网络状态信息来选择网络跳数最少的服务器或响应最快的服务器来就近交予用户资源。 ### DNS预解析 当浏览器访问一个域名的时候,需要解析一次DNS,获得对应域名的`ip`地址。在解析过程中,按照浏览器缓存、系统缓存、路由器缓存、`ISP`(运营商)`DNS`缓存、根域名服务器、顶级域名服务器、主域名服务器的顺序,逐步读取缓存,直到拿到`IP`地址。 `DNS Prefetch`,即`DNS`预解析就是根据浏览器定义的规则,提前解析之后可能会用到的域名,使解析结果缓存到系统缓存中,缩短`DNS`解析时间,来提高网站的访问速度。若是提前解析`DNS`,由于其是并行的,不会堵塞页面渲染,这样可以缩短资源加载的时间。 ```html ``` ### 配置ETag 实体标签`ETag`是`Web`服务器和浏览器用来确定浏览器缓存中的资源是否与原始服务器上的资源匹配的一种机制,添加了`ETag`,以提供一种比上次修改日期更灵活的验证实体的机制。`ETag`是唯一标识组件特定版本的字符串,唯一的格式限制是用引号引起来,原始服务器使用`ETag`响应头指定组件的`ETag`。 ### 尽早释放缓冲 当用户请求页面时,后端服务器将`HTML`页面拼接在一起可能需要`200`到`500`毫秒的时间,在这段时间内,浏览器在等待数据到达时处于空闲状态,这段时间则可以将服务端部分已经处理好的数据发送到前端,使浏览器先开始加载外部资源。 例如使用`PHP`,则可以使用函数`flush()`将部分就绪的`HTML`响应发送到浏览器,以便浏览器可以在后端忙于处理`HTML`页面的其余部分时开始获取资源,好处主要体现在繁忙的后端或轻量级前端。 ## 每日一题 - ## 参考 - - - - -