廢話:異步加載和預加載一直都是前端優化必備技能之一,今天咱們就來深度解析一下經常使用的幾個關鍵點。
廢話很少說,任何長篇大論的教程都抵不過一張清晰明瞭的高清大圖來得好: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)