最近在研究 vue-cli 3.0生成的工程,在構建後生成的 index.html裏面發現了下面這種用法:css
<link as=style href=/css/app.f60416c7.css rel=preload>複製代碼
<link as=script href=/js/app.69189fdd.js rel=preload>複製代碼
這就觸到了本人的知識盲區了,本着掃盲的目的,研究了下 link 標籤,發現這個小東西功能仍是挺強大的,上面的就是爲了實現預加載功能,懂點兒英文的,一看見preload 就大體知道了。html
以前也有預加載技術,像 prefetch,subresource 等,關於這二者和 preload 的區別,這是另外的話題了, 感興趣的能夠本身搜一下,不想搜的,你只要知道這兩個跟 preload 相比弱的一逼就好了,就是 prefetch 瀏覽器兼容性方面稍微好一點點,這三個也各有偏重和應用場景,就不詳細介紹了,下面咱們就詳細展開preload 這塊。前端
preload 是一項新的 web 標準,旨在提升性能,讓 FE 對加載的控制更加粒度化。它讓開發者有自定義加載邏輯的能力,免受基於腳本的加載器所帶來的性能損耗。vue
preload 一個基本的用法就是提早加載資源,儘管大多數基於標記語言的資源能被瀏覽器的預加載器(preloader)儘早發現,但不是全部的資源都是基於標記語言的,好比一些隱藏在 css 和 js 中的資源(字體,圖片等),當瀏覽器發現頁面須要這些資源時,從新走一遍加載執行渲染的過程,會下降用戶體驗,而且對頁面的渲染 形成延遲;git
Preloader 簡介
HTML 解析器在建立 DOM 時若是碰上同步腳本(synchronous script),解析器會中止建立 DOM,轉而去執行腳本。因此,若是資源的獲取只發生在解析器建立 DOM時,同步腳本的介入將使網絡處於空置狀態,尤爲是對外部腳本資源來講,固然,頁面內的腳本有時也會致使延遲。
預加載器(Preloader)的出現就是爲了優化這個過程,預加載器經過分析瀏覽器對 HTML 文檔的早期解析結果(這一階段叫作「令牌化(tokenization)」),找到可能包含資源的標籤(tag),並將這些資源的 URL 收集起來。令牌化階段的輸出將會送到真正的 HTML 解析器手中,而收集起來的資源 URLs 會和資源類型一塊兒被送到讀取器(fetcher)手中,讀取器會根據這些資源對頁面加載速度的影響進行有次序地加載。
預加載的好處:github
屬性名web |
取值範圍vue-cli |
介紹json |
as跨域 |
script:js 腳本font:字體文件style:樣式表audio:音頻video: 視頻document:將被嵌入到<frame>或<iframe>元素內部的頁面image: 圖片fetch:將要經過 fetch 和 XHR 請求獲取的資源好比jsonobject: 將被嵌入到<embed >元素內的文件worker:js 的 web worker 或 share worker |
該屬性僅在 link 元素設置了rel=preload 是才能使用。規定了 link 元素要預加載的資源的類型,其取值範圍也限制了哪些資源纔可被預加載。設置了此屬性使瀏覽器可以:1,更精確地優化資源加載優先級。2,匹配將來的加載需求,在適當的狀況下,重複利用同一資源。3,爲資源應用正確的內容安全策略。4,爲資源設置正確的 Accept 請求頭。 |
href |
<url> |
指定要加載資源的 URL,可使絕對地址也能夠是相對地址 |
rel |
preload(當前功能相關) |
此屬性用於指明被連接的資源相對於當前頁面的關係。屬性值必定是被空格分開的連接類型值。這個屬性最多見的取值是:stylesheet,代表被鏈接資源對當前文檔來講是一個層疊樣式表。當前取值 preload 代表連接資源是一個預加載的資源 |
type |
MIME涵蓋類型 |
連接資源的 MIME 類型,在瀏覽器進行預加載到時候,這個屬性將會很是有用,瀏覽器將使用 type 屬性來判斷它是否支持這一資源類型,若是支持,將正常預加載,下載將開始,不然對其忽略。 |
crossorigin |
|
加載字體文件的時候須要用到,詳情往下看 |
<link>元素有一個很棒的特性是它們可以接受一個media屬性。它們能夠接受媒體類型或有效的媒體查詢做爲屬性值,這將令你可以使用響應式的預加載!
讓咱們來看一個簡單的示例(能夠查看Github上的源代碼或在線示例):
<head> <meta charset="utf-8"> <title>Responsive preload example</title> <link rel="preload" href="bg-image-narrow.png" as="image" media="(max-width: 600px)"> <link rel="preload" href="bg-image-wide.png" as="image" media="(min-width: 601px)"> <link rel="stylesheet" href="main.css"></head><body> <header> <h1>My site</h1> </header> <script> var mediaQueryList = window.matchMedia("(max-width: 600px)"); var header = document.querySelector('header'); if(mediaQueryList.matches) { header.style.backgroundImage = 'url(bg-image-narrow.png)'; } else { header.style.backgroundImage = 'url(bg-image-wide.png)'; } </script></body>複製代碼
你能夠看到咱們在<link>元素中包含了一個media屬性,所以,當用戶在使用較窄屏幕的設備時,較窄的圖片將會被預加載,而在較寬的設備上,較寬的圖片將被預加載。而後咱們仍須要在header元素上附加合適的圖片——經過Window.matchMedia / MediaQueryList 來加以實現(能夠查看Testing media queries一文來了解更多信息)。
web 字體是較晚才能被發現的關鍵資源中常見的一種。可是在用戶體驗對前端來講相當重要的現階段前端開發來講,web 字體對頁面的渲染也是相當重要。字體的引用被深埋在 css 中,即使預加載器有提早解析 css,也沒法肯定包含字體信息的選擇器是否會真正做用在 dom 節點上。因此爲了減小 FOUT(無樣式字體閃爍,flash of unstyled text )須要預加載字體文件,有了 preload,一行代碼搞定:
<link rel=preload href='font.woff2' as=font type='font/woff2' crossorigin />複製代碼
NOTE :
crossorigin 屬性在加載字體的時候是必須的,即使字體沒有跨域是在本身公司的服務器上,由於用戶代理必須採用匿名模式來獲取字體資源( 爲何會這樣呢?)。
type 屬性能夠確保瀏覽器只獲取本身支持的資源。
另一個有意思的場景也由於 preload 的出現變得可能——當你想加載某一資源但卻不想執行它。好比說,你想在頁面生命週期的某一時刻執行一段腳本,而你沒法對這段腳本作任何修改,不可能爲它建立一個所謂的 runNow()函數。
在 preload 出現以前,你能作的頗有限。若是你的方法是在但願腳本執行的位置插入腳本,因爲腳本只有在加載完成之後才能被瀏覽器執行,也就是說你得等上一下子。若是採用 XHR 提早加載腳本,瀏覽器會拒絕重用這段腳本,有些狀況下,你可使用 eval 函數來執行這段腳本,但該方法並不老是行得通,也不是徹底沒有反作用。
如今有了 preload,一切變得可能
var link = document.createElement("link");複製代碼
link.href = "myscript.js";複製代碼
link.rel = "preload";複製代碼
link.as = "script";複製代碼
document.head.appendChild(link);複製代碼
上面這段代碼可讓你預先加載腳本,下面這段代碼可讓腳本執行
var script = document.createElement("script");複製代碼
script.src = "myscript.js";複製代碼
document.body.appendChild(script);
複製代碼
先看代碼
<link rel="preload" as="style" href="asyncstyle.css" onload="this.rel='stylesheet'">複製代碼
preload 的 onload 事件能夠在資源加載完成後修改 rel 屬性,從而實現很是酷的異步資源加載。
腳本也能夠採用這種方法實現異步加載
難道咱們不是已經有了<script async>? <scirpt async>雖好,但卻會阻塞 window 的 onload 事件。某些狀況下,你可能但願這樣,但總有一些狀況你不但願阻塞 window 的 onload 。
舉個例子,你想盡量快的加載一段統計頁面訪問量的代碼,但又不肯意這段代碼的加載給頁面渲染形成延遲從而影響用戶體驗,關鍵是,你不想延遲 window 的 onload 事件。
有了preload, 分分鐘搞定。
<link rel="preload" as="script" href="async_script.js"複製代碼
onload="var script = document.createElement('script'); script.src = this.href; document.body.appendChild(script);">複製代碼
preload 是一個link,根據規範有一個media 屬性(如今 Chrome 還不支持,不過快了),該屬性使得選擇性加載成爲可能。
有什麼用處呢?假設你的站點同時支持桌面和移動端的訪問,在使用桌面瀏覽器訪問時,你但願呈現一張可交互的大地圖,而在移動端,一張較小的靜態地圖就足夠了。
你確定不想同時加載兩個資源,如今常見的作法是經過 JS 判斷當前瀏覽器類型動態地加載資源,但這樣一來,瀏覽器的預加載器就沒法及時發現他們,可能耽誤加載時機,影響用戶體驗和 SpeedIndex 評分。
怎樣才能讓瀏覽器儘量早的發現這些資源呢?仍是 Preload!
經過 Preload,咱們能夠提早加載資源,利用 media 屬性,瀏覽器只會加載須要的資源。
<link rel="preload" as="image" href="map.png" media="(max-width: 600px)">複製代碼
<link rel="preload" as="script" href="map.js" media="(min-width: 601px)">複製代碼
Preload 還有一個特性是其能夠經過 HTTP 頭信息被呈現。也就是說上文中大多數的基於標記語言的聲明能夠經過 HTTP 響應頭實現。(惟一的例外是有 onload 事件的例子,咱們不可能在 HTTP 頭信息中定義事件處理函數。)
Link: <thing_to_load.js>;rel="preload";as="script"複製代碼
Link: <thing_to_load.woff2>;rel="preload";as="font";crossorigin複製代碼
這一方式在有些場景尤爲有用,好比,當負責優化的人員與頁面開發人員不是同一人時(也就是說優化人員可能沒法或者不想修改頁面代碼),還有一個傑出的例子是外部優化引擎(External optimization engine),該引擎對內容進行掃描並優化。
前面全部的列子都基於一種假設——瀏覽器必定程度上支持 preload,至少實現了腳本和樣式加載等基本功能。但若是這個假設不成立了。一切都將是然並卵。
爲了判斷瀏覽器是否支持 preload,咱們修改了 DOM 的規範從而可以獲知 rel 支持那些值(是否支持 rel=‘preload’)。
至於如何進行檢查,原文中沒有,但 Github有一段代碼可供參考。
var DOMTokenListSupports = function(tokenList, token) {複製代碼
if (!tokenList || !tokenList.supports) {複製代碼
return;複製代碼
}複製代碼
try {複製代碼
return tokenList.supports(token);複製代碼
} catch (e) {複製代碼
if (e instanceof TypeError) {複製代碼
console.log("The DOMTokenList doesn't have a supported tokens list");複製代碼
} else {複製代碼
console.error("That shouldn't have happened");複製代碼
}複製代碼
}複製代碼
};複製代碼
複製代碼
var linkSupportsPreload = DOMTokenListSupports(document.createElement("link").relList, "preload");複製代碼
if (!linkSupportsPreload) {複製代碼
// Dynamically load the things that relied on preload.複製代碼
}複製代碼
caniuse.com 網站上顯示瀏覽器版本支持狀況以下,目前仍是比較高版本的瀏覽器會支持此功能,不過你們也不要擔憂,在不支持的瀏覽器環境中,這部分標籤會被忽略,能夠作到平穩降級。