Vue依賴收集引發的問題


問題背景

在我們的項目中有一個可視化配置的模塊,是通過go.js生成canvas來實現的。但是,我們發現這個模塊在瀏覽器中經常會引起該tab頁崩潰。開啟chrome的任務管理器一看,進入該頁面內存和cpu就會暴漲,內存經常會飆到700多M。但是我們的canvas實際的像素只有約500x500,根據一些粗略的計算,大概只占了1M的內存,這個計算過程可參考100*100的 canvas 占多少內存。那么我們這700M內存是哪里來的呢?

定位問題

我們可以使用chrome開發者工具來分析我們的調用棧。這邊我是先通過Performance來幫助我們定位問題,它會幫我們生成一段過程中一些數據的變化,包括js堆內存、dom節點數量、動畫幀等數據,如圖:

這是切換至一個canvas畫布較大的一個模塊的performance分析表現,可以看到占用了472M的內存。下面折線圖藍色部分是js堆內存的變化,而Main下面黃色與紫色的矩形框就是我們的調用棧,上下兩部分是按照時間一一對應的。可以看到,藍色的折線呈高低起伏的態勢,GC回收之后低點基本和高點持平,因此可以斷定幾乎不存在內存泄漏的問題。然后我們可以放大去看一看,內存升高的時候,js做了些什么事情,找一找規律。

我們隨機找一段內存增長的區域,可以看到在內存增長的過程中,最為頻繁調用的就是Observer相關的代碼。但是就這么看,我們不能夠明白Observer是在干什么。此時我們可以借用Memory選項中的Allocation Sampling按照javascript function來查看內存分配,我們同樣錄制以上的一段操作。

此時我們能夠清楚的看到,的確是這個Observer在作怪。同時,我們可以看到這是vue的代碼,點擊右邊的文件查看source code,就可以清楚的明白這就是vue在執行依賴收集的操作,此時會給屬性添加watcher。那我們這里為什么會有如此多的屬性被添加了watcher呢?看了一下代碼,原來是我把go.js的一個實例掛到了vue的data選項中,放到data中的屬性會被vue執行依賴收集的相關操作,而這個實例擁有非常多的嵌套屬性,全部都會被添加watcher。其實,我們只是想單純的存儲一下這個實例,供我們后續調用其相關的方法,添加watcher對我們來說完全沒有意義,那我們如何避免這樣的問題呢?

解決問題

上網搜索了相關的解決方案,大概有如下幾種:

  1. 在data中定義的屬性前面加上$,即通知vue該屬性不需要被依賴收集,例如:```javascript
    data() {
    return {
    $goDiagram: null
    }
    }
2. 不在data中聲明,直接在賦值的時候聲明this.goDiagram = diagram。這同樣會遇到第一種方案的問題,模板中會提示找不到goDiagram屬性。
3. 不在data中聲明,而是利用$options來存儲goDiagram,例如:
```javascript
export default {
    goDiagram: null,
    mounted() {
        this.$options.goDiagram = xxx
    }
}

這應該是比較好的一個方法,vue官方中也說明了$options用來包含自定義屬性,例如我們平時引入的常量或是枚舉類型,我們也不希望它們被添加無意義的watcher,因此可以通過這種方式來定義,在template中引用時只需要{{$options.xxx}}即可。這種方式唯一的缺點就是不能像data那樣一眼望去就能清楚地知道你定義了什么屬性。
項目中我采用了第一種方式,經過修改后內存占用量減少到原來的1/5到1/6,可以說效果非常好,再也不會出現瀏覽器崩潰的情況了。

總結

通過這樣的一個問題,我們主要能夠學習到兩點:

  1. 如何通過chrome的開發者工具,去快速地定位代碼中存在的內存問題
  2. 不要盲目的將屬性都掛載到data選項中,一些常量我們可以采取上面提到的幾種方式來定義,以此來作為一種優化手段


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM