Skripty:async, defer

Na moderních webových stránkách jsou skripty často „těžší“ než HTML:jejich velikost ke stažení je větší a doba zpracování je také delší.

Když prohlížeč načte HTML a narazí na <script>...</script> tag, nemůže pokračovat ve vytváření DOM. Musí spustit skript právě teď. Totéž platí pro externí skripty <script src="..."></script> :prohlížeč musí počkat na stažení skriptu, spustit stažený skript a teprve poté může zpracovat zbytek stránky.

To vede ke dvěma důležitým problémům:

  1. Skripty nevidí pod sebou prvky DOM, takže nemohou přidávat obslužné nástroje atd.
  2. Pokud je v horní části stránky objemný skript, „blokuje stránku“. Uživatelé neuvidí obsah stránky, dokud se nestáhne a nespustí:
<p>...content before script...</p>

<script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>

<!-- This isn't visible until the script loads -->
<p>...content after script...</p>

Existují pro to určitá řešení. Můžeme například umístit skript na konec stránky. Pak může vidět prvky nad ním a neblokuje zobrazení obsahu stránky:

<body>
 ...all content is above the script...

 <script src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>
</body>

Toto řešení má ale k dokonalosti daleko. Prohlížeč si například všimne skriptu (a může jej začít stahovat) až poté, co stáhne celý dokument HTML. U dlouhých HTML dokumentů to může být znatelné zpoždění.

Takové věci jsou pro lidi používající velmi rychlé připojení neviditelné, ale mnoho lidí na světě má stále pomalé internetové připojení a používá zdaleka ne dokonalé mobilní internetové připojení.

Naštěstí existují dva <script> atributy, které za nás problém vyřeší:defer a async .

odložit

defer atribut říká prohlížeči, aby nečekal na skript. Místo toho bude prohlížeč nadále zpracovávat HTML, vytvářet DOM. Skript se načte „na pozadí“ a poté se spustí, když je DOM plně sestaven.

Zde je stejný příklad jako výše, ale s defer :

<p>...content before script...</p>

<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>

<!-- visible immediately -->
<p>...content after script...</p>

Jinými slovy:

  • Skripty s kódem defer stránku nikdy neblokujte.
  • Skripty s kódem defer vždy spustit, když je DOM připraven (ale před DOMContentLoaded událost).

Následující příklad ukazuje druhou část:

<p>...content before scripts...</p>

<script>
 document.addEventListener('DOMContentLoaded', () => alert("DOM ready after defer!"));
</script>

<script defer src="https://javascript.info/article/script-async-defer/long.js?speed=1"></script>

<p>...content after scripts...</p>
  1. Obsah stránky se zobrazí okamžitě.
  2. DOMContentLoaded obsluha události čeká na odložený skript. Spustí se pouze tehdy, když je skript stažen a spuštěn.

Odložené skripty si zachovávají relativní pořadí, stejně jako běžné skripty.

Řekněme, že máme dva odložené skripty:long.js a poté small.js :

<script defer src="https://javascript.info/article/script-async-defer/long.js"></script>
<script defer src="https://javascript.info/article/script-async-defer/small.js"></script>

Prohlížeče vyhledávají na stránce skripty a stahují je paralelně, aby se zlepšil výkon. Takže ve výše uvedeném příkladu se oba skripty stahují paralelně. small.js pravděpodobně skončí jako první.

…Ale defer Kromě toho, že prohlížeči říkáte, aby „neblokoval“, zajišťuje zachování relativního pořadí. Tedy i když small.js načte se jako první, stále čeká a běží po long.js provede.

To může být důležité v případech, kdy potřebujeme načíst knihovnu JavaScript a poté skript, který na ní závisí.

defer atribut je pouze pro externí skripty

defer atribut je ignorován, pokud je <script> značka nemá src .

asynchronní

async atribut je něco jako defer . Díky tomu je skript také neblokující. Ale má důležité rozdíly v chování.

async atribut znamená, že skript je zcela nezávislý:

  • Prohlížeč neblokuje async skripty (jako defer ).
  • Ostatní skripty nečekají na async skripty a async skripty na ně nečekají.
  • DOMContentLoaded a asynchronní skripty na sebe nečekají:
    • DOMContentLoaded k obojímu může dojít před asynchronním skriptem (pokud se asynchronní skript dokončí načítání po dokončení stránky)
    • …nebo po asynchronním skriptu (pokud je asynchronní skript krátký nebo byl v mezipaměti HTTP)

