废话:异步加载和预加载一直都是前端优化必备技能之一,今天咱们就来深度解析一下经常使用的几个关键点。
废话很少说,任何长篇大论的教程都抵不过一张清晰明了的高清大图来得好:css
和预加载(preload、prefetch、dns-prefetch、preconnect 、prerender)")html
从这张图里面,咱们看到了什么,大概总结为如下四点:前端
好了,区分的大概基本已经了解了,那怎么记住呢?默认的状况咱们已经很熟了,就无需多记了。html5
defer翻译过来是延缓的意思,也就是拖拖拉拉了,因此比较懒,也就是说什么都不想作,也就是哪怕你把饭端在我面前,我也懒得动嘴的那种,这么一想,咱们不就记住了,哪怕你客户端把JS文件下载好了,我也懒得执行,最后实在是你们都干完事了,我才不情愿的去执行JS文件。jquery
async翻译过来就是异步的意思,异步异步,不就是一步一步嘛,什么都想一步到位,也就是说,只要下载完我就立马执行,至于其余的想都不想。es6
module翻译过来就是模块的意思,es6用过的人基本都了解这个关键字,加载也和defer差很少,只不过能够加载多个JS文件而已。web
咱们再来看看这几个加载的DOM事件时机:api
和预加载(preload、prefetch、dns-prefetch、preconnect 、prerender)")跨域
从这张图能够看出大概这几点:浏览器
从上面咱们能够看出,若是你的脚本依赖于DOM构建完成是否完成,则可使用defer;若是无需DOM的构建,那就能够放心的使用async了。
defer属性仅适用于外部脚本,也就是仅当存在src属性时才会生效;若是一个script标签上面即存在defer属性,也存在async属性,那么浏览器会如何解析这种状况呢?咱们经过一段代码验证结果,详情点击这里。
也就是说defer的优先级没有async高,咱们看一下规范是怎么处理这种状况的。
The defer attribute may be specified even if the async attribute is specified, to cause legacy Web browsers that only support defer (and not async) to fall back to the defer behavior instead of the blocking behavior that is the default.
规范只是说明了在不支持async的状况下浏览器将会回退支持defer,但并无明确指明两种都支持的这种状况,也就是说这一种状况浏览器自行处理,通过测试,各个浏览器表现行为:
IE暂时没有安装,看来各大浏览器表现一致,总之async的优先级是最高的。
下面来看看defer的兼容性,移动端一片大绿,能够放心使用,IE10以上能够放心使用,IE6-9有一点小问题就是不会按照script标签的执行顺序进行执行,对于不依赖先后脚本库的能够不用担忧,可是若是依赖库的就不行了,好比你的项目依赖jQuery,后面紧接着使用jQuery的方法可能就会出现问题。
和预加载(preload、prefetch、dns-prefetch、preconnect 、prerender)")
和defer同样,也仅仅适用于外部脚本,也就是仅当存在src属性时才会生效。
async的兼容性在移动端也是一片大绿,IE仅支持IE10+。
和预加载(preload、prefetch、dns-prefetch、preconnect 、prerender)")
在现代浏览器中,咱们能够声明acript标签type=’module’属性从而拥抱es6的模块导入导出语法,就像这样:
<script type="module"> import { Max } from "./math.js"; console.log(Max(1, 2, 7, 2, 0)); //7 </script>
看起来是否是使人很激动,彷佛对于开发者十分友好,可是这里也有几个与传统脚本不同的地方:
模块的导入方式目前仅支持如下几种模式:
支持 import {math} from './math.mjs'; import {math} from '../math.mjs'; import {math} from '/modules/math.mjs'; import {math} from 'https://simple.example/modules/math.mjs'; //不支持 import {math} from 'jquery';
固然,浏览器厂商也在考虑支持 import {math} from ‘jquery’ 这种格式,不过,仍是须要一段很长的路要走。
module的默认状况就是defer的,所以没必要再module上面又添加一个defer熟悉,而且自己就不支持这种写法,可是支持async属性,其加载渲染方式和async差很少,这里再也不赘述。
在移动端的兼容性还算能够,可是IE貌似都败下阵来,只要edge16+以上还算支持,对于不支持module的浏览器可使用nomodule属性做为版本回退的方案解决。
和预加载(preload、prefetch、dns-prefetch、preconnect 、prerender)")
最后来讲一下module的使用建议,大型项目(100模块以上)不建议直接使用模块语法,应该使用打包工具诸如Webpack,Rollup,、或 Parcel,由于静态导入或导出语法是静态可分析的,经过捆绑工具能够去掉多余的模块,咱们考虑下面这一种场景:
import { Modal } from './util.js'; Modal({ title: 'hello' })
若是咱们经过打包工具打包这一份代码,最终生成的JS文件将会只包含Modal这一个函数,假若咱们没有使用打包工具,浏览器将会下载整个util这一个JS文件,并经过进一步分析了解了使用了Modal这一个函数,这对于没有用到util里面的所有函数的方式,则是一种多余的带宽浪费。
在咱们的浏览器加载资源的时候,对于每个资源都有其自身的默认优先级,假若咱们能修改每个资源的默认优先级,那咱们几乎能够按照咱们的预期加载想要加载的资源。
以谷歌浏览器为例,咱们打开控制台,并切换到Network选项,点击刷新页面,在网络下面的title一行点击鼠标右键,勾选Priority便可看到加载资源的优先级,咱们能够看到样式的级别比脚本的优先级高,毕竟页面的一加载进来确定是样式首先须要渲染的,否则整个页面便会四分五裂,用户体验很差。
和预加载(preload、prefetch、dns-prefetch、preconnect 、prerender)")
preload翻译过来就是预加载,一旦启用后便会告知浏览器应该尽快的加载某个资源,若是提取的资源3s内未在当前使用,在谷歌开发工具将会触发警告消息
和预加载(preload、prefetch、dns-prefetch、preconnect 、prerender)")
大概的语法以下:
<link rel="preload" as="script" href="foo.js"> <link rel="preload" as="style" href="bar.css">
除了以上指定的资源外,还能够加载audio、font、video以及document等,详情点击这里了解。
如需加载跨域的资源列表,则须要正确设置CORS,接着即可以在<link>元素中设置好crossorigin属性便可:
<link rel="preload" as="font" crossorigin="crossorigin" type="font/woff2" href="foo.woff2">
这里有一个特例即是不管是否跨域,字体的获取都须要设置crossorigin属性,这是因为历史缘由形成,有兴趣了解可移步这里了解,另外咱们还可使用media响应式的加载图片,好比:
<link rel="preload" href="bg@2x.png" as="image" media="(max-width: 325px)"> <link rel="preload" href="bg@3x.png" as="image" media="(min-width: 400px)">
另外一个重要的地方即是若是预加载一个脚本,它并非执行:
//只拉取下载不执行 var preloadLink = document.createElement("link"); preloadLink.href = "foo.js"; preloadLink.rel = "preload"; preloadLink.as = "script"; document.head.appendChild(preloadLink); //若是须要执行 var preloadedScript = document.createElement("script"); preloadedScript.src = "foo.js"; document.body.appendChild(preloadedScript);
兼容彷佛IE全体阵亡,edge也得17+才能勉强支持,火狐须要手动启动支持,移动端支持程度仍是挺好的。
和预加载(preload、prefetch、dns-prefetch、preconnect 、prerender)")
简而言之预提取就是在咱们页面加载完成后,在带宽可用的状况下,加载用户下一步期待的页面资源,好比企业认证,通常都是分好几个页面进行认证的,在用户从第一个页面进行认证的时候,在页面加载完成,用户正在填写表单数据之时,加载第二个页面的部分资源,从而使用户更快打开下一个页面,从而增长用户体验,示例:
<link rel="prefetch" href="demo.html"> <link rel="stylesheet" href="demo.css">
当浏览器解析到link标签时,读取到rel的值为prefetch,便会将这一个资源添加的队列中,当浏览器空闲时便会预提取资源,可是在demo.html页面中只是加载HTML,不会加载demo页面里面的任何其余资源,除非你在demo页面也明确使用了预提取。
各大浏览器支持都还挺好,IE11+以上,可是Safari貌似到如今还没支持。
咱们都知道,当咱们在浏览器的地址栏输入域名的时候,首先要进行的就是域名解析,由于咱们须要加载域名对应的资源,这个过程很快,可是若是在移动端,那但是一个分秒必争的地方,当一个页面须要访问许多外部域名的资源的时候,若是咱们能在用户浏览页面的时候,在浏览器空闲的时间,把可能须要访问的域名都提早作好了域名解析,那是否是大大增长了用户打开页面的响应时间,增长用户体验,为了解决这个问题,w3c便提出来一个标准,学名叫dns-prefetch。
和预加载(preload、prefetch、dns-prefetch、preconnect 、prerender)")
使用方法上面中已经支持了,指定rel=”dns-prefetch”,在href中指定页面须要解析的域名便可,你可能已经注意到了上面的图中域名使用了双斜杠,这个双斜杠表示URL以主机名开头,和你使用完整URL(好比http://g.alicdn.com/)是等效的。在RFC1808中被指定。
固然并非全部的页面须要用到的外部域名都须要作这样的域名解析,浏览器默认会解析超连接属性的href里面的域名,而且你的网站域名还不能是HTTPS,若是是HTTPS,则须要设置请求头或加入一段强制开启域名解析的meta标签。
//HTTP <link rel="dns-prefetch" href="//a.com"> //多余 <a href="http://a.com"> //HTTPS <meta http-equiv="x-dns-prefetch-control" content="on">//强制开启 <a href="http://a.com">
固然,并不建议对HTTPS网站开启强制解析的方式,由于这样会带来一些安全隐患,具体可参考这里。
预链接,也就是启动早期链接(包括DNS查找,TCP握手和可选TLS协商),咱们来看一个例子:
<link href='https://fonts.demo.com' rel='preconnect' crossorigin> <link href='https://demo.com/css?family=黑体' rel='stylesheet'>
一个网络字体正常加载通常都包括:
差很少一个字体的渲染要通过这么几个过程,可是若是字体的前期准备(DNS查找,TCP握手和可选TLS协商)和样式的加载是并行执行,是否是能够更快的渲染页面,preconnect就是为这个而生的,从而优化用户体验。
固然若是是跨域资源,不要忘了加上crossorigin属性。
IE15+以上部分兼容,移动端兼容良好。
和预加载(preload、prefetch、dns-prefetch、preconnect 、prerender)")
预渲染,简单来讲就是浏览器会下载指定连接的资源,并下载以及渲染它,就比如咱们打开了一个新的Tab标签页,静默的在后台的下载执行,固然,浏览器也不必定会下载渲染它,这取决预不少状况,好比浏览器是否空闲以及操做系统是否会放弃下载过慢的资源文件。
除非你真的能十分的确定用户接下来必定会触发你所指定的资源地址,不然对于用户来讲这是一种带宽的浪费,使用例子以下:
<link rel="prerender" href="https://www.apple.com/">
虽然是prerender是HTML5规范的一部分,可是彷佛不少厂商都尚未实现,可是IE11居然支持。
和预加载(preload、prefetch、dns-prefetch、preconnect 、prerender)")
讲了这么多,最后整理了一个表格,帮助你们快速查阅参考,每一个浏览器的实施细节都有所区别,这里以Chrome浏览器表格为例:
和预加载(preload、prefetch、dns-prefetch、preconnect 、prerender)")
参考:
[1] https://www.w3.org/TR/resource-hints/#prerender
[2] https://dev.chromium.org/developers/design-documents/dns-prefetching
[3] 资源优先级 – 让浏览器助您一臂之力
[4] JavaScript Loading Priorities in Chrome
[5] Chrome Resource Priorities and Scheduling
[6] Using JavaScript modules on the web
[7] https://www.w3.org/TR/html5/webappapis.html#module-script
原文出处:深度解析之异步加载(defer、async、module)和预加载(preload、prefetch、dns-prefetch、preconnect 、prerender)