主頁 > 前端設計 > 深入理解 JavaScript Runtime Event Loop

深入理解 JavaScript Runtime Event Loop

2021-10-06 08:43:50 前端設計

先簡單聊一聊

JavaScript中的“Event Loop”應該是JavaScript中一個非常重要的一個知識點,至少在我以往的面試程序中被問到很多次,初次了解“Event Loop”相關知識的時候我想好多同學,特別是初學或者經驗不是太豐富的同學大概是這樣一個程序:(實際上,不僅僅限于“Event Loop”稍微有深度的知識也大概是這樣)

  1. 打開瀏覽器,跳轉到百度
  2. 搜索關鍵字Event Loop
  3. 點擊第一條搜索結果
  4. 下拉滾動條,目測文章長度(此處有可能直接跳到第五步),試著閱讀幾分鐘
  5. 當頭皮感到發麻時,關閉頁面,關閉瀏覽器, 整個學習程序結束,Cool!

言歸正傳,的確我們可能會被那些枯燥無味的概念、原理和理論知識所擊敗,越往下讀越覺得枯燥,概念性的描述無法像代碼那樣直觀的理解一個知識點,但是JavaScript中代碼是怎樣正確執行的恰好就需要“Event Loop”機制做支撐,所以想要完整的掌握這個知識點,確實需要耐心的了解各種涉及到的理論知識,當把一些概念理解清楚時,我們才能懂得JavaScript Runtime是如何實作”Event Loop“,屆時,我們才能夠完成從“看懂代碼”--->”代碼是如何執行“這個程序的轉化,

了解完JavaScript Runtime中“Event Loop”的運行機制,我們可以很好的理解以下幾個問題:

  • 當用戶點擊頁面上按鈕到發出回應這個程序中JavaScript做了哪些作業
  • setTimeout(callback)是如何作業的
  • 某些情況下可能引發頁面block的一些產生原因
  • JavaScript中的異步是如何實作的

OK,讓我們來一步步探究這些問題,

首先要了解的幾個問題

  • 瀏覽器的多行程和多執行緒
  • 常說的JavaScript單執行緒是何含意
  • 為什么JavaScript必須單執行緒執行

瀏覽器的多行程和多執行緒

瀏覽器的多行程

瀏覽器也是一個應用程式,當打開瀏覽器時,OS就會為其分配虛擬地址空間,創建對應的行程,我們打開一個瀏覽器,并不是創建了一個行程而是多個,

打開Chrome瀏覽器時行程情況

一般地,瀏覽器的主要行程包含以下幾個:

①Browser行程 瀏覽器的主行程,我們簡單的理解為它是一個“Leader”統領著各項作業,如:瀏覽器界面顯示,頁面管理等等,

②GPU行程 顧名思義是負責圖形繪制渲染的作業,

③插件行程

渲染行程 我們重點關注下這個,渲染行程即瀏覽器內核,主要用于控制頁面渲染、JavaScript執行、各種event處理等等,

當我們每打開一個Tab時,就會對應創建新的渲染行程(當Tab是空白時,也就是沒有打開任何網頁的時候,會做優化,不會單獨開辟新的行程),

打開多個Tab時行程的情況

每個Tab在單獨的行程中運行,很好理解,因為每個行程具有獨立的地址空間,一個行程不能訪問另一個行程的代碼和資料,也不能直接的操作OS內核的代碼和資料,在瀏覽器的層面去看,那就可以避免一個Tab中訪問另一個Tab內的資料,同時當一個Tab發生崩潰時,也不會去影響其他Tab,

比如:打開一個Tab的console輸入”while(true){}“,顯然當前Tab顯示的頁面會被block,你無法去點擊和選中頁面上的任何內容,當你切換到其他Tab時,完全沒有受到影響,

瀏覽器內核中的多執行緒

