vue單頁應(yīng)用的內(nèi)存泄露定位和修復(fù)問題小結(jié)
在前端項(xiàng)目(PC端)中,內(nèi)存泄露的定位往往比修復(fù)更加困難,即使google瀏覽器有提供Memory工具,但是面對(duì)成千上萬的元素和錯(cuò)綜復(fù)雜的引用關(guān)系,開發(fā)則依然很難快速定位到問題代碼塊。
一、什么是內(nèi)存泄漏?
系統(tǒng)進(jìn)程不再用到的內(nèi)存,沒有及時(shí)釋放,就叫做內(nèi)存泄漏(memory leak)。當(dāng)內(nèi)存占用越來越高,輕則影響系統(tǒng)性能,重則導(dǎo)致進(jìn)程崩潰。Chrome限制了瀏覽器所能使用的內(nèi)存極限(64位為1.4GB,32位為1.0GB),這也就意味著瀏覽器將無法直接操作一些大內(nèi)存對(duì)象。
V8引擎在執(zhí)行垃圾回收時(shí)會(huì)阻塞 JavaScript應(yīng)用邏輯,直到垃圾回收結(jié)束再重新執(zhí)行JavaScript應(yīng)用邏輯,這種行為被稱為“全停頓”(stop-the-world)。 若V8的堆內(nèi)存為1.5GB,V8做一次小的垃圾回收需要50ms以上,造成假死現(xiàn)象。
二、JS內(nèi)存管理和垃圾回收機(jī)制GC
高級(jí)語言基本都有垃圾回收機(jī)制(garbage collection)自動(dòng)管理內(nèi)存,降低程序員的負(fù)擔(dān),以達(dá)到解決內(nèi)存泄漏的目的,但是不允許人為手動(dòng)觸發(fā),無法對(duì)內(nèi)存管理進(jìn)行任何干預(yù)。
老版本的瀏覽器使用引用計(jì)數(shù)法(Reference Counting)來管理內(nèi)存,即每次引用加一,被釋放時(shí)減一,當(dāng)這個(gè)值的引用次數(shù)變成 0 時(shí),就可以將其內(nèi)存空間回收,缺點(diǎn)是循環(huán)引用時(shí)無法回收。
現(xiàn)代瀏覽器基本采用標(biāo)記清除法(Mark-and-Sweep)來管理內(nèi)存,即瀏覽器周期性地從某個(gè)根元素(譬如 window 對(duì)象)開始找引用變量,及這些變量引用的變量,這樣一直找下去。能找到的變量為可獲得變量,不能找到的將被內(nèi)存回收。

缺點(diǎn)是清除后內(nèi)存會(huì)產(chǎn)生很多細(xì)化的分塊,所以又衍生了標(biāo)記-整理法,不細(xì)講。

三、VUE中容易出現(xiàn)內(nèi)存泄露的幾種情況
內(nèi)存泄露是一個(gè)累積的過程,只有頁面生命周期略長(zhǎng)的時(shí)候才暴露出問題,頻繁交互能夠加快累積的過程,偏展示的頁面很難把這樣的問題暴露出來(所謂刷新一下又能滿血復(fù)活)。所以很多時(shí)候我們都是被動(dòng)式的等待問題暴露然后進(jìn)行排查的,主動(dòng)式的分析通常比較難。vue頁面大多是單頁應(yīng)用,高交互且停留時(shí)間久,處理不好很容易出現(xiàn)內(nèi)存泄漏。本文章 主要針對(duì)游離的dom對(duì)象進(jìn)行排查 ,普通的JS變量排查有時(shí)間再補(bǔ)充。
1.全局變量造成的內(nèi)存泄露
<template>
<div id="home">
這里是首頁
</div>
</template>
<script>
export default {
mounted () {
window.test = { // 此處在全局window對(duì)象中引用了本頁面的dom對(duì)象
name: 'home',
node: document.getElementById('home')
}
}
}
</script>
按下Heap snapshots鍵,搜索Detached,發(fā)現(xiàn)沒有脫離文檔樹的dom元素,屬于正?,F(xiàn)象

改變路由跳轉(zhuǎn)到other頁面,按下Heap snapshots鍵,搜索Detached,發(fā)現(xiàn)有兩處dom元素游離于當(dāng)前頁面之外,很明顯是window對(duì)象引用了home頁面中的div,即使此時(shí)home頁面已經(jīng)銷毀,home中的dom元素卻還駐留在內(nèi)存中無法釋放。

