众所周知,JS的加载和执行会阻塞浏览器渲染,因此目前业界广泛推荐把script放到</body>以前,以解决js执行时找不到dom等问题。但随着现代浏览器的普及,浏览器为咱们提供了更多强大的武器,合理利用,方可大幅提升页面加载速度。
首先咱们从浏览器的角度解释一下从输入URL到页面展现经历了些什么,以以下html文档举例javascript
<html> <head> <link rel="stylesheet" type="text/css" href="/style.css"> <script type="text/javascript" src="/header.js"></script> </head> <body> <p>Text</p> <script type="text/javascript" src="/main.js"></script> </body> </html>
浏览器自上而下读取html文档(此过程叫html parser),当发现style.css文件时,浏览器parser停下来去搞css,等style.css下载并解析完毕,浏览器继续parser。紧接着发现header.js, 因而html parser又停了,浏览器下载并执行完header.js,继续parser。此时屏幕上还什么都没有。...parser,发现<p>,遂将p中文字展现了出来。紧接着又发现main.js,浏览器又停下parser,下载并执行完main.js才继续parser,直到页面渲染完毕。css
咱们假设header.js中只有一行代码console.log('header')
, 但服务器响应很慢,要10秒才能把它返回给浏览器,浏览器执行这段代码须要1ms,那在这 10s+1ms 内,页面将一直空白。浏览器执行JS的时间取决于代码质量和硬件,并非前端工程师随即可以优化的,因此优化的重点在JS的下载时间。html
很是简单,效果立竿见影,加快页面加载时间,多用于预解析CDN的地址的DNS前端
<!--在head标签中,越早越好--> <link rel="dns-prefetch" href="//example.com">
浏览器会在遇到以下link标签时,马上开始下载main.js(不阻塞parser),并放在内存中,但不会执行其中的JS语句。
只有当遇到script标签加载的也是main.js的时候,浏览器才会直接将预先加载的JS执行掉。java
<link rel="preload" href="/main.js" as="script">
浏览器会在空闲的时候,下载main.js, 并缓存到disk。当有页面使用的时候,直接从disk缓存中读取。其实就是把决定是否和什么时间加载这个资源的决定权交给浏览器。segmentfault
若是prefetch还没下载完以前,浏览器发现script标签也引用了一样的资源,浏览器会再次发起请求,这样会严重影响性能的,加载了两次,,因此不要在当前页面立刻就要用的资源上用prefetch,要用preload。浏览器
<link href="main.js" rel="prefetch">
上面咱们的例子中,script标签都是在没有多余属性的状况下执行的,只要下载过程结束,浏览器就会将JS执行掉。
defer和async是script标签的两个属性,用于在不阻塞页面文档解析的前提下,控制脚本的下载和执行。缓存
defer,async与下载时机也有关,具体看这张图。性能优化
defer的执行时间是在全部元素解析完成以后,DOMContentLoaded 事件触发以前。服务器
async的执行时间是在当前JS脚本下载完成后,因此多个async script是执行顺序是不固定的。async只能用于加载一些独立无依赖的代码,好比Google Analysis之类。
前面两节帮咱们弄懂了JS的下载和执行时机,那什么样的页面才是完美符合现代浏览器的那?其实关键在于的preload和prefetch!提早告知浏览器,咱们的网站立刻要用的是什么,之后可能要用的是什么,浏览器才能更快的渲染页面。下面是一段实例代码
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Faster</title> <link rel="dns-prefetch" href="//cdn.com/"> <link rel="preload" href="//js.cdn.com/currentPage-part1.js" as="script"> <link rel="preload" href="//js.cdn.com/currentPage-part2.js" as="script"> <link rel="preload" href="//js.cdn.com/currentPage-part3.js" as="script"> <link rel="prefetch" href="//js.cdn.com/prefetch.js"> </head> <body> <script type="text/javascript" src="//js.cdn.com/currentPage-part1.js" defer></script> <script type="text/javascript" src="//js.cdn.com/currentPage-part2.js" defer></script> <script type="text/javascript" src="//js.cdn.com/currentPage-part3.js" defer></script> </body> </html>
首先,Parser在遇到head中preload时开始下载JS,读到script标签的时候,若是已经下载完了,直接按顺序执行之。若是没下载完,则会等到下载完再执行。这样就能够在刚进入页面时开始非阻塞的下载JS代码了。
其次,页面会在空闲时,加载prefetch的JS,若是以后页面发生跳转,跳转的目标页面引入了prefetch.js,浏览器会直接从disk缓存中读取执行。
将script标签依然放在</body>以前,并增长defer标签,确保老浏览器兼容,并在全部DOM元素解析完成以后执行其中的代码。
至此,完美的HTML结构出炉了。
CSS的下载和解析同样会阻塞渲染,形成白屏,CSS中的字体文件更是影响首屏渲染关键因素之一,下一篇幅我会结合preload和prefetch,带你一块儿优化CSS,告诉你什么是最适合现代浏览器的CSS加载策略,期待的话,点个赞吧!