我們都知道,執行緒是隸屬行程之中的,執行緒間共享行程中分配的資源,那么瀏覽器內核或者說是渲染行程中都會維護一組執行緒來進行作業,主要包含以下幾個執行緒:

  • GUI渲染執行緒 主要負責頁面的渲染,包括決議Html決議和計算CSS,構建渲染樹,布局和繪制,渲染時也會維護一個Queue,我們后面再去展開討論,
  • JavaScript引擎執行緒 顧名思義,決議JavaScript,所有的JavaScript相關代碼都會在JavaScript引擎上去執行,后續詳細討論JavaScript引擎執行緒,
  • 事件觸發執行緒 當對應的事件觸發,比如用戶點擊、發送Ajax等等,事件觸發執行緒會把對應的事件的callback加入到Tasks Queue中,等到JavaScript執行緒Stack清空時,會把事件對應的callback壓入JavaScript引擎執行緒Stack中去執行,
  • Http請求執行緒 通過瀏覽器發送Http請求,當請求的Status發送變化時,比如請求成功或者請求失敗,會將對應的callback加入到Tasks Queue中,等到JavaScript執行緒Stack清空時,會把事件對應的callback壓入JavaScript引擎執行緒Stack中去執行,
  • 定時觸發器執行緒 因為JS引擎是單執行緒的,如果我們把定時器的執行程序直接放在JS引擎中處理,當遇到Stack中有大量Function堆積時,那么定時器或者延時器的計時是不準確的,現在當我們觸發setTimeout之后,事件觸發執行緒會將對應的任務單獨添加到定時器觸發執行緒中去執行等待,當等待結束后,會將對應的callback加入到Tasks Queue中,等到JavaScript執行緒Stack清空時,會把事件對應的callback壓入JavaScript引擎執行緒Stack中去執行,
瀏覽器內核中的多執行緒

常說的JavaScript單執行緒是何含意

當我們接觸JavaScript這門語言的時候,我們或多或少都聽過或者看到過“JavaScript是執行緒的”、“JavaScript語言是單執行緒的”這樣的話,那么到底該如何去理解呢?我覺得不能說JavaScript這門語言設計是單執行緒的,這句話原本就讓人讀起來就有點別扭,“執行緒”這個術語就不屬于“JS語言”的單獨范疇,通過前面的描述我們都知道,JS的決議和運行是在JS引擎執行緒中進行的,JS引擎執行緒存在且唯一,所以我覺得正確的理解應該是“JavaScript是單執行緒運行的“或者說是”JavaScript在瀏覽器中運行是單執行緒的”,這與它的運行環境有關,

為什么JavaScript必須單執行緒執行

JavaScript主要職責就是處理頁面與用戶的互動、操作DOM樹、操作CSS樣式以及相關邏輯處理,我們以操作DOM來說,假設有兩個執行緒同時去操作一個DOM元素,那么這個DOM元素就會成為執行緒間競爭的資源,我們就需要去處理執行緒同步,那么事情將變得一步步復雜起來,所以JavaScript要單執行緒執行,即使后續引入Web Worker來提高CPU的利用率,子執行緒受控于主執行緒,子執行緒依然不能操作DOM元素,

JavaScript引擎執行緒

Ok,JS引擎執行緒跟其他執行緒一樣,有一個分配記憶體空間的共享堆和一個堆疊,堆疊中存盤傳遞給方法的區域變數、實參以及記錄堆疊中方法執行位置的一個地址,

JS引擎執行緒

我們簡單來說一下,JS中方法是如何在Call Stack中執行的,

function func1() {
  return func2();
}
function func2() {
  return func3();
}
function func3() {
  console.log('over!');
}
func1();
執行程序

我想上述的程序都不陌生,至少在軟體工程課上好多同學都畫過,整個執行程序很簡單,當有方法需要執行時入堆疊,執行完畢出堆疊,直到執行堆疊清空,如果遇到遞回沒有出口時,整個執行堆疊將會迅速被壓滿溢位,瀏覽器會拋出如下例外:

Event Loop

當我們使用XMLHTTPRequest發送一個請求時,我們依舊可以在頁面上選擇文本;當我們設定一個setTimeout后,頁面并沒有發生阻塞,而是在某個地方為我們默默地倒計時;既然我們前文已經說明了Javascript是單執行緒運行的,那上述的情況背后又是如何作業的呢?我們的JavaScript代碼能夠按照某種“特定”的順序執行,實際上就是“Event Loop”機制在起到關鍵性作用,

簡單認識下“Event Loop”

