Cesium的3D在多個單頁面應用中,記憶體只增不減致記憶體溢位問題的解決

krify發表於2019-02-16

1、背景:
專案使用的語言是vue+iview,因為用到了3D,所以找公司買了3d地圖的產品,但是問題隨之而來。把我們專案需要用到的3d地圖封裝成一個元件叫3dMap.vue,方便各個頁面呼叫,vue的工作機制是在離開當前頁面的時候把當前頁面進行銷燬,但是由於Cesium的特性,他並沒有沒銷燬,每當訪問一次,就會重新new一個Cesium.

const Viewer = new Cesium.Viewer("newID", {
    navigation: this.navigation,
    infoBox: this.infoBox
});

檢視計算機程式會發現,chrome瀏覽器會同事跑6個以上程式,其中一個是Cesium的,它所佔用的記憶體會隨著訪問次數的增加不斷上漲。
2、解決思路:
既然每訪問一次3dMap.vue就會new一個Cesium,那能不能就創造一個全域性的Cesium,讓他一直存在,通過顯示與隱藏去控制在每個單頁面應用中的顯示呢。
3、動手:
·3.1首先建立一個全域性的Cesium,起名global3D.vue,這個只是用來初始化

<script>
    import Cesium from "Cesium";
    import aa from `../../static/serverconfig.json`
    import Vue from `vue`;
    //建立一個div,用來做3d的盒子
    const _div = document.createElement("div");
    _div.id = "newID";
    _div.style.display = "none";
    //掛載在body裡面,因為整個系統載入的時候,頁面只有body,其他元素都還未載入
    document.body.appendChild(_div);
    //用Viewer來接收
    const Viewer = new Cesium.Viewer("newID", {
        navigation: this.navigation,
        infoBox: this.infoBox
    })
    
    //中間部分寫初始化3d需要的方法
    
    //把Viewer丟擲去
    export default {
        Viewer,
    };
</script>

3.2在main.js中進行掛載

import global_ from `./components/global3D`
Vue.prototype.GLOBAL = global_

3.3 建立一個3DViewer.vue,用來接收全域性的Cesium,這個檔案中可以寫一些設定相機視角,獲取經緯度啊等等方法,

export default {
    data() {
        return {
            //接收全域性的Viewer,這個Viewer是Cesium new 出來的
            viewer: this.GLOBAL.Viewer,
            scene: this.GLOBAL.Viewer.scene
        };
    },
 }

3.4 單頁面應用

<template>
    <div class="GISbox" id="GISbox" ref="gisBox">
        <SmViewer ref="TestSmViewer"></SmViewer>
    </div>    
</template>
<script>
    import SmViewer from "../../../../components/Common/3D/3DViewer";
    export default{
        components: {SmViewer},
        mounted() {
            this.setGIS();
        },
        methods: {
            setGIS() {
                //獲取到3d的盒子
                var gis = document.getElementById("newID");
                //3d在頁面中的樣式,可根據自己的需求進行調整
                gis.style.display = "block";
                gis.style.position = "absolute";
                gis.style.top = "0px";
                gis.style.height = "100%";
                gis.style.width = "99%";
                //從body中移除
                document.body.removeChild(gis);
                //在本頁面中的制定位置進行掛載
                document.getElementById("GISbox").appendChild(gis);
                // 載入視角,即寫在3DViewer.vue中的方法,父調子
                this.$refs.TestSmViewer.setViewAngAngle();
            },
            destory3D() {
                //獲取到3d的盒子
                var gis = document.getElementById("newID");
                //隱藏
                gis.style.display = "none";
                //從當前頁面中移除
                document.getElementById("GISbox").removeChild(gis);
                //重新掛載回body
                document.body.appendChild(gis);
            }
        },
        beforeDestory(){
            //在本頁面銷燬前進行這一步操作
            this.destory3D()
        } 
</script>

3.4 如果你的3d只是應用在不同的模組中,且這些模組之間沒有共同的元件,如下圖,在demo1模組中,demo1Page1和demo1Page2共同使用demo1Menu,且只有demo1Page1頁面使用3d元件,demo2同,那麼到3.3,就可以完美的解決這個問題,但是,如果你的3d是應該在同一個模組,且有共同的元件,那麼在不同頁面之間跳轉的時候就會出現問題。比如,在demo1模組中,demo1Page1和demo1Page2共同使用demo1Menu,且demo1Page1頁面和demo1Page2頁面都使用了3d元件,demo2同,那麼在由demo1裡面的頁面跳向demo2裡面的頁面的時候,3d就會丟失了

demo1                    demo2
    demo1Menu                demo2Menu
    demo1Page1               demo2Page1
    demo1Page2               demo2Page2

3.5 解決
解決這個問題主要是使用的vue的keep-Alive,
首先,在App.vue中,做判斷,如果使用了keep-Alive,則走第一個,否則的話,走第二個

  <keep-alive>
    <router-view v-if="$route.meta.keepAlive"></router-view>
  </keep-alive>
  <router-view v-if="!$route.meta.keepAlive"></router-view>

然後,在router裡面,對需要被快取的模組進行設定

           {
                path: `homePage`,
                component: UMPatrolHomePage,
                name: `UMPatrolHomePage`,
                meta: {
                    keepAlive: true //需要被快取
                }
            }

最後,在單頁面中寫入如下,to是要去的那個頁面的路徑,from是從哪個頁面來的那個路徑,next()必須執行,否則跳轉會被攔截,如果要去的頁面或者from的頁面使用了3d,則這個頁面需要被快取,即keep.Alive=true,成功快取後,然後執行銷燬操作,這樣在不同頁面之間切換的時候,就不會出現3d丟失的情況。原理感興趣的同學可以自行搜尋,網上有很多詳細解說的文章

beforeRouteLeave(to, from, next) {
    if (
        to.name == "UMPatrolHomePage" ||
        to.name == "UMDetailEquipment" ||
        to.name == "虛擬巡檢" ||
        to.name == "人員定位詳情" ||
        to.name == "管廊安防監控列表" ||
        to.name == "管廊環境監控列表" ||
        from.name == "人員定位詳情" ||
        from.name == "虛擬巡檢" ||
        from.name == "UMDetailEquipment" ||
        from.name == "UMPatrolHomePage" ||
        from.name == "管廊安防監控列表" ||
        from.name == "管廊環境監控列表"
    ) {
        from.meta.keepAlive = true;
        to.meta.keepAlive = true;
        this.$destroy();
        next();
    } else {
        from.meta.keepAlive = false;
        to.meta.keepAlive = false;
        this.$destroy();
        next();
    }
},

相關文章