主頁 > 前端設計 > 一文帶你了解如何排查記憶體泄漏導致的頁面卡頓現象

一文帶你了解如何排查記憶體泄漏導致的頁面卡頓現象

2021-04-08 11:42:54 前端設計

在這里插入圖片描述
不知道在座的各位有沒有被問到過這樣一個問題:如果頁面卡頓,你覺得可能是什么原因造成的?有什么辦法鎖定原因并解決嗎?

這是一個非常寬泛而又有深度的問題,他涉及到很多的頁面性能優化問題,我依稀還記得當初面試被問到這個問題時我是這么回答的:

  1. 先會檢查是否是網路請求太多,導致資料回傳較慢,可以適當做一些快取
  2. 也有可能是某塊資源的bundle太大,可以考慮拆分一下
  3. 然后排查一下js代碼,是不是某處有過多回圈導致占用主執行緒時間過長
  4. 瀏覽器某幀渲染的東西太多,導致的卡頓
  5. 在頁面渲染程序中,可能有很多重復的重排重繪
  6. emmmmmm…不知道了

后來了解到了,感官上的長時間運行頁面卡頓也有可能是因為記憶體泄漏引起的

記憶體泄漏的定義

那什么是記憶體泄漏呢?借助別的大佬給出的定義,記憶體泄漏就是指由于疏忽或者程式的某些錯誤造成未能釋放已經不再使用的記憶體的情況,簡單來講就是假設某個變數占用100M的記憶體,而你又用不到這個變數,但是這個變數沒有被手動的回識訓自動回收,即仍然占用100M的記憶體空間,這就是一種記憶體的浪費,即記憶體泄漏

JS的資料存盤

JavaScript的記憶體空間分為堆疊記憶體堆記憶體,前者用來存放一些簡單變數,后者用來存放復雜物件

  • 簡單變數指的是JS的基本資料型別,例如:StringNumberBooleannullundefinedSymbolBigInt
  • 復雜物件指的是JS的參考資料型別,例如:ObjectArrayFunction

JS垃圾回識訓制

根據記憶體泄漏的定義,有些變數或資料不再被使用或不需要了,那么它就是垃圾變數或垃圾資料,如果其一直保存在記憶體中,最終可能會導致記憶體占用過多的情況,那么此時就需要對這些垃圾資料進行回收,這里引入了垃圾回識訓制的概念

垃圾回收的機制分為手動自動兩種

例如C/C++采用的就是手動回收的機制,即先用代碼為某個變數分配一定的記憶體,然后在不需要了后,再用代碼手動釋放掉記憶體

JavaScript采用的則是自動回收的機制,即我們不需要關心何時為變數分配多大的記憶體,也不需要關心何時去釋放記憶體,因為這一切都是自動的,但這不表示我們不需要關心記憶體的管理!!!!否則也不會有本文討論的記憶體泄露了

接下來就講一下JavaScript的垃圾回識訓制

通常全域狀態(window)下的變數是不會被自動回收的,所以我們來討論一下區域作用域下的記憶體回收情況

function fn1 () {
    let a = {
        name: '零一'
    }

    let b = 3

    function fn2() {
        let c = [1, 2, 3]
    }

    fn2()

    return a
}

let res = fn1()

以上代碼的呼叫堆疊如下圖所示:

img

圖中左側為堆疊空間,用于存放一些執行背景關系和基本型別資料;右側為堆空間,用于存放一些復雜物件資料

當代碼執行到fn2()時,堆疊空間內的執行背景關系從上往下依次是 fn2函式執行背景關系 => fn1函式執行背景關系 => 全域執行背景關系

fn2函式內部執行完畢以后,就該退出fn2函式執行背景關系了,即箭頭向下移動,此時fn2函式執行背景關系會被清除并釋放堆疊記憶體空間,如圖所示:

img

fn1函式內部執行完畢以后,就該退出fn1函式執行背景關系了,即箭頭再向下移動,此時fn1函式執行背景關系會被清除并釋放相應的堆疊記憶體空間,如圖所示:

img

此時處于全域的執行背景關系中,JavaScript的垃圾回收器會每隔一段時間遍歷呼叫堆疊,假設此時觸發了垃圾回識訓制,當遍歷呼叫堆疊時發現變數b和變數c沒有被任何變數所參考,所以認定它們是垃圾資料并給它們打上標記,因為fn1函式執行完后將變數a回傳了出去,并存盤在全域變數res中,所以認定其為活動資料并打上相應標記,待空閑時刻就會將標記上垃圾資料的變數給全部清除掉,釋放相應的記憶體,如圖所示:

img

從這我們得出幾點結論:

  1. JavaScript的垃圾回識訓制是自動執行的,并且會通過標記來識別并清除垃圾資料
  2. 在離開區域作用域后,若該作用域內的變數沒有被外部作用域所參考,則在后續會被清除

補充: JavaScript的垃圾回識訓制有著很多的步驟,上述只講到了標記-清除,其實還有其它的程序,這里簡單介紹一下就不展開討論了,例如:標記-整理,在清空部分垃圾資料后釋放了一定的記憶體空間后會可能會留下大面積的不連續記憶體片段,導致后續可能無法為某些物件分配連續記憶體,此時需要整理一下記憶體空間;交替執行,因為JavaScript是運行在主執行緒上的,所以執行垃圾回識訓制時會暫停js的運行,若垃圾回收執行時間過長,則會給用戶帶來明顯的卡頓現象,所以垃圾回識訓制會被分成一個個的小任務,穿插在js任務之中,即交替執行,盡可能得保證不會帶來明顯的卡頓感

Chrome devTools查看記憶體情況

在了解一些常見的記憶體泄漏的場景之前,先簡單介紹一下如何使用Chrome的開發者工具來查看js記憶體情況

首先打開Chrome的無痕模式,這樣做的目的是為了屏蔽掉Chrome插件對我們之后測驗記憶體占用情況的影響

img

然后打開開發者工具,找到Performance這一欄,可以看到其內部帶著一些功能按鈕,例如:開始錄制按鈕;重繪頁面按鈕;清空記錄按鈕;記錄并可視化js記憶體、節點、事件監聽器按鈕;觸發垃圾回識訓制按鈕等等

img

簡單錄制一下百度頁面,看看我們能獲得什么,如下動圖所示:

img

從上圖中我們可以看到,在頁面從零到加載完成這個程序中JS Heap(js堆記憶體)documents(檔案)Nodes(DOM節點)Listeners(監聽器)GPU memory(GPU記憶體)的最低值、最高值以及隨時間的走勢曲線,這也是我們主要關注的點

再來看看開發者工具中的Memory一欄,其主要是用于記錄頁面堆記憶體的具體情況以及js堆記憶體隨加載時間線動態的分配情況

img

堆快照就像照相機一樣,能記錄你當前頁面的堆記憶體情況,每快照一次就會產生一條快照記錄,如圖所示:

img

如上圖所示,剛開始執行了一次快照,記錄了當時堆記憶體空間占用為13.9MB,然后我們點擊了頁面中某些按鈕,又執行一次快照,記錄了當時堆記憶體空間占用為13.4MB,并且點擊對應的快照記錄,能看到當時所有記憶體中的變數情況(結構、占總占用記憶體的百分比…)

然后我們還可以看一下頁面動態的記憶體變化情況,如圖所示:

img

在開始記錄后,我們可以看到圖中右上角有起伏的藍色與灰色的柱形圖,其中藍色表示當前時間線下占用著的記憶體;灰色表示之前占用的記憶體空間已被清除釋放,

從上圖程序來看,我們可以看到剛開始處于的tab所對應顯示的頁面中占用了一定的堆記憶體空間,成藍色柱形,在點擊別的tab后,原tab對應的內容消失,并且原來藍色的柱形變成灰色(表示原占用的記憶體空間得到了釋放),同時新tab所對應顯示的頁面也占用了一定的堆記憶體空間,因此后續我們就可以針對這個圖來查看記憶體的占用與清除情況