"Event Loop"一般我們稱之為“事件回圈”(也有稱之為“事件輪詢”),是一種以事件驅動為思想的執行模型或者運行機制,不同JavaScript運行環境對“Event Loop”機制有不同的具體實作,比如”瀏覽器環境“、”Node環境“等等,HTML5標準規范中關于“Event Loop”的相關介紹

后續所討論的“Event Loop”的相關內容,都是基于瀏覽器運行環境下實作的,

我們圍繞以下幾個話題進行討論:

  • Call Stack
  • Queue
  • Event loop的整個事件驅動流程

Call Stack

實際上前文我們已經圖文并茂的介紹了JS引擎執行緒中的Call Stack,當一個個function被呼叫時,按照順序入堆疊執行,執行完畢之后依次出堆疊,我們的JavaScript同步代碼直接在呼叫堆疊中執行,如:變數賦值,console.log等等,異步代碼如setTimeout、send XMLHttpRequest則在呼叫堆疊中觸發,然后呼叫瀏覽器的某個webapi將對應的任務放到某個地方(background threads)中去執行,待執行完畢或者達到某種狀態時將該任務對應的callback加入到某個對應佇列中,待callstack清空后,對應的callback入堆疊執行,

Queue

佇列,Queue是“Event Loop”機制中一個非常重要的組成部分,里面存盤了對應task的callback,當call stack清空時,會讀取任務佇列中的callback加入到stack中等待執行,

實際上,整個運行機制中一共維護了三個佇列,分別是:

  • Macro queue / Tasks queue 宏佇列
  • Micro queue 微佇列
  • Render queue / Animation callbacks 渲染佇列

三個不同的queue有著不同的讀取優先級,同時在每輪“Event Loop”中不同佇列中的callback入堆疊的情況也是不同的,

Macro queue / Tasks queue 宏佇列

以下幾種任務會進入宏佇列:

  • setTimeout
  • setInterval
  • 事件觸發
  • I/O

Micro queue 微佇列

以下幾種任務會進入宏佇列:

  • Promise
  • MutationObserver

Render queue / Animation callbacks 渲染佇列

RAF(requestAnimationFrame)回呼及Render流程

需要注意的點:

  • 無論是哪個佇列都必須等到堆疊清空后才能讀取佇列中的callback入堆疊執行
  • 三個佇列的優先級分別是:微佇列 > 渲染佇列 >宏佇列
  • Micro queue不為空,每輪事件回圈會依次將Micro queue中的所有callback入堆疊執行直到Micro queue清空,若期間又有新的微任務產生,則會繼續入佇列,等待壓入堆疊中執行,直到Micro queue佇列清空,進入下一個流程,若微任務的生成速度大于微任務的執行速度,那么Micro queue的出佇列入堆疊程序將一直持續下去,阻塞整個“Event Loop
  • Render queue / Animation callbacks不為空,每輪事件回圈會依次將Render queue中的所有callback入堆疊執行直到Render queue清空,若回呼中有新的RAF回呼,則會進入Render queue在下次事件回圈中執行
  • Tasks queue不為空,每輪事件回圈從Tasks queue中讀取隊頭的callback入堆疊執行,期間新添加的taskTasks queue隊尾進佇列等待后續事件回圈執行
Queue

Event loop完整流程

  1. 同步代碼在call stack中執行,直到所有同步代碼執行完畢,stack清空,(同步代碼并不說明等同于同步任務,比如同步設定一個4s的setTimeout,實際等待程序是異步的;期間根據不同的任務會加入到不同的佇列中,比如Promise、sendHttp分別進入到微佇列和宏佇列中)
  2. 讀取微佇列micro queue,若微佇列長度不為0,則從隊首依次讀取隊中元素到stack中執行callback,直到微佇列清空,此步驟才算結束;若期間微任務不斷產生則一直持續入堆疊執行,
  3. 微佇列處理完成后,讀取render queue或者是RAF callbacks,若渲染佇列長度不為0,則從隊首依次讀取隊中元素到stack中執行callback,直到渲染佇列清空,此步驟才算結束;若期間又有RAF回呼不斷產生,則放到下一輪“Event Loop”執行,【RAF callback是在每次重繪(Style、Layout、Paint)之前更新影片】
  4. 最后讀取宏佇列macro queue,若微佇列長度不為0,則只讀取隊首元素到stack中執行,queue長度-1,期間有新的宏任務產生則入佇列,等待后續“Event Loop”執行,
  5. 等待stack中function執行完畢清空,本輪“Event Loop”結束,
  6. 回圈往復Step2-5
