日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

defer和async的原理与区别

發(fā)布時(shí)間:2023/12/10 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 defer和async的原理与区别 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

上一篇?jiǎng)傓D(zhuǎn)載了一篇有關(guān)于網(wǎng)站性能優(yōu)化的文章,其中提及到了頁面的加載和渲染的過程,提到了defer和async的相關(guān)區(qū)別,但是本人在此之前并沒有深究其中的區(qū)別。

defer和async是script標(biāo)簽的兩個(gè)屬性,用于在不阻塞頁面文檔解析的前提下,控制腳本的下載和執(zhí)行。
在介紹他們之前,我們有必要先了解一下頁面的加載和渲染過程:

  • 列表內(nèi)容
  • 瀏覽器通過HTTP協(xié)議請(qǐng)求服務(wù)器,獲取HMTL文檔并開始從上到下解析,構(gòu)建DOM;
  • 在構(gòu)建DOM過程中,如果遇到外聯(lián)的樣式聲明和腳本聲明,則暫停文檔解析,創(chuàng)建新的網(wǎng)絡(luò)連接,并開始下載樣式文件和腳本文件;
  • 樣式文件下載完成后,構(gòu)建CSSDOM;腳本文件下載完成后,解釋并執(zhí)行,然后繼續(xù)解析文檔構(gòu)建DOM
  • 完成文檔解析后,將DOM和CSSDOM進(jìn)行關(guān)聯(lián)和映射,最后將視圖渲染到瀏覽器窗口
  • 在這個(gè)過程中,腳本文件的下載和執(zhí)行是與文檔解析同步進(jìn)行,也就是說,它會(huì)阻塞文檔的解析,如果控制得不好,在用戶體驗(yàn)上就會(huì)造成一定程度的影響。

    所以我們需要清楚的了解和使用defer和async來控制外部腳本的執(zhí)行。

    先來簡明概要的介紹一下下面三者的區(qū)別:

  • <script src="script.js"></script>
    沒有 defer 或 async,瀏覽器會(huì)立即加載并執(zhí)行指定的腳本,“立即”指的是在渲染該 script 標(biāo)簽之下的文檔元素之前,也就是說不等待后續(xù)載入的文檔元素,讀到就加載并執(zhí)行。
  • <script async src="script.js"></script>
    有 async,加載和渲染后續(xù)文檔元素的過程將和 script.js 的加載與執(zhí)行并行進(jìn)行(異步)
  • <script defer src="myscript.js"></script>
    有 defer,加載后續(xù)文檔元素的過程將和 script.js 的加載并行進(jìn)行(異步),但是 script.js 的執(zhí)行要在所有元素解析完成之后,DOMContentLoaded 事件觸發(fā)之前完成。
  • 接著,我們來看一張圖:

    藍(lán)色線代表網(wǎng)絡(luò)讀取,紅色線代表執(zhí)行時(shí)間,這倆都是針對(duì)腳本的;綠色線代表 HTML 解析。

    此圖告訴我們以下幾個(gè)要點(diǎn):

  • defer 和 async 在網(wǎng)絡(luò)讀取(下載)這塊兒是一樣的,都是異步的(相較于 HTML 解析)
  • 它倆的差別在于腳本下載完之后何時(shí)執(zhí)行,顯然 defer 是最接近我們對(duì)于應(yīng)用腳本加載和執(zhí)行的要求的
  • 關(guān)于 defer,此圖未盡之處在于它是按照加載順序執(zhí)行腳本的,這一點(diǎn)要善加利用
  • async 則是一個(gè)亂序執(zhí)行的主,反正對(duì)它來說腳本的加載和執(zhí)行是緊緊挨著的,所以不管你聲明的順序如何,只要它加載完了就會(huì)立刻執(zhí)行
  • 仔細(xì)想想,async 對(duì)于應(yīng)用腳本的用處不大,因?yàn)樗耆豢紤]依賴(哪怕是最低級(jí)的順序執(zhí)行),不過它對(duì)于那些可以不依賴任何腳本或不被任何腳本依賴的腳本來說卻是非常合適的,最典型的例子:Google Analytics
  • 下面為了演示腳本的執(zhí)行情況,借鑒了詳解defer和async原理及應(yīng)用


    為了演示腳本的執(zhí)行情況,進(jìn)而介紹這兩個(gè)屬性的作用,我們先來搭建一個(gè)簡單的服務(wù)器:
    創(chuàng)建了一個(gè)簡易的Node服務(wù)器server.js,其代碼如下:

    var http = require('http'); var fs = require('fs');var typeMapping = {'html': 'text/html','js' : 'text/javascript','css' : 'text/css','ico' : 'image/x-icon' };var getResourceExtension = function(req) {var url = req.url;var lastIndexOfDot = url.lastIndexOf('.');if (lastIndexOfDot === -1) return 'text/plain';return url.substring(lastIndexOfDot + 1); };var respondResourceToClient = function(req, res) {//read the reource and respond via 'pipe'fs.createReadStream(req.url.replace(/^\//, '')).pipe(res); };var server = http.createServer(function(req, res) {console.log('requesting url: ', req.url);var extension = getResourceExtension(req);res.writeHead(200, {'Content-Type': typeMapping[extension]});var delay = function(time) {setTimeout(function() {respondResourceToClient(req, res);}, time || 0);};if (extension === 'html' || extension === 'css') {delay(0);} else if (extension === 'js') {delay(1000);} else {res.end('');} });server.listen(3000);console.log('listening at port 3000...');

    從上面的代碼我們可以看出,當(dāng)服務(wù)器接收到請(qǐng)求之后,會(huì)判斷請(qǐng)求資源是否為JS,如果是則延遲1s后返回對(duì)應(yīng)的資源。
    啟動(dòng)這個(gè)服務(wù)很簡單,只需執(zhí)行node server.js即可,然后就可以在瀏覽器中輸入http://localhost:3000/app/index.html訪問主頁了
    現(xiàn)在我們來看看index.html中的內(nèi)容:

    <!DOCTYPE html> <html><head><title>defer & async</title><link rel="stylesheet" type="text/css" href="css/main.css"><script type="text/javascript" src="js/1.js"></script></head><body><div class="text">Hello World</div><script type="text/javascript" src="js/2.js"></script></body> </html>

    在這個(gè)HTML文檔中,我們先在head中引用了一個(gè)外部的腳本文件js/1.js,然后在我們要顯示的Hello World后面又引用了一個(gè)js/2.js,它們的內(nèi)容都很簡單,就是彈出對(duì)應(yīng)的標(biāo)示信息:

    // js/1.js alert(1);// js/2.js alert(2);

    下面我們就來訪問主頁,看看會(huì)發(fā)生些什么:

    從圖中可以看到,渲染的過程的確是自上而下,同步進(jìn)行的,也就是說遇到外部的腳本,就得暫停文檔的解析,下載并且解釋執(zhí)行,這種方式是阻塞的,會(huì)造成網(wǎng)頁空白的現(xiàn)象。

    現(xiàn)在稍微修改一下代碼,將head中的script標(biāo)簽加上defer屬性,然后也稍微改動(dòng)一下兩個(gè)JS文件:

    <!DOCTYPE html> <html><head><title>defer & async</title><link rel="stylesheet" type="text/css" href="css/main.css"><!-- adding a 'defer' attribute, by default, the value will be 'true' --><script type="text/javascript" src="js/1.js" defer></script></head><body><div class="text">Hello World</div><script type="text/javascript" src="js/2.js"></script></body> </html> // js/1.js console.log(1);// js/2.js console.log(2);

    再次訪問index.html,我們會(huì)在控制臺(tái)中看到下面的執(zhí)行順序:

    顯而易見,1.js被延后致至文檔解析完成后執(zhí)行了,它的執(zhí)行順序比body中的

    <!DOCTYPE html> <html><head><title>defer & async</title><link rel="stylesheet" type="text/css" href="css/main.css"><!-- adding a 'defer' attribute, by default, the value will be 'true' --><script type="text/javascript" src="js/1.js" defer></script><script type="text/javascript" src="js/2.js" defer></script><script type="text/javascript" defer>console.log(3);</script></head><body><div class="text">Hello World</div><script type="text/javascript">document.addEventListener("DOMContentLoaded", function() {console.log('dom content loaded, ready state:', this.readyState);}, false);window.addEventListener('load', function() {console.log('window loaded, dom ready state:', document.readyState);}, false);</script></body> </html>

    可以看到,外聯(lián)的腳本是按照聲明順序執(zhí)行的,內(nèi)聯(lián)腳本并沒有遵守這個(gè)規(guī)則,另外,DOMContentLoaded和load事件依次被捕獲,DOM的狀態(tài)分別變?yōu)閕nteractive和complete

    接下來我們介紹一下async屬性,為了能夠很好的演示執(zhí)行順序,我們還需要一些修改:

    <!DOCTYPE html> <html><head><title>defer & async</title><link rel="stylesheet" type="text/css" href="css/main.css"><!-- adding a 'async' attribute, by default, the value is 'true' as well --><script type="text/javascript" src="js/1.js" async></script><script type="text/javascript" src="js/2.js" async></script><script type="text/javascript" src="js/3.js" async></script></head><body><div class="text">Hello World</div></body> </html>

    JS文件內(nèi)如下:

    // js/1.js console.log(1);// js/2.js console.log(2);// js/3.js console.log(3);

    再次訪問index.html,會(huì)發(fā)現(xiàn)控制臺(tái)打印如下:

    我們發(fā)現(xiàn),3個(gè)腳本的執(zhí)行是沒有順序的,我們也無法預(yù)測每個(gè)腳本的下載和執(zhí)行的時(shí)間和順序。async和defer一樣,不會(huì)阻塞當(dāng)前文檔的解析,它會(huì)異步地下載腳本,但和defer不同的是,async會(huì)在腳本下載完成后立即執(zhí)行,如果項(xiàng)目中腳本之間存在依賴關(guān)系,不推薦使用async。

    關(guān)于async,也需要注意以下幾點(diǎn):
    1. 只適用于外聯(lián)腳本,這一點(diǎn)和defer一致
    2. 如果有多個(gè)聲明了async的腳本,其下載和執(zhí)行也是異步的,不能確保彼此的先后順序
    3. async會(huì)在load事件之前執(zhí)行,但并不能確保與DOMContentLoaded的執(zhí)行先后順序

    以上就是defer和async的介紹,了解和掌握這兩個(gè)屬性的作用,不僅對(duì)JS代碼執(zhí)行的控制更加熟練,也會(huì)對(duì)頁面渲染多一分了解。

    參考文章:

    • http://blog.csdn.net/liuhe688/article/details/51247484
    • https://segmentfault.com/q/1010000000640869

    總結(jié)

    以上是生活随笔為你收集整理的defer和async的原理与区别的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。