Jinými slovy async skripty se načítají na pozadí a spouštějí se, až budou připraveny. DOM a další skripty na ně nečekají a nečekají na nic. Plně nezávislý skript, který se spustí při načtení. Tak jednoduché, jak to jen může být, že?

Zde je příklad podobný tomu, který jsme viděli u defer :dva skripty long.js a small.js , ale nyní s async místo defer .

Nečekají na sebe. Cokoli se načte jako první (pravděpodobně small.js ) – běží jako první:

<p>...content before scripts...</p>

<script>
 document.addEventListener('DOMContentLoaded', () => alert("DOM ready!"));
</script>

<script async src="https://javascript.info/article/script-async-defer/long.js"></script>
<script async src="https://javascript.info/article/script-async-defer/small.js"></script>

<p>...content after scripts...</p>
  • Obsah stránky se zobrazí okamžitě:async neblokuje to.
  • DOMContentLoaded může nastat před i po async , zde nejsou žádné záruky.
  • Menší skript small.js je druhý, ale pravděpodobně se načte před long.js , takže small.js běží první. I když to může být, že long.js nejprve se načte, pokud je uložen v mezipaměti, pak se spustí jako první. Jinými slovy, asynchronní skripty se spouštějí v pořadí „načítání jako první“.

Asynchronní skripty jsou skvělé, když do stránky integrujeme nezávislý skript třetí strany:čítače, reklamy a tak dále, protože na našich skriptech nezávisí a naše skripty by na ně neměly čekat:

<!-- Google Analytics is usually added like this -->
<script async src="https://google-analytics.com/analytics.js"></script>
async atribut je pouze pro externí skripty

Stejně jako defer , async atribut je ignorován, pokud je <script> značka nemá src .

Dynamické skripty

Existuje ještě jeden důležitý způsob, jak přidat skript na stránku.

Můžeme vytvořit skript a připojit jej k dokumentu dynamicky pomocí JavaScriptu:

let script = document.createElement('script');
script.src = "/article/script-async-defer/long.js";
document.body.append(script); // (*)

Skript se začne načítat, jakmile je připojen k dokumentu (*) .

Dynamické skripty se ve výchozím nastavení chovají jako „asynchronní“.

To je:

  • Na nic nečekají, nic na ně nečeká.
  • Skript, který se načte jako první – spustí se jako první (pořadí „načíst jako první“).

Toto lze změnit, pokud explicitně nastavíme script.async=false . Potom budou skripty spouštěny v pořadí dokumentů, stejně jako defer .

V tomto příkladu loadScript(src) funkce přidá skript a také nastaví async na false .

Takže long.js vždy běží jako první (jako první je přidán):

function loadScript(src) {
 let script = document.createElement('script');
 script.src = src;
 script.async = false;
 document.body.append(script);
}

// long.js runs first because of async=false
loadScript("/article/script-async-defer/long.js");
loadScript("/article/script-async-defer/small.js");

Bez script.async=false , skripty by se spouštěly ve výchozím pořadí, v prvním pořadí načítání (small.js pravděpodobně první).

Opět jako u defer , na pořadí záleží, pokud chceme načíst knihovnu a poté další skript, který na ní závisí.

Shrnutí

Oba async a defer mají jednu společnou věc:stahování takových skriptů neblokuje vykreslování stránky. Uživatel tak může číst obsah stránky a okamžitě se s ní seznámit.

Ale jsou mezi nimi také zásadní rozdíly:

Objednat DOMContentLoaded
async Načíst první objednávku . Na jejich pořadí dokumentů nezáleží – kdo se načte jako první, spustí se jako první Irelevantní. Může se načíst a spustit, dokud dokument ještě nebyl plně stažen. To se stane, pokud jsou skripty malé nebo uložené v mezipaměti a dokument je dostatečně dlouhý.
defer Objednávka dokumentů (jak je uvedeno v dokumentu). Spustit po načtení a analýze dokumentu (v případě potřeby čekají), těsně před DOMContentLoaded .

V praxi defer se používá pro skripty, které potřebují celý DOM a/nebo je důležité jejich relativní pořadí provádění.

A async se používá pro nezávislé skripty, jako jsou počítadla nebo reklamy. A na jejich relativním pořadí provedení nezáleží.

Stránka bez skriptů by měla být použitelná

Poznámka:pokud používáte defer nebo async , pak uživatel uvidí stránku před skript se načte.

V takovém případě pravděpodobně ještě nejsou některé grafické komponenty inicializovány.

Nezapomeňte uvést indikaci „načítání“ a deaktivovat tlačítka, která ještě nejsou funkční. Nechte uživatele jasně vidět, co může na stránce dělat a co se ještě připravuje.