摘要:當(dāng)然并不是所有的頁面需要用到的外部域名都需要做這樣的域名解析,瀏覽器默認(rèn)會(huì)解析超鏈接屬性的里面的域名,并且你的網(wǎng)站域名還不能是,如果是,則需要設(shè)置請(qǐng)求頭或加入一段強(qiáng)制開啟域名解析的標(biāo)簽。
廢話:異步加載和預(yù)加載一直都是前端優(yōu)化必備技能之一,今天我們就來深度解析一下常用的幾個(gè)關(guān)鍵點(diǎn)。異步加載
廢話不多說,任何長篇大論的教程都抵不過一張清晰明了的高清大圖來得好:
和預(yù)加載(preload、prefetch、dns-prefetch、preconnect 、prerender)")
從這張圖里面,我們看到了什么,大概總結(jié)為以下四點(diǎn):
默認(rèn)情況HTML解析,然后加載JS,此時(shí)HTML解析中斷,然后執(zhí)行JS,最后JS執(zhí)行完成恢復(fù)HTML解析。
defer情況下HTML和JS并駕齊驅(qū),最后才執(zhí)行JS
async情況則HTML和JS并駕齊驅(qū),JS的執(zhí)行可能在HTML解析之前就已經(jīng)完成了
最后module情況和defer的情況類似,只不過會(huì)在提取的過程中加載多個(gè)JS文件罷了
好了,區(qū)分的大概基本已經(jīng)了解了,那怎么記住呢?默認(rèn)的情況我們已經(jīng)很熟了,就無需多記了。
defer翻譯過來是延緩的意思,也就是拖拖拉拉了,所以比較懶,也就是說什么都不想做,也就是哪怕你把飯端在我面前,我也懶得動(dòng)嘴的那種,這么一想,我們不就記住了,哪怕你客戶端把JS文件下載好了,我也懶得執(zhí)行,最后實(shí)在是大家都干完事了,我才不情愿的去執(zhí)行JS文件。
async翻譯過來就是異步的意思,異步異步,不就是一步一步嘛,什么都想一步到位,也就是說,只要下載完我就立馬執(zhí)行,至于其他的想都不想。
module翻譯過來就是模塊的意思,es6用過的人基本都了解這個(gè)關(guān)鍵字,加載也和defer差不多,只不過可以加載多個(gè)JS文件而已。
我們?cè)賮砜纯催@幾個(gè)加載的DOM事件時(shí)機(jī):
和預(yù)加載(preload、prefetch、dns-prefetch、preconnect 、prerender)")
從這張圖可以看出大概這幾點(diǎn):
async 會(huì)在加載完JS后立即執(zhí)行,最遲也會(huì)在load事件前執(zhí)行完。
defer會(huì)在HTML解析完成后執(zhí)行,最遲也會(huì)在DOMContentLoaded事件前執(zhí)行完。
從上面我們可以看出,如果你的腳本依賴于DOM構(gòu)建完成是否完成,則可以使用defer;如果無需DOM的構(gòu)建,那就可以放心的使用async了。
deferdefer屬性僅適用于外部腳本,也就是僅當(dāng)存在src屬性時(shí)才會(huì)生效;如果一個(gè)script標(biāo)簽上面即存在defer屬性,也存在async屬性,那么瀏覽器會(huì)如何解析這種情況呢?我們通過一段代碼驗(yàn)證結(jié)果,詳情點(diǎn)擊這里。
也就是說defer的優(yōu)先級(jí)沒有async高,我們看一下規(guī)范是怎么處理這種情況的。
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.
規(guī)范只是說明了在不支持async的情況下瀏覽器將會(huì)回退支持defer,但并沒有明確指明兩種都支持的這種情況,也就是說這一種情況瀏覽器自行處理,經(jīng)過測試,各個(gè)瀏覽器表現(xiàn)行為:
Chrome瀏覽器表現(xiàn)為解析為async特性
Safari瀏覽器表現(xiàn)為async特性
Opera瀏覽器表現(xiàn)為async特性
Firefox瀏覽器表現(xiàn)為async特性
IE暫時(shí)沒有安裝,看來各大瀏覽器表現(xiàn)一致,總之a(chǎn)sync的優(yōu)先級(jí)是最高的。
兼容性下面來看看defer的兼容性,移動(dòng)端一片大綠,可以放心使用,IE10以上可以放心使用,IE6-9有一點(diǎn)小問題就是不會(huì)按照script標(biāo)簽的執(zhí)行順序進(jìn)行執(zhí)行,對(duì)于不依賴前后腳本庫的可以不用擔(dān)心,但是如果依賴庫的就不行了,比如你的項(xiàng)目依賴jQuery,后面緊接著使用jQuery的方法可能就會(huì)出現(xiàn)問題。
和預(yù)加載(preload、prefetch、dns-prefetch、preconnect 、prerender)")
async和defer一樣,也僅僅適用于外部腳本,也就是僅當(dāng)存在src屬性時(shí)才會(huì)生效。
兼容性async的兼容性在移動(dòng)端也是一片大綠,IE僅支持IE10+。
和預(yù)加載(preload、prefetch、dns-prefetch、preconnect 、prerender)")
module在現(xiàn)代瀏覽器中,我們可以聲明acript標(biāo)簽type=’module’屬性從而擁抱es6的模塊導(dǎo)入導(dǎo)出語法,就像這樣:
看起來是不是令人很激動(dòng),似乎對(duì)于開發(fā)者十分友好,但是這里也有幾個(gè)與傳統(tǒng)腳本不一樣的地方:
module默認(rèn)使用了”use strict”模式,這也意味著不能使用諸如arguments.callee這一類的語法。
模塊只會(huì)加載一次,無論前后你寫了多少次。
不支持注釋。
module有自己的詞法作用域,比如定義一個(gè) var a = 1,并不會(huì)創(chuàng)建一個(gè)全局變量,因此你并不能通過window.a 訪問到它的值。
模塊的導(dǎo)入方式目前僅支持以下幾種模式:
支持 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";
當(dāng)然,瀏覽器廠商也在考慮支持 import {math} from ‘jquery’ 這種格式,不過,還是需要一段很長的路要走。
module的默認(rèn)情況就是defer的,因此不必再module上面又添加一個(gè)defer熟悉,并且本身就不支持這種寫法,但是支持async屬性,其加載渲染方式和async差不多,這里不再贅述。
兼容性在移動(dòng)端的兼容性還算可以,但是IE貌似都敗下陣來,只要edge16+以上還算支持,對(duì)于不支持module的瀏覽器可以使用nomodule屬性作為版本回退的方案解決。
和預(yù)加載(preload、prefetch、dns-prefetch、preconnect 、prerender)")
最后來說一下module的使用建議,大型項(xiàng)目(100模塊以上)不建議直接使用模塊語法,應(yīng)該使用打包工具諸如Webpack,Rollup,、或 Parcel,因?yàn)殪o態(tài)導(dǎo)入或?qū)С稣Z法是靜態(tài)可分析的,通過捆綁工具可以去掉多余的模塊,我們考慮下面這一種場景:
import { Modal } from "./util.js"; Modal({ title: "hello" })
如果我們通過打包工具打包這一份代碼,最終生成的JS文件將會(huì)只包含Modal這一個(gè)函數(shù),倘若我們沒有使用打包工具,瀏覽器將會(huì)下載整個(gè)util這一個(gè)JS文件,并通過進(jìn)一步分析了解了使用了Modal這一個(gè)函數(shù),這對(duì)于沒有用到util里面的全部函數(shù)的方式,則是一種多余的帶寬浪費(fèi)。
預(yù)加載在我們的瀏覽器加載資源的時(shí)候,對(duì)于每一個(gè)資源都有其自身的默認(rèn)優(yōu)先級(jí),倘若我們能修改每一個(gè)資源的默認(rèn)優(yōu)先級(jí),那我們幾乎可以按照我們的預(yù)期加載想要加載的資源。
以谷歌瀏覽器為例,我們打開控制臺(tái),并切換到Network選項(xiàng),點(diǎn)擊刷新頁面,在網(wǎng)絡(luò)下面的title一行點(diǎn)擊鼠標(biāo)右鍵,勾選Priority即可看到加載資源的優(yōu)先級(jí),我們可以看到樣式的級(jí)別比腳本的優(yōu)先級(jí)高,畢竟頁面的一加載進(jìn)來肯定是樣式首先需要渲染的,不然整個(gè)頁面便會(huì)四分五裂,用戶體驗(yàn)不好。
和預(yù)加載(preload、prefetch、dns-prefetch、preconnect 、prerender)")
preloadpreload翻譯過來就是預(yù)加載,一旦啟用后便會(huì)告知瀏覽器應(yīng)該盡快的加載某個(gè)資源,如果提取的資源3s內(nèi)未在當(dāng)前使用,在谷歌開發(fā)工具將會(huì)觸發(fā)警告消息
和預(yù)加載(preload、prefetch、dns-prefetch、preconnect 、prerender)")
大概的語法如下:
除了以上指定的資源外,還可以加載audio、font、video以及document等,詳情點(diǎn)擊這里了解。
跨域資源如需加載跨域的資源列表,則需要正確設(shè)置CORS,接著便可以在元素中設(shè)置好crossorigin屬性即可:
這里有一個(gè)特例便是無論是否跨域,字體的獲取都需要設(shè)置crossorigin屬性,這是由于歷史原因造成,有興趣了解可移步這里了解,另外我們還可以使用media響應(yīng)式的加載圖片,比如:
另一個(gè)重要的地方便是如果預(yù)加載一個(gè)腳本,它并不是執(zhí)行:
//只拉取下載不執(zhí)行 var preloadLink = document.createElement("link"); preloadLink.href = "foo.js"; preloadLink.rel = "preload"; preloadLink.as = "script"; document.head.appendChild(preloadLink); //如果需要執(zhí)行 var preloadedScript = document.createElement("script"); preloadedScript.src = "foo.js"; document.body.appendChild(preloadedScript);兼容性
兼容似乎IE全體陣亡,edge也得17+才能勉強(qiáng)支持,火狐需要手動(dòng)啟動(dòng)支持,移動(dòng)端支持程度還是挺好的。
和預(yù)加載(preload、prefetch、dns-prefetch、preconnect 、prerender)")
prefetch簡而言之預(yù)提取就是在我們頁面加載完成后,在帶寬可用的情況下,加載用戶下一步期待的頁面資源,比如企業(yè)認(rèn)證,一般都是分好幾個(gè)頁面進(jìn)行認(rèn)證的,在用戶從第一個(gè)頁面進(jìn)行認(rèn)證的時(shí)候,在頁面加載完成,用戶正在填寫表單數(shù)據(jù)之時(shí),加載第二個(gè)頁面的部分資源,從而使用戶更快打開下一個(gè)頁面,從而增加用戶體驗(yàn),示例:
當(dāng)瀏覽器解析到link標(biāo)簽時(shí),讀取到rel的值為prefetch,便會(huì)將這一個(gè)資源添加的隊(duì)列中,當(dāng)瀏覽器空閑時(shí)便會(huì)預(yù)提取資源,但是在demo.html頁面中只是加載HTML,不會(huì)加載demo頁面里面的任何其他資源,除非你在demo頁面也明確使用了預(yù)提取。
兼容性各大瀏覽器支持都還挺好,IE11+以上,但是Safari貌似到現(xiàn)在還沒支持。
dns-prefetch我們都知道,當(dāng)我們?cè)跒g覽器的地址欄輸入域名的時(shí)候,首先要進(jìn)行的就是域名解析,因?yàn)槲覀冃枰虞d域名對(duì)應(yīng)的資源,這個(gè)過程很快,但是如果在移動(dòng)端,那可是一個(gè)分秒必爭的地方,當(dāng)一個(gè)頁面需要訪問許多外部域名的資源的時(shí)候,如果我們能在用戶瀏覽頁面的時(shí)候,在瀏覽器空閑的時(shí)間,把可能需要訪問的域名都提前做好了域名解析,那是不是大大增加了用戶打開頁面的響應(yīng)時(shí)間,增加用戶體驗(yàn),為了解決這個(gè)問題,w3c便提出來一個(gè)標(biāo)準(zhǔn),學(xué)名叫dns-prefetch。
和預(yù)加載(preload、prefetch、dns-prefetch、preconnect 、prerender)")
使用方法上面中已經(jīng)支持了,指定rel=”dns-prefetch”,在href中指定頁面需要解析的域名即可,你可能已經(jīng)注意到了上面的圖中域名使用了雙斜杠,這個(gè)雙斜杠表示URL以主機(jī)名開頭,和你使用完整URL(比如http://g.alicdn.com/)是等效的。在RFC1808中被指定。
當(dāng)然并不是所有的頁面需要用到的外部域名都需要做這樣的域名解析,瀏覽器默認(rèn)會(huì)解析超鏈接屬性的href里面的域名,并且你的網(wǎng)站域名還不能是HTTPS,如果是HTTPS,則需要設(shè)置請(qǐng)求頭或加入一段強(qiáng)制開啟域名解析的meta標(biāo)簽。
//HTTP //多余 //HTTPS //強(qiáng)制開啟
當(dāng)然,并不建議對(duì)HTTPS網(wǎng)站開啟強(qiáng)制解析的方式,因?yàn)檫@樣會(huì)帶來一些安全隱患,具體可參考這里。
preconnect預(yù)連接,也就是啟動(dòng)早期連接(包括DNS查找,TCP握手和可選TLS協(xié)商),我們來看一個(gè)例子:
一個(gè)網(wǎng)絡(luò)字體正常加載一般都包括:
頁面加載樣式,解析樣式用到的網(wǎng)絡(luò)字體
網(wǎng)絡(luò)字體開始下載,首先開始DNS的查找
然后TCP握手
如果是HTTPS,還有TLS協(xié)商,最后下載字體
差不多一個(gè)字體的渲染要經(jīng)過這么幾個(gè)過程,但是如果字體的前期準(zhǔn)備(DNS查找,TCP握手和可選TLS協(xié)商)和樣式的加載是并行執(zhí)行,是不是可以更快的渲染頁面,preconnect就是為這個(gè)而生的,從而優(yōu)化用戶體驗(yàn)。
當(dāng)然如果是跨域資源,不要忘了加上crossorigin屬性。
兼容性IE15+以上部分兼容,移動(dòng)端兼容良好。
和預(yù)加載(preload、prefetch、dns-prefetch、preconnect 、prerender)")
prerender預(yù)渲染,簡單來說就是瀏覽器會(huì)下載指定鏈接的資源,并下載以及渲染它,就好比我們打開了一個(gè)新的Tab標(biāo)簽頁,靜默的在后臺(tái)的下載執(zhí)行,當(dāng)然,瀏覽器也不一定會(huì)下載渲染它,這取決預(yù)很多情況,比如瀏覽器是否空閑以及操作系統(tǒng)是否會(huì)放棄下載過慢的資源文件。
除非你真的能十分的肯定用戶接下來一定會(huì)觸發(fā)你所指定的資源地址,否則對(duì)于用戶來說這是一種帶寬的浪費(fèi),使用例子如下:
兼容性雖然是prerender是HTML5規(guī)范的一部分,但是似乎很多廠商都還沒有實(shí)現(xiàn),但是IE11竟然支持。
和預(yù)加載(preload、prefetch、dns-prefetch、preconnect 、prerender)")
結(jié)尾講了這么多,最后整理了一個(gè)表格,幫助大家快速查閱參考,每個(gè)瀏覽器的實(shí)施細(xì)節(jié)都有所區(qū)別,這里以Chrome瀏覽器表格為例:
和預(yù)加載(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]?資源優(yōu)先級(jí) – 讓瀏覽器助您一臂之力
[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)和預(yù)加載(preload、prefetch、dns-prefetch、preconnect 、prerender)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/54787.html
摘要:當(dāng)然并不是所有的頁面需要用到的外部域名都需要做這樣的域名解析,瀏覽器默認(rèn)會(huì)解析超鏈接屬性的里面的域名,并且你的網(wǎng)站域名還不能是,如果是,則需要設(shè)置請(qǐng)求頭或加入一段強(qiáng)制開啟域名解析的標(biāo)簽。 廢話:異步加載和預(yù)加載一直都是前端優(yōu)化必備技能之一,今天我們就來深度解析一下常用的幾個(gè)關(guān)鍵點(diǎn)。 異步加載 廢話不多說,任何長篇大論的教程都抵不過一張清晰明了的高清大圖來得好: showImg(http...
摘要:模塊化主要是用來抽離公共代碼,隔離作用域,避免變量沖突等。將一個(gè)復(fù)雜的系統(tǒng)分解為多個(gè)模塊以方便編碼。順手寫一個(gè)省略省略實(shí)現(xiàn)此時(shí)的對(duì)應(yīng)的形式解析省略執(zhí)行兼容,模塊化語法。 模塊化主要是用來抽離公共代碼,隔離作用域,避免變量沖突等。將一個(gè)復(fù)雜的系統(tǒng)分解為多個(gè)模塊以方便編碼。 會(huì)講述以下內(nèi)容 CommonJS AMD 及 核心原理實(shí)現(xiàn) CMD 及 核心原理實(shí)現(xiàn) UMD 及 源碼解析 ES6...
摘要:如果對(duì)語法分析和預(yù)編譯,還有疑問引擎執(zhí)行的過程的理解語法分析和預(yù)編譯階段。參與執(zhí)行過程的線程分別是引擎線程也稱為內(nèi)核,負(fù)責(zé)解析執(zhí)行腳本程序的主線程例如引擎。以上便是引擎執(zhí)行宏任務(wù)的整個(gè)過程。 一、概述 js引擎執(zhí)行過程主要分為三個(gè)階段,分別是語法分析,預(yù)編譯和執(zhí)行階段,上篇文章我們介紹了語法分析和預(yù)編譯階段,那么我們先做個(gè)簡單概括,如下: 1、語法分析: 分別對(duì)加載完成的代碼塊進(jìn)行語法...
摘要:如果對(duì)語法分析和預(yù)編譯,還有疑問引擎執(zhí)行的過程的理解語法分析和預(yù)編譯階段。參與執(zhí)行過程的線程分別是引擎線程也稱為內(nèi)核,負(fù)責(zé)解析執(zhí)行腳本程序的主線程例如引擎。以上便是引擎執(zhí)行宏任務(wù)的整個(gè)過程。一、概述 js引擎執(zhí)行過程主要分為三個(gè)階段,分別是語法分析,預(yù)編譯和執(zhí)行階段,上篇文章我們介紹了語法分析和預(yù)編譯階段,那么我們先做個(gè)簡單概括,如下: 1、語法分析: 分別對(duì)加載完成的代碼塊進(jìn)行語法檢驗(yàn),語...
摘要:所以覺得把這個(gè)執(zhí)行的詳細(xì)過程整理一下,幫助更好的理解。類似的語法報(bào)錯(cuò)的如下圖所示三預(yù)編譯階段代碼塊通過語法分析階段之后,語法都正確的下回進(jìn)入預(yù)編譯階段。另開出新文章詳細(xì)分析,主要介紹執(zhí)行階段中的同步任務(wù)執(zhí)行和異步任務(wù)執(zhí)行機(jī)制事件循環(huán)。 一、概述 js是一種非常靈活的語言,理解js引擎的執(zhí)行過程對(duì)于我們學(xué)習(xí)js是非常有必要的??戳撕芏噙@方便文章,大多數(shù)是講的是事件循環(huán)(event loo...
閱讀 3007·2023-04-26 02:22
閱讀 2355·2021-11-17 09:33
閱讀 3251·2021-09-22 16:06
閱讀 1164·2021-09-22 15:54
閱讀 3599·2019-08-29 13:44
閱讀 2020·2019-08-29 12:37
閱讀 1374·2019-08-26 14:04
閱讀 1976·2019-08-26 11:57