瀏覽器的Event Loop實作

至此,根據上述大篇幅的介紹和分析,我想現在我們應該可以很好的理解上述我畫的這幅圖,

關于setTimeout的探討

  • setTimeout(fn,0)的分析
  • setTimeout并沒有想象的那么“準確”
  • setTimeout非阻塞

setTimeout(fn,0)的分析

首先我們要知道,setTImeout、setInterval是瀏覽器提供給我們的webapi(Web API 介面參考),我們可以用JavaScript代碼去呼叫這些api進行使用,

那么我們先看下面的代碼片

console.log('hello')
setTimeout(function () {
   console.log('timer')
}, 0)
console.log('js')

最最開始的時候學習JS的時候,對“setTimeout”的定義應該是“在n毫秒之后執行fn”,那么就覺得第二引數傳0應該就是立即執行啊,實則不然,根據我們前面所講述的,應該很容易分析出答案,

我把上述代碼的運行程序再次畫出來,進一步讓大家理解的更加清晰一點,

setTimeout執行程序

所以說setTimeout(fn,0)不能準確的表示fn立即執行,而表示盡快的執行,

因為setTimeout會創建一個異步任務,等待結束后callback加入tasks queue等待入堆疊執行,而tasks queue入堆疊的前提就是stack必須為空,所以即使設定為0,當有大量同步代碼在主執行緒中執行時,就必須等它們執行結束,stack清空后才能去執行setTimeout的回呼,

要強調的是:即使setTimeout的ms引數設定為0,在w3c的標準規范下,也會延長到4ms左右(大概是4.7ms),

setTimeout并沒有想象的那么“準確”

setTimeout(function () {
   console.log('s1')
}, 1000)
setTimeout(function () {
   console.log('s2')
}, 1000)
setTimeout(function () {
   console.log('s3')
}, 1000)

上述代碼設定了三個延時器,那么根據我們上述的分析很容易知道,這三個宏任務會并行等待1s后依次加入tasks queue,又根據我們前文所說的tasks queue的入堆疊執行情況(每次讀取隊頭入堆疊執行)可知,callback2入堆疊執行必須等待callback1執行完畢堆疊清空,callback3入堆疊執行必須等待callback2執行完畢堆疊清空,那么callback1在執行時callback2只能等待,那么對于第二個setTimeout來說,整個程序肯定不止等了1000ms,第三個setTimeout則等了更長,

所以,“setTimeout(fn,n)”應該是fn最快在n毫秒后執行,

setTimeout非阻塞

<button id="btn" style="width: 300px;height: 50px;">while(true)</button>
<script>
    document.querySelector('#btn').addEventListener('click',function(){
      while(true){}
    })
</script>

點擊按鈕后,頁面block,文字不能選中,按鈕不能點擊,因為同步代碼引發死回圈,stack一直不為空,阻塞了UI render,

 <button id="btn" style="width: 300px;height: 50px;">while(true)</button>
 <script>
   document.querySelector('#btn').addEventListener('click', function () {
     let foo = () => {
       setTimeout(foo, 0);
     };
     foo();
   })
 </script>

貌似看來這段代碼也是一個遞回導致的死回圈,但是當我們點擊按鈕之后,頁面就好像什么都沒有發生一樣,并不會阻塞頁面渲染,文字可以選中,按鈕可以正常點擊,

實際上我們稍加分析便知,前文說明了佇列讀取的優先級,渲染佇列的讀取優先級是高于宏佇列的,所以雖然有源源不斷的宏任務產生,入佇列然后入堆疊執行,但整個程序中如果遇到UI Render要執行(60HZ螢屏,一般是16.67ms render一次),則UI Render正常立即執行,所以整個程序并不阻塞頁面渲染,

“Event Loop”對頁面渲染可能帶來的影響

盡量不要阻塞“Event Loop"

我們繼續將上述案例給改寫成如下形式:

 <button id="btn" style="width: 300px;height: 50px;">while(true)</button>
 <script>
    document.querySelector('#btn').addEventListener('click', function () {
      let foo = () => {
        Promise.resolve().then(foo)
      }
      foo()
    })
 </script>

