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