摘要:當然并不是所有的頁面需要用到的外部域名都需要做這樣的域名解析,瀏覽器默認會解析超鏈接屬性的里面的域名,并且你的網站域名還不能是,如果是,則需要設置請求頭或加入一段強制開啟域名解析的標簽。
廢話:異步加載和預加載一直都是前端優化必備技能之一,今天我們就來深度解析一下常用的幾個關鍵點。異步加載
廢話不多說,任何長篇大論的教程都抵不過一張清晰明了的高清大圖來得好:
和預加載(preload、prefetch、dns-prefetch、preconnect 、prerender)")
從這張圖里面,我們看到了什么,大概總結為以下四點:
默認情況HTML解析,然后加載JS,此時HTML解析中斷,然后執行JS,最后JS執行完成恢復HTML解析。
defer情況下HTML和JS并駕齊驅,最后才執行JS
async情況則HTML和JS并駕齊驅,JS的執行可能在HTML解析之前就已經完成了
最后module情況和defer的情況類似,只不過會在提取的過程中加載多個JS文件罷了
好了,區分的大概基本已經了解了,那怎么記住呢?默認的情況我們已經很熟了,就無需多記了。
defer翻譯過來是延緩的意思,也就是拖拖拉拉了,所以比較懶,也就是說什么都不想做,也就是哪怕你把飯端在我面前,我也懶得動嘴的那種,這么一想,我們不就記住了,哪怕你客戶端把JS文件下載好了,我也懶得執行,最后實在是大家都干完事了,我才不情愿的去執行JS文件。
async翻譯過來就是異步的意思,異步異步,不就是一步一步嘛,什么都想一步到位,也就是說,只要下載完我就立馬執行,至于其他的想都不想。
module翻譯過來就是模塊的意思,es6用過的人基本都了解這個關鍵字,加載也和defer差不多,只不過可以加載多個JS文件而已。
我們再來看看這幾個加載的DOM事件時機:
和預加載(preload、prefetch、dns-prefetch、preconnect 、prerender)")
從這張圖可以看出大概這幾點:
async 會在加載完JS后立即執行,最遲也會在load事件前執行完。
defer會在HTML解析完成后執行,最遲也會在DOMContentLoaded事件前執行完。
從上面我們可以看出,如果你的腳本依賴于DOM構建完成是否完成,則可以使用defer;如果無需DOM的構建,那就可以放心的使用async了。
deferdefer屬性僅適用于外部腳本,也就是僅當存在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,但并沒有明確指明兩種都支持的這種情況,也就是說這一種情況瀏覽器自行處理,經過測試,各個瀏覽器表現行為:
Chrome瀏覽器表現為解析為async特性
Safari瀏覽器表現為async特性
Opera瀏覽器表現為async特性
Firefox瀏覽器表現為async特性
IE暫時沒有安裝,看來各大瀏覽器表現一致,總之async的優先級是最高的。
兼容性下面來看看defer的兼容性,移動端一片大綠,可以放心使用,IE10以上可以放心使用,IE6-9有一點小問題就是不會按照script標簽的執行順序進行執行,對于不依賴前后腳本庫的可以不用擔心,但是如果依賴庫的就不行了,比如你的項目依賴jQuery,后面緊接著使用jQuery的方法可能就會出現問題。
和預加載(preload、prefetch、dns-prefetch、preconnect 、prerender)")
async和defer一樣,也僅僅適用于外部腳本,也就是僅當存在src屬性時才會生效。
兼容性async的兼容性在移動端也是一片大綠,IE僅支持IE10+。
和預加載(preload、prefetch、dns-prefetch、preconnect 、prerender)")
module在現代瀏覽器中,我們可以聲明acript標簽type=’module’屬性從而擁抱es6的模塊導入導出語法,就像這樣:
看起來是不是令人很激動,似乎對于開發者十分友好,但是這里也有幾個與傳統腳本不一樣的地方:
module默認使用了”use strict”模式,這也意味著不能使用諸如arguments.callee這一類的語法。
模塊只會加載一次,無論前后你寫了多少次。
不支持注釋。
module有自己的詞法作用域,比如定義一個 var a = 1,并不會創建一個全局變量,因此你并不能通過window.a 訪問到它的值。
模塊的導入方式目前僅支持以下幾種模式:
支持 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)")
preloadpreload翻譯過來就是預加載,一旦啟用后便會告知瀏覽器應該盡快的加載某個資源,如果提取的資源3s內未在當前使用,在谷歌開發工具將會觸發警告消息
和預加載(preload、prefetch、dns-prefetch、preconnect 、prerender)")
大概的語法如下:
除了以上指定的資源外,還可以加載audio、font、video以及document等,詳情點擊這里了解。
跨域資源如需加載跨域的資源列表,則需要正確設置CORS,接著便可以在元素中設置好crossorigin屬性即可:
這里有一個特例便是無論是否跨域,字體的獲取都需要設置crossorigin屬性,這是由于歷史原因造成,有興趣了解可移步這里了解,另外我們還可以使用media響應式的加載圖片,比如:
另一個重要的地方便是如果預加載一個腳本,它并不是執行:
//只拉取下載不執行 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)")
prefetch簡而言之預提取就是在我們頁面加載完成后,在帶寬可用的情況下,加載用戶下一步期待的頁面資源,比如企業認證,一般都是分好幾個頁面進行認證的,在用戶從第一個頁面進行認證的時候,在頁面加載完成,用戶正在填寫表單數據之時,加載第二個頁面的部分資源,從而使用戶更快打開下一個頁面,從而增加用戶體驗,示例:
當瀏覽器解析到link標簽時,讀取到rel的值為prefetch,便會將這一個資源添加的隊列中,當瀏覽器空閑時便會預提取資源,但是在demo.html頁面中只是加載HTML,不會加載demo頁面里面的任何其他資源,除非你在demo頁面也明確使用了預提取。
兼容性各大瀏覽器支持都還挺好,IE11+以上,但是Safari貌似到現在還沒支持。
dns-prefetch我們都知道,當我們在瀏覽器的地址欄輸入域名的時候,首先要進行的就是域名解析,因為我們需要加載域名對應的資源,這個過程很快,但是如果在移動端,那可是一個分秒必爭的地方,當一個頁面需要訪問許多外部域名的資源的時候,如果我們能在用戶瀏覽頁面的時候,在瀏覽器空閑的時間,把可能需要訪問的域名都提前做好了域名解析,那是不是大大增加了用戶打開頁面的響應時間,增加用戶體驗,為了解決這個問題,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 //多余 //HTTPS //強制開啟
當然,并不建議對HTTPS網站開啟強制解析的方式,因為這樣會帶來一些安全隱患,具體可參考這里。
preconnect預連接,也就是啟動早期連接(包括DNS查找,TCP握手和可選TLS協商),我們來看一個例子:
一個網絡字體正常加載一般都包括:
頁面加載樣式,解析樣式用到的網絡字體
網絡字體開始下載,首先開始DNS的查找
然后TCP握手
如果是HTTPS,還有TLS協商,最后下載字體
差不多一個字體的渲染要經過這么幾個過程,但是如果字體的前期準備(DNS查找,TCP握手和可選TLS協商)和樣式的加載是并行執行,是不是可以更快的渲染頁面,preconnect就是為這個而生的,從而優化用戶體驗。
當然如果是跨域資源,不要忘了加上crossorigin屬性。
兼容性IE15+以上部分兼容,移動端兼容良好。
和預加載(preload、prefetch、dns-prefetch、preconnect 、prerender)")
prerender預渲染,簡單來說就是瀏覽器會下載指定鏈接的資源,并下載以及渲染它,就好比我們打開了一個新的Tab標簽頁,靜默的在后臺的下載執行,當然,瀏覽器也不一定會下載渲染它,這取決預很多情況,比如瀏覽器是否空閑以及操作系統是否會放棄下載過慢的資源文件。
除非你真的能十分的肯定用戶接下來一定會觸發你所指定的資源地址,否則對于用戶來說這是一種帶寬的浪費,使用例子如下:
兼容性雖然是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)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/109336.html
摘要:當然并不是所有的頁面需要用到的外部域名都需要做這樣的域名解析,瀏覽器默認會解析超鏈接屬性的里面的域名,并且你的網站域名還不能是,如果是,則需要設置請求頭或加入一段強制開啟域名解析的標簽。 廢話:異步加載和預加載一直都是前端優化必備技能之一,今天我們就來深度解析一下常用的幾個關鍵點。 異步加載 廢話不多說,任何長篇大論的教程都抵不過一張清晰明了的高清大圖來得好: showImg(http...
摘要:模塊化主要是用來抽離公共代碼,隔離作用域,避免變量沖突等。將一個復雜的系統分解為多個模塊以方便編碼。順手寫一個省略省略實現此時的對應的形式解析省略執行兼容,模塊化語法。 模塊化主要是用來抽離公共代碼,隔離作用域,避免變量沖突等。將一個復雜的系統分解為多個模塊以方便編碼。 會講述以下內容 CommonJS AMD 及 核心原理實現 CMD 及 核心原理實現 UMD 及 源碼解析 ES6...
摘要:如果對語法分析和預編譯,還有疑問引擎執行的過程的理解語法分析和預編譯階段。參與執行過程的線程分別是引擎線程也稱為內核,負責解析執行腳本程序的主線程例如引擎。以上便是引擎執行宏任務的整個過程。 一、概述 js引擎執行過程主要分為三個階段,分別是語法分析,預編譯和執行階段,上篇文章我們介紹了語法分析和預編譯階段,那么我們先做個簡單概括,如下: 1、語法分析: 分別對加載完成的代碼塊進行語法...
摘要:如果對語法分析和預編譯,還有疑問引擎執行的過程的理解語法分析和預編譯階段。參與執行過程的線程分別是引擎線程也稱為內核,負責解析執行腳本程序的主線程例如引擎。以上便是引擎執行宏任務的整個過程。一、概述 js引擎執行過程主要分為三個階段,分別是語法分析,預編譯和執行階段,上篇文章我們介紹了語法分析和預編譯階段,那么我們先做個簡單概括,如下: 1、語法分析: 分別對加載完成的代碼塊進行語法檢驗,語...
摘要:所以覺得把這個執行的詳細過程整理一下,幫助更好的理解。類似的語法報錯的如下圖所示三預編譯階段代碼塊通過語法分析階段之后,語法都正確的下回進入預編譯階段。另開出新文章詳細分析,主要介紹執行階段中的同步任務執行和異步任務執行機制事件循環。 一、概述 js是一種非常靈活的語言,理解js引擎的執行過程對于我們學習js是非常有必要的。看了很多這方便文章,大多數是講的是事件循環(event loo...
閱讀 1213·2021-09-26 09:55
閱讀 3158·2019-08-30 15:55
閱讀 948·2019-08-30 15:53
閱讀 2285·2019-08-30 13:59
閱讀 2367·2019-08-29 13:08
閱讀 1098·2019-08-29 12:19
閱讀 3290·2019-08-26 13:41
閱讀 411·2019-08-26 13:24