記憶體泄漏的場景

那么到底有哪些情況會出現記憶體泄漏的情況呢?這里列舉了常見的幾種:

  1. 閉包使用不當引起記憶體泄漏
  2. 全域變數
  3. 分離的DOM節點
  4. 控制臺的列印
  5. 遺忘的定時器

接下來介紹一下各種情況,并嘗試用剛才講到的兩種方法來捕捉問題所在

1.閉包使用不當

文章開頭的例子中,在退出fn1函式執行背景關系后,該背景關系中的變數a本應被當作垃圾資料給回收掉,但因fn1函式最終將變數a回傳并賦值給全域變數res,其產生了對變數a的參考,所以變數a被標記為活動變數并一直占用著相應的記憶體,假設變數res后續用不到,這就算是一種閉包使用不當的例子

接下來嘗試使用PerformanceMemory來查看一下閉包導致的記憶體泄漏問題,為了使記憶體泄漏的結果更加明顯,我們稍微改動一下文章開頭的例子,代碼如下:

<button onclick="myClick()">執行fn1函式</button>
<script>
    function fn1 () {
        let a = new Array(10000)  // 這里設定了一個很大的陣列物件

        let b = 3

        function fn2() {
            let c = [1, 2, 3]
        }

        fn2()

        return a
    }

    let res = []  

    function myClick() {
        res.push(fn1())
    }
</script>

設定了一個按鈕,每次執行就會將fn1函式的回傳值添加到全域陣列變數res中,是為了能在performacne的曲線圖中看出效果,如圖所示:

img

在每次錄制開始時手動觸發一次垃圾回識訓制,這是為了確認一個初始的堆記憶體基準線,便于后面的對比,然后我們點擊了幾次按鈕,即往全域陣列變數res中添加了幾個比較大的陣列物件,最后再觸發一次垃圾回收,發現錄制結果的JS Heap曲線剛開始成階梯式上升的,最后的曲線的高度比基準線要高,說明可能是存在記憶體泄漏的問題

在得知有記憶體泄漏的情況存在時,我們可以改用Memory來更明確得確認問題和定位問題

首先可以用Allocation instrumentation on timeline來確認問題,如下圖所示:

img

在我們每次點擊按鈕后,動態記憶體分配情況圖上都會出現一個藍色的柱形,并且在我們觸發垃圾回收后,藍色柱形都沒變成灰色柱形,即之前分配的記憶體并未被清除

所以此時我們就可以更明確得確認記憶體泄漏的問題是存在的了,接下來就精準定位問題,可以利用Heap snapshot來定位問題,如圖所示:

img

第一次先點擊快照記錄初始的記憶體情況,然后我們多次點擊按鈕后再次點擊快照,記錄此時的記憶體情況,發現從原來的1.1M記憶體空間變成了1.4M記憶體空間,然后我們選中第二條快照記錄,可以看到右上角有個All objects的欄位,其表示展示的是當前選中的快照記錄所有物件的分配情況,而我們想要知道的是第二條快照與第一條快照的區別在哪,所以選擇Object allocated between Snapshot1 and Snapshot2,即展示第一條快照和第二條快照存在差異的記憶體物件分配情況,此時可以看到Array的百分比很高,初步可以判斷是該變數存在問題,點擊查看詳情后就能查看到該變數對應的具體資料了

以上就是一個判斷閉包帶來記憶體泄漏問題并簡單定位的方法了

2.全域變數

全域的變數一般是不會被垃圾回收掉的,在文章開頭也提到過了,當然這并不是說變數都不能存在全域,只是有時候會因為疏忽而導致某些變數流失到全域,例如未宣告變數,卻直接對某變數進行賦值,就會導致該變數在全域創建,如下所示:

function fn1() {
    // 此處變數name未被宣告
    name = new Array(99999999)
}

fn1()

此時這種情況就會在全域自動創建一個變數name,并將一個很大的陣列賦值給name,又因為是全域變數,所以該記憶體空間就一直不會被釋放

