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();
}
},