當我們點擊按鈕發現頁面被block,文字無法選中,按鈕無法點擊,在我們前面的分析得出,不斷產生任務不是沒有阻塞JS執行緒的stack嗎?怎么還是會阻塞頁面渲染?

因為Promise是會產生微任務,進入微佇列,我們也說過微佇列的讀取順序的優先級是最高的在渲染之前,同時也說過微佇列在讀取時一直會把佇列清空后才能進入下一環節,上述案例顯然微任務在不斷產生,micro queue永遠不能被清空,一直在阻塞整個“Event Loop”,導致后續UI Render不能被執行,頁面不能重繪,

綜合案例

案例一

el.addEventListener('click', () => {
  console.log(1);
  new Promise((resolve, _) => {
    console.log(2);
    resolve()
  }).then(() => {
    console.log(3);
  })
})
el.addEventListener('click', () => {
  console.log(4);
  setTimeout(() => {
    console.log(5);
  }, 0);
  Promise.resolve().then(() => {
    console.log(6);
  })
})

頁面手動點擊按鈕后,console該如何輸出?

請先認真思考一下再看正確結果,

應該很容易理解上述輸出,我們簡單理一下執行程序,

首先同步代碼邏輯:

1.獲取dom元素

2.為按鈕添加一個事件監聽listener1

3.為按鈕添加一個事件監聽listener2

按鈕點擊:

目前tasks queue分別存放listener1的 callback、listener2的 callback;micro queue length = 0

  1. 首先讀取micro queue為空,讀取task queue,取隊頭callback1入堆疊執行
  2. log(1)執行->出堆疊 輸出 1
  3. 設定Promise,同步執行log(2),log出堆疊[Promise中then callback、catch callback才是異步回呼的],同時向micro queue 中加入promise callback1,此時堆疊清空,輸出 2
  4. 堆疊清空,優先讀取micr oqueue,log(3)入堆疊執行,log(3)出堆疊,此時堆疊清空,輸出 3
  5. 堆疊清空,micro queue為空,tasks queue中隊頭為listener2的 callback,入堆疊執行,tasks queue清空,
  6. log(4)入堆疊執行,log(4)出堆疊,設定setTimeout,tasks queue中添加setTimeout callback,輸出 4
  7. 設定Promise,micro queue中添加promise callback2,listener2的callback執行完畢,此時堆疊清空,
  8. 優先讀取micro queue,promise callback2入堆疊執行,log(6)入堆疊,log(6)執行完畢出堆疊,輸出 6
  9. 再讀取tasks queue,setTimeout callback入堆疊執行,log(5)入堆疊,log(5)執行完畢出堆疊,輸出 5

故以上代碼片輸出順序為: 1 2 3 4 6 5

案例二

const el1 = document.querySelector('#btn1')
el.addEventListener('click', () => {
  console.log(1);
  new Promise((resolve, _) => {
    console.log(2);
    resolve()
  }).then(() => {
    console.log(3);
  })
})
el.addEventListener('click', () => {
  console.log(4);
  setTimeout(() => {
    console.log(5);
  }, 0);
  Promise.resolve().then(() => {
    console.log(6);
  })
})
el.click();

請先認真思考一下再看正確結果,

乍一看,代碼邏輯好像是沒有發生變化,一個是手動點擊按鈕,一個是JS代碼觸發點擊事件,那么為什么輸出結果就發生變化了呢?

對比完兩個輸出結果可以發現,listener1 callback同步代碼執行完之后,立即執行了listener2 callback而不是micro queue,

是因為,在讀去任何佇列的前提一定是呼叫堆疊為空,那么最后一句el.click()始終在呼叫堆疊上還未出堆疊,所以說listener1 callback同步代碼執行完之后不能立即讀取佇列,而應該繼續執行click觸發的listener2,然后繼續執行,直到listener2 callback執行完畢后,click出堆疊,開始讀取佇列,目前micro queue中有兩個元素:promise1 callback、promise2 callback,tasks queue中有一個元素:setTimeout callback,按照“Event Loop”的執行順序最后的輸出結果為:1 2 4 3 6 5

寫在最后

