Defer and async attributes of script tag

November 6, 2022 776 points heat 0 likes 1 comment

 image.png

preface

During the interview, we often encounter a classic interview question:

How to optimize web page loading speed?

There is always one routine answer:

Place the css file at the top of the page and the js file at the bottom of the page.

So why put the js file at the bottom of the page?

Let's first look at this code:

 <! DOCTYPE html >
 < html  lang =" zh " >
   < head >
     < title > Hi </ title >
     < script >
         console . log ( "Howdy ~" ) ;
     </ script >
     < script  src =" https://unpkg.com/vue @3.2.41/dist/vue.global.js " > </ script >
     < script  src =" https://unpkg.com/vue-router @4.1.5/dist/vue-router.global.js " > </ script >
   </ head >
   < body > Hello  👋🏻 ~ </ body >
 </ html >

His execution sequence is:

  • Print on the console: Howdy ~
  • Request and execute vue.global.js
  • Request and execute vue-router.global.js
  • Show on the page: Hello 👋🏻 ~
  • Trigger DOMContentLoaded Events

 image.png

The browser's resolution rule is: if script Tag, construction is suspended DOM , start execution instead script Label, if it is External script , then the browser needs to wait for it to "download" and "execute" before continuing to parse the following HTML.

If you request and execute the " vue.global.js "It takes 3 seconds," vue-router.global.js "Takes 2 seconds, then the Hello 👋🏻 ~ , it will take at least 5 seconds to display.

As you can see, the script tag will block the browser from parsing HTML script All on head In the case of poor network, the page will be blank for a long time.

A long time ago, these external scripts were generally placed in body At the end of the tag, make sure to parse the display first body And then request the execution of these external scripts one by one.

Is there any other more elegant solution?

The answer is yes, now script Two new attributes have been added to the tag: defer And async , is to solve such problems and improve page performance.

<script defer>

Let's take a look at the explanation on the MDN:

This Boolean property is set to notify the browser that the script will be executed after the document is parsed and before the DOMContentLoaded event is triggered.
The script with the defer attribute will block the DOMContentLoaded event until the script is loaded and parsing is completed.

The document is a direct summary of its features. Let's take a look at the following code to expand the details and deepen our understanding.

 <! DOCTYPE html >
 < html  lang =" zh " >
   < head >
     < title > Hi </ title >
     < script >
       console . log ( "Howdy ~" ) ;
     </ script >
     < script  defer  src =" https://unpkg.com/vue @3.2.41/dist/vue.global.js " > </ script >
     < script  defer  src =" https://unpkg.com/vue-router @4.1.5/dist/vue-router.global.js " > </ script >
   </ head >
   < body > Hello  👋🏻 ~ </ body >
 </ html >

His execution sequence is:

  • Print on the console: Howdy ~
  • Show on the page: Hello 👋🏻 ~
  • Request and execute vue.global.js
  • Request and execute vue-router.global.js
  • Trigger DOMContentLoaded Events

 image.png

If on script The label is set with defer Property, the browser will silently start downloading the script in the background and continue parsing the subsequent HTML when it parses here, without blocking the parsing operation.

After HTML parsing is completed, the browser will immediately execute the script downloaded in the background. After the script is executed, it will trigger DOMContentLoaded Event.

It seems easy to understand, right? Let's discuss two details:

Q1: If you set the defer The script of the attribute has not been downloaded yet. What will happen?
A1: The browser will execute the script after the script download is completed, and then trigger the DOMContentLoaded Event.

Q2: If there are multiple settings defer Property, how will the browser handle it?
A2: The browser will download these scripts in the background in parallel. After HTML parsing is completed and all scripts are downloaded, they will be executed in the relative order in HTML. After all scripts are executed, they will be triggered DOMContentLoaded Event.

Best Practices:

It is recommended that all external scripts set this attribute by default, because it will not block HTML parsing, can download JavaScript resources in parallel, and can also be executed in their relative order in HTML to ensure that dependent scripts do not lack dependencies when running.

In the application of SPA, you can consider script Label plus defer Property, and put body At the back of. In modern browsers, it can be downloaded in parallel to improve the speed, and it can also ensure that in old browsers, the browser is not blocked from parsing HTML, playing a role of degradation.

be careful:

  • defer Property only applies to external scripts if script Script does not src , will be ignored defer Properties.
  • defer Attribute pair module script( <script type='module'></script> )Invalid because the module script is based on defer Is loaded in the form of.

<script async>

As usual, let's look at the explanation on the MDN:

For ordinary scripts, if the async attribute exists, the ordinary script will be requested in parallel and parsed and executed as soon as possible.
For module scripts, if the async attribute exists, the script and all its dependencies will be executed in the delay queue, so they will be requested in parallel and parsed and executed as soon as possible.
This attribute can eliminate parsing blocked Javascript.
Parsing blocked Javascript will cause the browser to load and execute the script before parsing can continue.

It seems that this description is quite clear, but let's take a look at the following code first to talk about details and deepen our understanding.

 <! DOCTYPE html >
 < html  lang =" zh " >
   < head >
     < title > Hi </ title >
     < script >
       console . log ( "Howdy ~" ) ;
     </ script >
     < script  async  src =" https://google-analytics.com/analytics.js " > </ script >
     < script  async  src =" https://ads.google.cn/ad.js " > </ script >
   </ head >
   < body > Hello  👋🏻 ~ </ body >
 </ html >

His execution sequence is:

  • Print on the console: Howdy ~
  • Parallel requests analytics.js And ad.js
  • Show on the page: Hello 👋🏻 ~
  • According to the actual situation of the network, the following items will be executed out of order
    • Execution analytics.js (Execute immediately after downloading)
    • Execution ad.js (Execute immediately after downloading)
    • Trigger DOMContentLoaded Event (may be triggered before, between and after the above two scripts)

 image.png
The browser parses the async Attribute script When labeling, the script will not block the page. It is also downloaded silently in the background. When he finishes downloading, the browser will pause parsing HTML and immediately execute this script.

It seems easy to understand, right? Let's discuss two details:

Q1: If set async Attribute script After downloading, the browser has not yet parsed the HTML. What will happen?
A1: The browser will pause parsing HTML, immediately execute this script, and then continue parsing HTML after the execution.

Q2: If there are multiple async Attribute script After the tags are downloaded, will they be executed in code order?
A2: No. The order of execution is: who downloads first, who executes first. async It is characterized by "complete independence" and does not rely on other content.

Best Practices:

When our project needs to integrate other independent third-party libraries, we can use this attribute. They do not depend on us, nor do we depend on them.
It is a good optimization scheme to let the browser download and execute it asynchronously by setting this property.

be careful:

  • async Attributes apply only to external scripts if script Script does not src , will be ignored async Properties.

summary

defer

  • Do not block the browser to parse HTML. It will not be executed until the HTML is parsed script
  • JavaScript resources will be downloaded in parallel.
  • Scripts are executed in relative order in HTML.
  • Will be triggered only after the script is downloaded and executed DOMContentLoaded Event.
  • During the execution of the script, the existing elements in HTML can be obtained.
  • defer Attribute pair Module Script Invalid.
  • Applicable to: All external scripts (via src Referenced script )。

async

  • The browser is not blocked from parsing HTML, but script After the download is completed, the browser will be immediately interrupted to parse HTML and execute this script
  • JavaScript resources will be downloaded in parallel.
  • They are independent of each other. The one who downloads first will execute first. There is no fixed sequence and they are uncontrollable.
  • Since there is no specific execution time, the existing elements in HTML may not be available in the script.
  • DOMContentLoaded Events and script The scripts are irrelevant and cannot be prioritized.
  • Applicable to: independent third-party scripts.

In addition: async And defer The biggest difference between them is their execution timing.

One More Thing

Have you ever thought that if a script Label setting at the same time defer And async , what will the browser do?

Let's start with the conclusion: in terms of manifestation, async Priority ratio of defer High, that is, if these two attributes exist at the same time, the browser will async To load this script.

There are mainly two cases:

If it is a "normal script", the browser will give priority async Whether the attribute exists, and if so, the async Feature to load this script. If it does not exist, then determine whether it exists defer Property.

If it is " Module Script ", the browser will judge async Whether the attribute exists:

  • If it exists, the browser will download this module and all its dependent modules in parallel. After all the downloads are completed, the script will be executed immediately.
  • If it does not exist, the browser will download this module and all its dependent modules in parallel, and then execute this script after the browser has parsed the HTML.
  • In addition, it should be noted that: set on the module script defer The property is invalid.

A picture is worth a thousand words

Finally, let's use a diagram to summarize the loading modes of these two attributes:
 image.png

Thinking question 🤔

  • Why must browsers execute common script tags before parsing them?
  • Ordinary script tags will block the browser from parsing HTML. What problems will this cause?

reference resources

Gcod

If life is just like the first sight, what is the sad autumn wind painting fan

Article comments

  • move

    Can you add a vx and customize a paid mobile traffic disappear device

    November 28, 2022