解決辦法的話,自己平時要多加注意,不要在變數未宣告前賦值,或者也可以開啟嚴格模式,這樣就會在不知情犯錯時,收到報錯警告,例如:

function fn1() {
    'use strict';
    name = new Array(99999999)
}

fn1()

3.分離的DOM節點

什么叫DOM節點?假設你手動移除了某個dom節點,本應釋放該dom節點所占用的記憶體,但卻因為疏忽導致某處代碼仍對該被移除節點有參考,最終導致該節點所占記憶體無法被釋放,例如這種情況:

<div id="root">
    <div class="child">我是子元素</div>
    <button>移除</button>
</div>
<script>

    let btn = document.querySelector('button')
    let child = document.querySelector('.child')
    let root = document.querySelector('#root')
    
    btn.addEventListener('click', function() {
        root.removeChild(child)
    })

</script>

該代碼所做的操作就是點擊按鈕后移除.child的節點,雖然點擊后,該節點確實從dom被移除了,但全域變數child仍對該節點有參考,所以導致該節點的記憶體一直無法被釋放,可以嘗試用Memory的快照功能來檢測一下,如圖所示:

img

同樣的先記錄一下初始狀態的快照,然后點擊移除按鈕后,再點擊一次快照,此時記憶體大小我們看不出什么變化,因為移除的節點占用的記憶體實在太小了可以忽略不計,但我們可以點擊第二條快照記錄,在篩選框里輸入detached,于是就會展示所有脫離了卻又未被清除的節點物件

解決辦法如下圖所示:

<div id="root">
    <div class="child">我是子元素</div>
    <button>移除</button>
</div>
<script>
    let btn = document.querySelector('button')

    btn.addEventListener('click', function() {  
        let child = document.querySelector('.child')
        let root = document.querySelector('#root')

        root.removeChild(child)
    })

</script>

改動很簡單,就是將對.child節點的參考移動到了click事件的回呼函式中,那么當移除節點并退出回呼函式的執行上文后就會自動清除對該節點的參考,那么自然就不會存在記憶體泄漏的情況了,我們來驗證一下,如下圖所示:

img

結果很明顯,這樣處理過后就不存在記憶體泄漏的情況了

4.控制臺的列印

控制臺的列印也會造成記憶體泄漏嗎????是的呀,如果瀏覽器不一直保存著我們列印物件的資訊,我們為何能在每次打開控制的Console時看到具體的資料呢?先來看一段測驗代碼:

<button>按鈕</button>
<script>
    document.querySelector('button').addEventListener('click', function() {
        let obj = new Array(1000000)

        console.log(obj);
    })
</script>

我們在按鈕的點擊回呼事件中創建了一個很大的陣列物件并列印,用performance來驗證一下:

img

開始錄制,先觸發一次垃圾回收清除初始的記憶體,然后點擊三次按鈕,即執行了三次點擊事件,最后再觸發一次垃圾回收,查看錄制結果發現JS Heap曲線成階梯上升,并且最終保持的高度比初始基準線高很多,這說明每次執行點擊事件創建的很大的陣列物件obj都因為console.log被瀏覽器保存了下來并且無法被回收

接下來注釋掉console.log,再來看一下結果:

<button>按鈕</button>
<script>
    document.querySelector('button').addEventListener('click', function() {
        let obj = new Array(1000000)

        // console.log(obj);
    })
</script>

performance如圖所示:

img

可以看到沒有列印以后,每次創建的obj都立馬被銷毀了,并且最終觸發垃圾回識訓制后跟初始的基準線同樣高,說明已經不存在記憶體泄漏的現象了

其實同理,console.log也可以用Memory來進一步驗證

  • 未注釋console.log

img

  • 注釋掉了console.log

img

最后簡單總結一下:在開發環境下,可以使用控制臺列印便于除錯,但是在生產環境下,盡可能得不要在控制臺列印資料,所以我們經常會在代碼中看到類似如下的操作:

// 如果在開發環境下,列印變數obj
if(isDev) {
    console.log(obj)
}

這樣就避免了生產環境下無用的變數列印占用一定的記憶體空間,同樣的除了console.log之外,console.errorconsole.infoconsole.dir等等都不要在生產環境下使用

5.遺忘的定時器

其實定時器也是平時很多人會忽略的一個問題,比如定義了定時器后就再也不去考慮清除定時器了,這樣其實也會造成一定的記憶體泄漏,來看一個代碼示例:

<button>開啟定時器</button>
<script>

    function fn1() {
        let largeObj = new Array(100000)

        setInterval(() => {
            let myObj = largeObj
        }, 1000)
    }

    document.querySelector('button').addEventListener('click', function() {
        fn1()
    })
</script>

這段代碼是在點擊按鈕后執行fn1函式,fn1函式內創建了一個很大的陣列物件largeObj,同時創建了一個setInterval定時器,定時器的回呼函式只是簡單的參考了一下變數largeObj,我們來看看其整體的記憶體分配情況吧:

img

按道理來說點擊按鈕執行fn1函式后會退出該函式的執行背景關系,緊跟著函式體內的區域變數應該被清除,但圖中performance的錄制結果顯示似乎是存在記憶體泄漏問題的,即最終曲線高度比基準線高度要高,那么再用Memory來確認一次:

img

在我們點擊按鈕后,從動態記憶體分配的圖上看到出現一個藍色柱形,說明瀏覽器為變數largeObj分配了一段記憶體,但是之后這段記憶體并沒有被釋放掉,說明的確存在記憶體泄漏的問題,原因其實就是因為setInterval的回呼函式內對變數largeObj有一個參考關系,而定時器一直未被清除,所以變數largeObj的記憶體也自然不會被釋放

那么我們如何來解決這個問題呢,假設我們只需要讓定時器執行三次就可以了,那么我們可以改動一下代碼:

<button>開啟定時器</button>
<script>
    function fn1() {
        let largeObj = new Array(100000)
        let index = 0

        let timer = setInterval(() => {
            if(index === 3) clearInterval(timer);
            let myObj = largeObj
            index ++
        }, 1000)
    }

    document.querySelector('button').addEventListener('click', function() {
        fn1()
    })
</script>

現在我們再通過performancememory來看看還不會存在記憶體泄漏的問題

  • performance

img

這次的錄制結果就能看出,最后的曲線高度和初始基準線的高度一樣,說明并沒有記憶體泄漏的情況

  • memory

img

這里做一個解釋,圖中剛開始出現的藍色柱形是因為我在錄制后重繪了頁面,可以忽略;然后我們點擊了按鈕,看到又出現了一個藍色柱形,此時就是為fn1函式中的變數largeObj分配了記憶體,3s后該記憶體又被釋放了,即變成了灰色柱形,所以我們可以得出結論,這段代碼不存在記憶體泄漏的問題

簡單總結一下: 大家在平時用到了定時器,如果在用不到定時器后一定要清除掉,否則就會出現本例中的情況,除了setTimeoutsetInterval,其實瀏覽器還提供了一個API也可能就存在這樣的問題,那就是requestAnimationFrame

總結

在專案程序中,如果遇到了某些性能問題可能跟記憶體泄漏有關時,就可以參照本文列舉的5種情況去排查,一定能找到問題所在并給到解決辦法的,

雖然JavaScript的垃圾回收是自動的,但我們有時也是需要考慮要不要手動清除某些變數的記憶體占用的,例如你明確某個變數在一定條件下再也不需要,但是還會被外部變數參考導致記憶體無法得到釋放時,你可以用null對該變數重新賦值就可以在后續垃圾回收階段釋放該變數的記憶體了,

最后,歡迎大家關注公眾號:前端印象,關注即可第一時間閱讀,

另外有任何技術方面的問題,也可以加我主頁聯系方式

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

標籤:其他

上一篇:JavaScript基礎入門

下一篇:CSS中em的正確打開方式

標籤雲
其他(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