記vue leaflet的一次canvas渲染爆棧

2022-05-24 05:30:12 字數 1934 閱讀 3351

在地圖上繪製大量的circlemarker,leaflet能選擇使用canvas來渲染,比起預設的svg渲染來說在大量繪製的情況下會更加流暢。但當觸發其中某乙個circlemarker的tooltip或popup時,瀏覽器報錯「uncaught rangeerror: maximum call stack size exceeded」:

1. 寫了個測試**來復現問題:

1

2319

2021

test

222386

8788

view code

繪製50000個circlemarker,當滑鼠移動到其中某個marker上時,瀏覽器報錯。

注釋第59行的**,或者把map從vue例項的data裡提取出來放在全域性都不會爆棧,因此現在有兩個問題:

2. 問題1肯定和vue.js的observe函式相關,通過檢視vue.js的**發現

vue.js初始化例項時會呼叫: _init->initstate->initdata->observe(data),在observe函式裡會新建個observe,標註__ob__屬性,如果該值為物件,還會呼叫walk函式來為物件的所有屬性新增observe。

3. 感覺爆棧可能和這個walk有關,但是要怎麼證明呢?修改了一下vue.js原始碼,每次walk的時候都輸出此時的遍歷鏈條,如:}遍歷到c屬性時輸出 a->b->c。

關鍵修改點:

1 observer.prototype.walk = function walk (obj, prefix = '') ->$`);

5 console.log(`$->$`);6}

7 };

同時,observe相關的函式都新增prefix來儲存遍歷的節點資訊

4. 先只繪製5個點來測試一下輸出結果

沒問題,當把滑鼠放在marker上觸發tooltip時,有意思的事情出現了:

注意到_order->prev這個鏈條很長,observe遞迴很深。當修改marker繪製數目為50000後,確實是->prev->不斷的遞迴並爆棧

5、檢視leaflet**發現canvas繪製時會為畫布上的元素新增_order鍊錶屬性來儲存畫布上所有元素的繪製先後順序,方便bringtofront、bringtoback之類的方法實現;當模擬事件觸發時也是通過這個鍊錶來尋找對應的元素。因此當繪製元素過多時,鍊錶太長,vue的observe不斷的遞迴,造成了爆棧現象

6、那麼為什麼只有觸發tooltip/popup的時候才爆棧呢?

因為map.addlayer(marker)時,能夠觸發observe的操作在搭建鍊錶關係之前(新增子圖層this._layers[id]=layer不能觸發observe)。紅框部分的push觸發了observe

7. 所以把map從vue例項的data中拿出來放在外面,map的屬性沒有被observe就不存在爆棧的問題了。而svg渲染時不存在這樣的鍊錶結構,所以也不會爆棧。

8. 為什麼svg不需要這種鍊錶結構?

因為svg可以利用dom api來實現bringtofront/bringtoback之類的操作,而且事件能直接繫結在dom元素上,也不需要遍歷所有元素來判斷哪個元素是事件的觸發物件。而canvas需要使用事件委託來捕獲事件,並遍歷所有元素來判斷具體哪個元素是事件的觸發物件。

記一次的使用

將jsp拆分frame框架,因為採用了第一種方式,一直在考慮用jquery非同步請求獲取資料,總是但不到效果,終於在js寫吐的時候選擇了第二種方式。參考網上的使用,大多是下面這個樣子,如果涉及靜態頁面之間定位,是沒有問題的 href 為目標頁面 通過target定位到frame views main...

記一次除錯

這是我最近幾個月來遇到的最棘手的乙個問題 昨天花了4個小時找出第一層次的原因 這個糾結啊,本來和老婆說好準時下班回家吃飯的,結果被這個問題拖了老久。這是乙個gradle的plugin,用來resolve公司內部的dependency的,弄完了跑測試專案的,拋乙個npe,而且npe還不在自己的 裡面。...

記一次 EqualsAndHashCode的疑惑

lombok的使用真的是讓開發人員欲罷不能,乙個 data不管有多少屬性全部搞定,以後加字段也不用從新生成get和set方法。不過這裡還是有乙個小坑需要注意一下,舉個例子 public class equalsandhashcodetest data noargsconstructor access...