沉迷于优化,
上一次写了 前端项目分析:我是如何做图片优化的(预加载和懒加载)
最近发现有关性能的东西,貌似都很有趣,故再写此篇,纪念(记录)一下。
从输入URL到页面展示,发生了什么?
1、首先从本地查找域名,有的话直接用hosts文件里的ip地址,否则查询DNS,得到ip地址
2、建立TCP连接——进行“三次握手”
3、客户端发送http请求
4、服务端处理,并返回结果给客户端
5、关闭TCP连接——需要“四次挥手”
6、浏览器收到结果,开始解析资源(JS、CSS、HTML),解析HTML生成的dom树,和同时解析css生成的cssom树结合生成渲染树
7、根据渲染树渲染页面
页面的打开速度对网站的优化有极大的意义,那么,如何评价一个页面打开的快不快,可以用两个指标描述:1、ready时间; 2、load时间
这个可以从控制台看到,这里交大家一个方法:
我们可以在控制台输入window.performance.getEntriesByType('paint')
来获取 First Paint (FP:文档中任意元素首次渲染时间)和 First Contentful Paint (FCP:也就是我们常说的 白屏时间 )
我以我所做的某一个网站(优C工作室)为例:
这两个值也不是固定的,比如在第一次打开页面和第二次打开页面时是不同的,,,
再来看另一个:
减少渲染堵塞
(1)减少head标签的JS堵塞
雅虎军规 里说,所有放在head标签里的css和js都会堵塞渲染。那么,如果这些css和js需要加载和解析很久的话,页面就空白了。(这恐怕是很多“同行”恐惧的事情吧…)
我试着渲染了几个js格式,发现,对于加载时间来说,js的渲染时间几乎可以忽略不计!
解决办法: 1、将script放在body后面
2、给script加 defer 属性(值)
(defer是HTML5新增的属性。一旦script是defer延迟的,那么这个script将会被 异步加载 ,不会马上执行,会在readystatechange变为Interactive后按顺序依次执行,例如:)
通过谷歌的浏览器插件,我发现,body中script最先执行,然后才是内联的script执行,不过他们可以并行加载。
但是前面说了,defer后的脚本会被异步加载,但是延后执行(最后),其内部发生了重大变化,不能够影响渲染DOM的过程,只能是渲染完成后才能生效。(此时其优先级甚至比图片的优先级还要低) 很多人想要在HTML结构中调用script的话,这样做就有些不妥了。
而且,defer只能在IE9之前浏览器中使用!
还有一个是 async ,这也是个异步加载,它可以加载完就执行,但是 async只能用在加载外部脚本,js不能写在script里 。
所以现在一般不推荐将js写在head里,不管是内联的还是script。
我还是推荐第一种解决办法,这也是目前大多数网站使用的办法。
(2)减少head里的css资源
由于css必须要放在head里(否则页面加载会出问题,比如加载完成后又进行DOM重绘),但是css太多,在head里又会阻塞页面渲染,所以我们要尽可能减少css代码量。(开始社团官网代码量巨多,后来精简、重构了一次,才发现原来不成熟,写的东西有好多漏洞,虽然现在看来和原来差不多,其代码可是很“精要”啊,继续努力!)
我们不能放太多base64在css里面,这样会导致css极度膨胀,例如:把一张3k的图片转成base64,体积将变成4k。
我曾经解决过一个“ 鼠标移入图片变换 ”的问题,这需要放两张图片,通过css的hover伪类达到效果(曾经说过: 能用css解决的就不要用js ),但是我发现第一次hover的时候不会马上变换,要稍微等到图片加载完,这在产品角度上时不可接受的!
一种解决办法就是我在另一篇博文(本文开头处)中提到的 预加载 , 另一种解决办法是:把hover写在svg里面,如下:
这样,如果我们开启了gzip压缩,实际传输大小不过30k。
但是,这种方法依旧不推荐使用,涉及图片,谁也说不准啊。
像一般的图片,有位前辈提点我说,完全可以用图标字体的解决方案。将svg转为icon,世界一片清净,,,
这样,gzip后大小不到10k。
优化图片
这个话题在文首博文中详细介绍,此处不再多说!
压缩和缓存
前面提到gzip,我们发现这能大大减少文件的体积,例如一个180k的css文件被压成30k,就减少了83%的体积。
如何开启,这需要Nginx服务器的支持(关于Nginx,我也多次提到,不再多说):
在 nginx.conf (Nginx的配置)中:
server{
gzip on;
gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css;
}
这属于HTTP协议的内容
但是它有缺点,就是重新渲染的问题
我们不如使用 etag ,Nginx开启etag只需要在server配置( nginx.conf etag )里加上一行:etag on;
即可。
升级到HTTP/2
据说现在http2已经席卷而来,而且其有一个强大的优势,在于对于一个域只进行一次tcp连接,使用多路复用,传输多个资源(同时加载),这样就不必使用诸如雪碧图、合并css/js文件等技术减少请求数了(使用雪碧图只有一个优点:减少请求次数,这和它不可避免的缺点(高清屏会失真、图片变化极不方便)相比,简直不足为道)。
这个技术的使用也很简单,只需要使用nginx 1.10.0和openssl 1.0.2以上版本,安装好后再配置文件中( ngnix.conf )加上:listen 443 ssl http2;
即可。(写在http块中的server块中)
当然,对于不兼容HTTP2的浏览器,nginx也会自动处理。
哦,对了,还要说说 代码优化 :HTML不要嵌套太多层(否则会加重页面layout的压力),css选择器别写的太复杂(不然计算量会很大),js不要滥用闭包(闭包会加深作用域链,加长变量查找时间)
还有,上面提到的nginx的优化,前提是:你用的nginx服务器,否则,你优化他干什么?
浏览器渲染进程会开启多个线程协作完成!
1、GUI渲染线程 ,负责渲染浏览器界面,解析HTML、css,构建DOM树和RenderObject树,布局和绘制 ——一旦界面因为某种操作引发了回流,此线程就会执行
2、JS引擎线程 ——和GUI线程互斥,在js引擎执行时,GUI线程被挂起
3、事件触发线程 ——依赖js的队列机制完成(当一个事件触发时该线程会把事件添加到待处理队列的队尾,等待js引擎处理)
4、定时器触发线程 ——依赖js的队列机制完成
5、异步http请求线程