本文提到的网站性能指网站的响应速度,这也符合绝大部分人对于网站性能的理解:访问快速的网站性能好,反之,访问速度越慢,则网站性能越差。本文总结的优化方法是宏观的工程层面的方法,并不包含微观的语言语法层面的方法,例如,JS、CSS的语法优化,这一部分一样影响网站的性能,但语言语法层面的优化更多的是取决于开发人员的编程水平。html
什么样的网站响应速度快呢?其实很容易想到,网站加载资源的速度越快,网站响应速度越快;网站须要加载的资源越少,网站响应速度越快。这就分别对应网站性能优化的两大方向:资源缓存、资源合并压缩。当浏览器完成资源的加载后,须要进一步解析资源,才能渲染出最终的网页,因此,浏览器的解析机制也是网站性能优化的一个方向。各类优化方法均可以归类到这三个大方向中。前端
将网站的静态资源分离,如静态HTML、图片Image、样式CSS、脚本JS等,把静态资源部署到CDN中,能够明显加快这部分资源的加载速度。webpack
HTTP缓存会把浏览器加载过的资源缓存到本地,下次加载时,只要缓存的资源没有过时,就能够直接使用本地的资源,减小了HTTP请求次数,加快了资源加载速度。具体作法是设置HTTP Header 中的Cache-Control参数。HTTP 1.0 中使用Pragma和Expires两个参数进行缓存,不过早已不推荐使用。web
用一个HTTP请求去加载一个10M的文件,和把这个文件拆分红1M的10个文件,用10个HTTP请求并行去加载,哪种方式能更快完成加载?既然提到减小HTTP请求能够提升网站响应速度,那么结论貌似应该是用一个HTTP请求的方式更快。其实正确的答案是:不必定!编程
我作了一个小实验:有两个html文件,index1.html和index2.html,index1.html中用1个<script>标签加载一个2M的js文件bundle.js,index2.html中用6个<script>标签分别加载bundle1.js, bundle2.js …… bundle6.js,这6个js文件由bundle.js平均拆分获得。分别请求index1.html和index2.html 10次,获得加载bundle.js的时间和加载bundle1.js 到 bundle6.js的时间(以最后一个js文件加载完成为结束时间),计算平均加载时间分别为:1.07s 和 1.87s。浏览器
实验结论证实了,一个HTTTP请求加载一个合并后的资源文件,比多个HTTTP请求并发加载多个资源文件效率高。但结论只是针对平均加载时间而言,对于单次的比较,彻底可能出现相反的结论,例如个人实验过程当中,单一HTTTP请求加载时间的最大值为2.36s,超过了第二种加载方式的平均时间1.87s。可能有些人会比较疑惑,为何并行的效率反而比串行的要低呢?其实,HTTP请求加载资源的瓶颈在带宽,而不是请求的数量,在一个请求已经利用带宽很充分的状况下,增长新的请求并不能减小总体的资源加载时间。缓存
其实,减小HTTP请求来提升网站性能主要是基于如下2个缘由:性能优化
1) HTTP链接的创建是比较耗时的,通常须要上百ms,每一个HTTP请求还有必定的网络延时,须要的HTTP请求越多,这两部分产生的耗时也就越多。固然,HTTP 1.1 对keep-alive的默认支持,能够实现链接的复用,很大程度上优化了这个问题。服务器
2)每一个HTTP请求都须要附带额外的数据,好比请求和响应中的头信息,Cookie信息。当请求的资源很小时,附带的额外数据可能比实际的资源还大。网络
合并压缩JS文件,一方面JS文件数量减小,须要的HTTP请求数也就减小了;另外一方面,压缩JS文件能够极大地减少文件体积。可使用webpack等Web构建工具对JS文件进行压缩合并。
要注意,压缩合并JS文件并非要把全部的JS文件都打包到一个JS文件中。通常的作法是按照“基础代码”+“页面代码”分别打包。“基础代码”指各个页面或路由(对单页面而言)都要用到的通用代码,“页面代码”是只在某个具体页面或路由中才会用到的代码。这样就能够实现JS代码按需加载,避免页面首屏加载时,由于单一JS文件过大,而影响首屏显示时间。对单页面应用来讲,还能够有一个vendor.js的文件,这个文件中的内容是一些用到频率比较高的第三方库(如ECharts等),但这些库并非每一个路由都会用到的,因此并不会被打包到“基础代码”中。将这样的第三方库从各个路由页面对应的JS文件中拆分,一是能够减小全部JS文件的总体大小,由于原本多是A、B等多个文件都会包含的代码,如今则只须要一份;二是vendor.js只须要被加载一次,后续打开其余路由时,就能够不须要再次加载这部分代码了,起到了资源预加载的做用。
对CSS文件进行合并压缩,基本原理和作法同JS文件。
1) 使用WebP格式的图片。WebP是一种支持有损压缩和无损压缩的图片文件格式,派生自图像编码格式 VP8。根据 Google 的测试,无损压缩后的 WebP 比 PNG 文件少了 45% 的文件大小,即便这些 PNG 文件通过其余压缩工具压缩以后,WebP 仍是能够减小 28% 的文件大小。
2)使用字体图标IconFont。能够任意设置Icon图形的大小和颜色(只能是单色,由于本质上是给字体设置颜色)。
3)使用CSS Sprites将多张图片合并成一张,从而减小HTTP请求数量。
4)使用Base64直接把图片编码成字符串写入CSS文件,也是从减小HTTP请求数量考虑。但须要注意,Base64编码的图片最好是小图片(最好几十字节级别的),由于图片通过Base64编码后,通常会比原文件更大些。并且太长的Base64编码字符串也会影响CSS的总体可读性。
5)对于须要大量图片的网站,应该把图片资源单独部署,并使用不一样的域名来访问。由于图片资源占带宽很大,若是把图片和其余资源部署到一台服务器或一个集群中,服务器端的出口带宽会受到很大影响。使用不一样的域名加载图片资源,能够更好的利用浏览器并行下载的特性,由于浏览器对于一个域名下的最大并行请求数是有限制的。
服务端开启gzip压缩,能够减小资源文件在网络传输过程当中的体积大小。
浏览器的工做原理很是繁琐和复杂,要想仔细了解,能够参考这篇经典的文章How browers work。
结合文章和我本身实验验证,简单来讲的话,当浏览器载入一个HTML文件后,
1)会先将加载HTML中引用的全部外部资源(JS、CSS文件等)的请求放到一个队列中,而后浏览器经过多个线程(具体由浏览器设置决定)并发加载这些资源。
2)紧接着对HTML进行自上而下的解析。
3)当解析到<script>标签时,若是标签内是内嵌到HTML中的JS代码,会直接执行这部分代码;若是标签引用了外部的JS文件,且这个文件此时尚未下载完成,解析过程会被阻塞,直到JS文件下载完成,而后解析执行JS代码,以后才会继续HTML的解析过程;若是标签引用了外部的JS文件,但此时这个JS文件已经下载完成,则会直接执行这部分JS代码,并不会阻塞HTML的解析(能够理解成此时JS代码的执行本就属于HTML解析这个<script>标签的过程)。
4) 当解析到<link>标签时,无论<link>中引用的外部CSS资源是否加载完成,都不会阻塞HTML继续向下解析。
这里有2个须要注意的地方:
1)由于JS的加载会阻塞HTML向下解析,因此多个JS文件中代码的执行顺序,是和他们在HTML中的位置顺序保持一致的。例如HTML中,从上向下依次引入a.js, b.js, a.js的文件大小远大于b.js,这样b.js文件极可能先完成加载,可是并不会先于a.js中的代码执行,由于在a.js加载、解析、并执行完成前,HTML的解析是处于阻塞的,b.js所在的<script>标签天然也不会被解析执行。若是不但愿加载外部JS文件阻塞HTML的解析,可使用script标签的defer或async属性,这里就再也不展开。
2)全部引用的外部脚本或样式文件,在HTML开始解析前,就已经加入到浏览器的请求队列中,因此多个外部资源开始加载的起始时间通常不会相差很大,除非请求的外部资源数量不少,超过了浏览器的并发请求数。
基于浏览器工做原理的经常使用优化性能的方法有2个:
1)引用外部CSS文件的link标签,通常会写在<head>内,这是为了能尽早的使<body>内的元素获取样式,优化视觉显示效果。
2)引用外部JS文件的script标签,通常会写在<body>底部,这是为了不HTML的解析被阻塞,从而使页面元素更快的显示出来。须要注意,虽然script写在<body>底部,但这不意味着<body>内的其余元素都解析完成后才开始加载这些JS文件,这些JS文件依然会在HTML开始解析前,就被加入到请求队列中。
以上就是从资源缓存、资源合并压缩和浏览器解析原理三个维度出发,经常使用的优化网站性能的实践方法
学习过程当中遇到什么问题或者想获取学习资源的话,欢迎加入学习交流群
343599877,咱们一块儿学前端!