解決方案就是在頁面卸載的時(shí)候順便處理掉該引用。
<template>
<div id="home">
這里是首頁
</div>
</template>
<script>
export default {
mounted () {
window.test = { // 此處在全局window對(duì)象中引用了本頁面的dom對(duì)象
name: 'home',
node: document.getElementById('home')
}
},
destroyed () {
window.test = null // 頁面卸載的時(shí)候解除引用
}
}
</script>
2.除了直接引用,window的原生方法也會(huì)起到引用dom元素使其無法釋放的效果。
<template>
<div id="home">這里是首頁</div>
</template>
<script>
export default {
mounted () {
window.addEventListener('resize', this.func) // window對(duì)象引用了home頁面的方法
},
methods: {
func () {
console.log('這是home頁面的函數(shù)')
}
}
}
</script>

解決方法一樣,也是在頁面銷毀的時(shí)候,順便解除引用,釋放內(nèi)存
mounted () {
window.addEventListener('resize', this.func)
},
beforeDestroy () {
window.removeEventListener('resize', this.func)
}
3.一些全局的方法使用不當(dāng)也會(huì)造成內(nèi)存無法釋放,在頁面卸載的時(shí)候也可以考慮解除引用
<template>
<div id="home">這里是首頁</div>
</template>
<script>
export default {
mounted () {
this.$EventBus.$on('homeTask', res => this.func(res))
},
methods: {
func (res) {
console.log(res)
}
}
}
</script>

mounted () {
this.$EventBus.$on('homeTask', res => this.func(res))
},
destroyed () {
this.$EventBus.$off()
}
造成游離dom節(jié)點(diǎn)的原因還有很多,不止這三種,總結(jié)起來:
1.window對(duì)象、事件總線、全局vuex上綁定了已銷毀頁面上的節(jié)點(diǎn),到時(shí)節(jié)點(diǎn)不隨頁面一起銷毀
2.使用第三方庫創(chuàng)建實(shí)例,第三方庫一般會(huì)提供銷毀函數(shù),頁面跳轉(zhuǎn)時(shí)沒有調(diào)用正確的銷毀函數(shù)
3.有同學(xué)會(huì)說在頁面中使用閉包也會(huì)造成內(nèi)存泄露,在vue框架里有管理內(nèi)存的機(jī)制,只要按照它的正確編寫方法,理論上是不會(huì)造成內(nèi)存泄露的
總結(jié)
以上所述是小編給大家介紹的vue單頁應(yīng)用的內(nèi)存泄露定位和修復(fù)問題小結(jié),希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
如果你覺得本文對(duì)你有幫助,歡迎轉(zhuǎn)載,煩請(qǐng)注明出處,謝謝!
相關(guān)文章
vue使用echarts實(shí)現(xiàn)中國地圖和點(diǎn)擊省份進(jìn)行查看功能
這篇文章主要介紹了vue使用echarts實(shí)現(xiàn)中國地圖和點(diǎn)擊省份進(jìn)行查看功能,本文通過實(shí)例代碼給大家詳細(xì)講解,對(duì)vue echarts 中國地圖相關(guān)知識(shí)感興趣的朋友一起看看吧2022-12-12
利用vue3自己實(shí)現(xiàn)計(jì)數(shù)功能組件封裝實(shí)例
組件(Component) 是Vue.js最強(qiáng)大的功能之一,組件可以擴(kuò)展 HTML 元素,封裝可重用的代碼,這篇文章主要給大家介紹了關(guān)于利用vue3自己實(shí)現(xiàn)計(jì)數(shù)功能組件封裝的相關(guān)資料,需要的朋友可以參考下2021-09-09
treeSelect樹組件設(shè)置父節(jié)點(diǎn)禁用的方法實(shí)例
這篇文章主要給大家介紹了關(guān)于treeSelect樹組件設(shè)置父節(jié)點(diǎn)禁用的相關(guān)資料,文中通過圖文以及示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-12-12
Vue.sync修飾符與$emit(update:xxx)詳解
這篇文章主要介紹了Vue.sync修飾符與$emit(update:xxx),實(shí)現(xiàn)思路非常簡(jiǎn)單,文章介紹了.sync修飾符的作用和使用.sync修飾符的寫法,實(shí)現(xiàn)代碼簡(jiǎn)單易懂對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-11-11
vue3中的reactive函數(shù)聲明數(shù)組方式
這篇文章主要介紹了vue3中的reactive函數(shù)聲明數(shù)組方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05
Vue中使用?Aplayer?和?Metingjs?添加音樂插件的方式
這篇文章主要介紹了Vue中使用?Aplayer?和?Metingjs?添加音樂插件,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08
Vue.js實(shí)現(xiàn)可排序的表格組件功能示例
這篇文章主要介紹了Vue.js實(shí)現(xiàn)可排序的表格組件功能,涉及vue.js事件響應(yīng)、元素動(dòng)態(tài)操作、排序、運(yùn)算等相關(guān)操作技巧,需要的朋友可以參考下2019-02-02
vue項(xiàng)目中axios請(qǐng)求網(wǎng)絡(luò)接口封裝的示例代碼
這篇文章主要介紹了vue項(xiàng)目中axios請(qǐng)求網(wǎng)絡(luò)接口封裝的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-12-12