至此,基本上把瀏覽器的“Event Loop”實作給闡述清楚了,從瀏覽器的多行程、多執行緒到JS引擎中的堆疊呼叫再到”Event Loop“的引出,同時又講述了”setTimeout的那些事“、阻塞”Event Loop“可能帶來的不良影響等問題,在一些原理性較深,較難理解的問題,我都通過手工繪圖的方式復現整個執行程序可視化的分析,幫助大家快速理解相關的問題,最后給出了兩個綜合案例幫助大家進一步鞏固”Event Loop“的運行機制,循序漸進,希望能從淺入深把”Event Loop“這個重要的知識點牢牢掌握,花了數天時間推敲和揣摩這篇文章,整個寫作程序也讓自己對這個知識點掌握的更加牢固,同時也希望能給大家帶來幫助,我相信如果能夠耐心把這篇文章看完,我相信多多少少都會有自己的識訓,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/305711.html

標籤:其他

上一篇:筆記8:商品分類【Vue實戰專案:電商管理系統(Element-UI)】

下一篇:webpack5的基本使用和深入優化

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • vue移動端上拉加載

    可能做得過于簡單或者比較low,請各位大佬留情,一起探討技術 ......

    uj5u.com 2020-09-10 04:38:07 more
  • 優美網站首頁,頂部多層導航

    一個個人用的瀏覽器首頁,可以把一下常用的網站放在這里,平常打開會比較方便。 第一步,HTML代碼 <script src=https://www.cnblogs.com/szharf/p/"js/jquery-3.4.1.min.js"></script> <div id="navigate"> <ul> <li class="labels labels_1"> ......

    uj5u.com 2020-09-10 04:38:47 more
  • 頁面為要加<!DOCTYPE html>

    最近因為寫一個js函式,需要用到$(window).height(); 由于手寫demo的時候,過于自信,其實對前端方面的認識也不夠體系,用文本檔案直接敲出來的html代碼,第一行沒有加上<!DOCTYPE html> 導致了$(window).height();的結果直接是整個document的高 ......

    uj5u.com 2020-09-10 04:38:52 more
  • WordPress網站程式手動升級要做好資料備份

    WordPress博客網站程式在進行升級前,必須要做好網站資料的備份,這個問題良家佐言是遇見過的;在剛開始接觸WordPress博客程式的時候,因為升級問題和博客網站的修改的一些嘗試,良家佐言是吃盡了苦頭。因為購買的是西部數碼的空間和域名,每當佐言把自己的WordPress博客網站搞到一塌糊涂的時候 ......

    uj5u.com 2020-09-10 04:39:30 more
  • WordPress程式不能升級為5.4.2版本的原因

    WordPress是一款個人博客系統,受到英文博客愛好者和中文博客愛好者的追捧,并逐步演化成一款內容管理系統軟體;它是使用PHP語言和MySQL資料庫開發的,用戶可以在支持PHP和MySQL資料庫的服務器上使用自己的博客。每一次WordPress程式的更新,就會牽動無數WordPress愛好者的心, ......

    uj5u.com 2020-09-10 04:39:49 more
  • 使用CSS3的偽元素進行首字母下沉和首行改變樣式

    網頁中常見的一種效果,首字改變樣式或者首行改變樣式,效果如下圖。 代碼: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, ......

    uj5u.com 2020-09-10 04:40:09 more
  • 關于a標簽的講解

    什么是a標簽? <a> 標簽定義超鏈接,用于從一個頁面鏈接到另一個頁面。 <a> 元素最重要的屬性是 href 屬性,它指定鏈接的目標。 a標簽的語法格式:<a href=https://www.cnblogs.com/summerxbc/p/"指定要跳轉的目標界面的鏈接">需要展示給用戶看見的內容</a> a標簽 在所有瀏覽器中,鏈接的默認外觀如下: 未被訪問的鏈接帶 ......

    uj5u.com 2020-09-10 04:40:11 more
  • 前端輪播圖

    在需要輪播的頁面是引入swiper.min.js和swiper.min.css swiper.min.js地址: 鏈接:https://pan.baidu.com/s/15Uh516YHa4CV3X-RyjEIWw 提取碼:4aks swiper.min.css地址 鏈接:https://pan.b ......

    uj5u.com 2020-09-10 04:40:13 more
  • 如何設定html中的背景圖片(全屏顯示,且不拉伸)

    1 <style>2 body{background-image:url(https://uploadbeta.com/api/pictures/random/?key=BingEverydayWallpaperPicture); 3 background-size:cover;background ......

    uj5u.com 2020-09-10 04:40:16 more
  • Java學習——HTML詳解(上)

    HTML詳解 初識HTML Hyper Text Markup Language(超文本標記語言) 1 <!--DOCTYPE:告訴瀏覽器我們要使用什么規范--> 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <!--meta 描述性的標簽,描述一些 ......

    uj5u.com 2020-09-10 04:40:33 more
最新发布
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 07:59:23 more
  • 生產事故-走近科學之消失的JWT

    入職多年,面對生產環境,盡管都是小心翼翼,慎之又慎,還是難免捅出簍子。輕則滿頭大汗,面紅耳赤。重則系統停擺,損失資金。每一個生產事故的背后,都是寶貴的經驗和教訓,都是專案成員的血淚史。為了更好地防范和遏制今后的各類事故,特開此專題,長期更新和記錄大大小小的各類事故。有些是親身經歷,有些是經人耳傳口授 ......

    uj5u.com 2023-04-18 07:55:04 more
  • 記錄--Canvas實作打飛字游戲

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 打開游戲界面,看到一個畫面簡潔、卻又富有挑戰性的游戲。螢屏上,有一個白色的矩形框,里面不斷下落著各種單詞,而我需要迅速地輸入這些單詞。如果我輸入的單詞與螢屏上的單詞匹配,那么我就可以獲得得分;如果我輸入的單詞錯誤或者時間過長,那么我就會輸 ......

    uj5u.com 2023-04-04 08:35:30 more
  • 了解 HTTP 看這一篇就夠

    在學習網路之前,了解它的歷史能夠幫助我們明白為何它會發展為如今這個樣子,引發探究網路的興趣。下面的這張圖片就展示了“互聯網”誕生至今的發展歷程。 ......

    uj5u.com 2023-03-16 11:00:15 more
  • 藍牙-低功耗中心設備

    //11.開啟藍牙配接器 openBluetoothAdapter //21.開始搜索藍牙設備 startBluetoothDevicesDiscovery //31.開啟監聽搜索藍牙設備 onBluetoothDeviceFound //30.停止監聽搜索藍牙設備 offBluetoothDevi ......

    uj5u.com 2023-03-15 09:06:45 more
  • canvas畫板(滑鼠和觸摸)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>canves</title> <style> #canvas { cursor:url(../images/pen.png),crosshair; } #canvasdiv{ bo ......

    uj5u.com 2023-02-15 08:56:31 more
  • 手機端H5 實作自定義拍照界面

    手機端 H5 實作自定義拍照界面也可以使用 MediaDevices API 和 <video> 標簽來實作,和在桌面端做法基本一致。 首先,使用 MediaDevices.getUserMedia() 方法獲取攝像頭媒體流,并將其傳遞給 <video> 標簽進行渲染。 接著,使用 HTML 的 < ......

    uj5u.com 2023-01-12 07:58:22 more
  • 記錄--短視頻滑動播放在 H5 下的實作

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 短視頻已經無數不在了,但是主體還是使用 app 來承載的。本文講述 H5 如何實作 app 的視頻滑動體驗。 無聲勝有聲,一圖頂百辯,且看下圖: 網址鏈接(需在微信或者手Q中瀏覽) 從上圖可以看到,我們主要實作的功能也是本文要講解的有: ......

    uj5u.com 2023-01-04 07:29:05 more
  • 一文讀懂 HTTP/1 HTTP/2 HTTP/3

    從 1989 年萬維網(www)誕生,HTTP(HyperText Transfer Protocol)經歷了眾多版本迭代,WebSocket 也在期間萌芽。1991 年 HTTP0.9 被發明。1996 年出現了 HTTP1.0。2015 年 HTTP2 正式發布。2020 年 HTTP3 或能正... ......

    uj5u.com 2022-12-24 06:56:02 more
  • 【HTML基礎篇002】HTML之form表單超詳解

    ??一、form表單是什么

    ??二、form表單的屬性

    ??三、input中的各種Type屬性值

    ??四、標簽 ......

    uj5u.com 2022-12-18 07:17:06 more