跳至主要內容

12、创建地球

祭司唐大约 2 分钟

12、创建地球

承接第一节的Cesium加载地球与环境搭建,需要注意几个点:

1、Vue的响应式与地图、三维等复杂状态对象

1.1、如何避免响应式

Vue 2中,其响应式是通过Object.defineProperty递归实现的,在Vue 2的文档中也明确指出:

dataVue实例的数据对象,Vue会递归地吧dataproperty转换为getter/setter,从而让dataproperty能够响应数据变化。对象必须是纯粹的对象(含有零个或多个Key/value对):浏览器API创建的原生对象,原型上的property会被忽略。大概来说,data应该只能是数据,不推荐观察拥有状态行为的对象。

问题:如何在Vue 2中避免响应式????

Vue 3中,有对应避免深层代理的API,如:shallowRef、shallowReactive、markRaw,这种就适用于

  • GIS类对象:openlayersCesiummapbox GL等等
  • 三维类对象:threejs、babylonjs等等
  • 图形接口对象:WebGLWebGPU等等
  • 任何具备自我行为与复杂内部状态的对象

1.2、Cesium中共享Viewer

1.2.1、pinia全局状态管理

Vue 中使用pinia ,可以把核心的 Viewer 对象送入全局状态中,但是要避免 Vue 的响应式劫持,响应式问题可以通过 Vue3 的 shallowRefshallowReactive 来解决:

<script lang="ts" setup>
import { onMounted, shallowRef, ref } from 'vue'
import { Viewer } from 'cesium'

const viewerDivRef = ref<HTMLDivElement>()
const viewerRef = shallowRef<Viewer>()
onMounted(() => {
  viewerRef.value = new Viewer(viewerDivRef.value as HTMLElement, /* ... */)
})
</script>

或者用 shallowReactive

<script lang="ts" setup>
import { onMounted, shallowReactive, ref } from 'vue'
import { Viewer } from 'cesium'

const viewerDivRef = ref<HTMLDivElement>()
const viewerRef = shallowReactive<{
  viewer: Viewer | null
}>({
  viewer: null
})
onMounted(() => {
  viewerRef.viewer = new Viewer(viewerDivRef.value as HTMLElement, /* ... */)
})
</script>

甚至可以更简单一些:

<script lang="ts" setup>
import { onMounted, ref } from 'vue'
import { Viewer } from 'cesium'

const viewerDivRef = ref<HTMLDivElement>()
let viewer: Viewer | null = null
onMounted(() => {
  viewer = new Viewer(viewerDivRef.value as HTMLElement, /* ... */)
})
</script>
1.2.2、provide/inject

仅适用于地图组件在最顶层的情况:

<!-- 顶层组件下发 Viewer -->
<script lang="ts" setup>
import { onMounted, ref, provide } from 'vue'
import { Viewer } from 'cesium'
import { CESIUM_VIEWER } from '@/symbol'

const viewerDivRef = ref<HTMLDivElement>()
let viewer: Viewer | null = null
onMounted(() => {
  viewer = new Viewer(viewerDivRef.value as HTMLElement, /* ... */)
  provide(CESIUM_VIEWER, viewer)
})
</script>

<!-- 下面是子组件调用 -->
<script lang="ts" setup>
import { inject } from 'vue'
import type { Viewer } from 'cesium'
import { CESIUM_VIEWER } from '@/symbol'

const viewer = inject<Viewer>(CESIUM_VIEWER)
</script>
1.2.3、兄弟组件或父子组件传值
  • defineExpose
  • 层层事件冒泡至父级组件,或者使用全局事件库(如 mitt
  • 使用全局状态 piniavuex

关于全局事件库mitt:

  • 安装mitt:

    npm install mitt -S
    
  • 封装mitt:

    //封装mitt
    import mitt from 'mitt';
    const bus = mitt();
    export default bus;
    
  • 按需引入mitt:

    //将实例提交到事件总线
    bus.emit('toOther',viewer)
    
    //监听数据转发
    bus.on('toOther',(res:Viewer)=>{
    	viewerCopy = res
    });
    
    //移除指定事件
    bus.off('toOther');
    

2、效果图