调试Cesium源码分析并解决在Vite中使用遇到的问题

CesiumJS 库与所有构建系统和框架兼容。但是,考虑到渲染引擎的性质,在使用 Vitewebpack 时,您需要采取一些额外的步骤来为您的应用程序配置它。

尽管找各种资料完全可以很快照着完成,但各种配置背后的作用和原理 是什么呢?于是本文总结在 Vite 中使用CesiumJs详细步骤,并通过debug的方式分析并解决过程中出现的问题。

1. 创建账号获取token

使用Cesium需要注册Cesium ion账户(免费)获取token

2. 下载Cesium资源包

Cesium提供CDNnpm两种方式引入资源包。CDN方式这里不做赘述,看这里

使用npm下载cesium依赖:

复制代码
npm install cesium

3. 引入控件样式文件、设置Ion默认token

widgets.css文件是cesium内置控件的样式,包括导航控件(Navigation)、时间轴控件(Timeline)、动画控件(Animation)、全屏按钮(Fullscreen Button)、地理编码搜索框(Geocoder)和场景模式切换器(SceneModePicker)等。

ts 复制代码
// main.ts
import "cesium/Build/Cesium/Widgets/widgets.css";
import { Ion } from 'cesium';
Ion.defaultAccessToken = 'your token';

3.1 设置Ion Token的作用

访问cesium的一些地图服务需要用到,如请求一些地形数据 (Terrain)、影像数据 (Imagery)、3D Tiles数据等。但如果不设置自己的token,在源码 中可以看到Ion有一个默认的token

js 复制代码
// packages/engine/Source/Core/Ion.js
const defaultAccessToken =
  "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI0ZDdmNWJiNy0wMmNlLTQ1MWUtODM2YS02NGM1MTBlOGMwMWQiLCJpZCI6MjU5LCJpYXQiOjE3MzMxNTc4OTV9.B3URHf0VdHDtGckb-hv7uqATdn8KfvkiuoAFZUq8tAo";
const Ion = {};
Ion.defaultAccessToken = defaultAccessToken;

默认的token请求数量有限;注册用户token会有更多的月度配额;付费的token可以访问更高分辨率的数据等。

4. 创建一个demo_cesium.vue文件:

vue 复制代码
<!-- demo_cesium.vue -->
<template>
    <div id="cesiumContainer"></div>
</template>
<script lang="ts" setup>
import { Cartesian3, createOsmBuildingsAsync, Ion, Terrain, Viewer } from 'cesium';
import { onMounted } from 'vue';

// Initialize the Cesium Viewer in the HTML element with the `cesiumContainer` ID.
onMounted(async () => {
    const viewer = new Viewer('cesiumContainer', {
        terrain: Terrain.fromWorldTerrain(),
    });

    // Fly the camera to San Francisco at the given longitude, latitude, and height.
    viewer.camera.flyTo({
        destination: Cartesian3.fromDegrees(-122.4175, 37.655, 400)
    });

    // Add Cesium OSM Buildings, a global 3D buildings layer.
    const buildingTileset = await createOsmBuildingsAsync();
    viewer.scene.primitives.add(buildingTileset);
})

</script>

<style lang="less" scoped>
#cesiumContainer {
    width: 100%;
    height: 100%;
}
</style>

打开浏览器devtool面板,发现如下报错:

根据报错链接进入source面板断点发现:

显然JSON.parse不能解析html文本,所以报错。在调用堆栈中找到上层函数调用:

发现 Resource.fetchJson 这个地址: "http://192.168.31.212:5173/welcome-to-my-website/node_modules/.vite/deps/Assets/approximateTerrainHeights.json"

那现在出现2个问题:

  1. 为什么fetchJson这个地址最终会变成JSON.parsehtml文本?
  2. "http://192.168.31.212:5173/welcome-to-my-website/node_modules/.vite/deps"这个前缀哪里来的?

第1个问题暂且可以搁置不管,解决报错需要寻找第2个问题的答案 。于是鼠标hoverbuildModuleUrl方法上:

点击[[FunctionLocation]]查看其实现:

同样的方式我们进入getCesiumBaseUrl方法:

发现源码中判断CESIUM_BASE_URL如果为空,就判断当前的模块环境,如果是ESM则是getAbsoluteUri(".", import.meta.url)其结果就是我们想要验证的前缀:"http://192.168.31.212:5173/welcome-to-my-website/node_modules/.vite/deps/"

所以我们要配置CESIUM_BASE_URL

4. 托管Cesium静态资源文件、配置 CESIUM_BASE_URL

4.1 配置CESIUM_BASE_URL的目的

CesiumJS 需要vite服务托管一些静态文件,例如 Web WorkerSVG 图标。那这些资源在哪呢?我们第一步安装cesium时,它们被安装到了node_modules下:

我们发现上图中箭头指向的Assets/approximateTerrainHeights.json文件正是上文debug解析的文件。

4.2 配置 CESIUM_BASE_URL

其实,开发阶段 我们完全可以直接通过 /node_modules/cesium ...去访问静态资源:

所以理论上我们将window.CESIUM_BASE_URL设置为/welcome-to-my-website/node_modules/cesium/Build/Cesium即可成功,验证如下:

ts 复制代码
// vite.config.ts

export default defineConfig({
    base: '/welcome-to-my-website',
    define: {
        CESIUM_BASE_URL: JSON.stringify(`/welcome-to-my-website/node_modules/cesium/Build/Cesium`),
    }
});

确实可以成功:

但既然使用了构建工具就必须考虑生成环境的打包路径。 正常打包vite会将所有静态资源都打包在assets下:

因此我们需要在开发环境生产环境 可以用同样的路径访问到 Cesium 静态资源,而viteStaticCopy包做了这件事。于是我们进行如下配置:

ts 复制代码
// vite.config.ts

import vue from "@vitejs/plugin-vue";
import { viteStaticCopy } from "vite-plugin-static-copy";

const base = "/welcome-to-my-website";

const cesiumSource = "node_modules/cesium/Build/Cesium";
const cesiumBaseUrl = "cesiumStatic";

export default defineConfig({
    base,
    define: {
        CESIUM_BASE_URL: JSON.stringify(`${base}/${cesiumBaseUrl}`),
    },
    plugins: [
        vue(),
        viteStaticCopy({
            targets: [
                { src: `${cesiumSource}/ThirdParty`, dest: cesiumBaseUrl },
                { src: `${cesiumSource}/Workers`, dest: cesiumBaseUrl },
                { src: `${cesiumSource}/Assets`, dest: cesiumBaseUrl },
                { src: `${cesiumSource}/Widgets`, dest: cesiumBaseUrl },
            ],
        }),
    ]
});

此时通过 http://192.168.31.212:5173/welcome-to-my-website/cesiumStatic/Assets/Images/ion-credit.png 也可以访问到静态资源:

再次打包发现新建了一个cesiumStatic目录存放cesium静态资源:

这样就可以更优雅的使用Cesium了:

4.3 简单概括viteStaticCopy做的事情:

  • 开发环境:映射访问,不复制 ➡️ 更快的开发体验

  • 生产环境:实际复制 ➡️ 确保部署后可用

最后:

其实,无论你是使用原生 html 还是 vite 又或者是 webpack,看 以下资料 就足以快速上手 Cesium。本文更想探寻的是 "照着写" 背后的原因、"不这么写为什么不行" 以及 "还可以怎么写"

  1. CesiumJS Quickstart
  2. Configuring Vite or Webpack for CesiumJS
  3. cesium-webpack-example
  4. cesium-vite-example
相关推荐
艾小逗1 小时前
vue3中的effectScope有什么作用,如何使用?如何自动清理
前端·javascript·vue.js
小小小小宇4 小时前
手写 zustand
前端
Hamm4 小时前
用装饰器和ElementPlus,我们在NPM发布了这个好用的表格组件包
前端·vue.js·typescript
小小小小宇5 小时前
前端国际化看这一篇就够了
前端
大G哥5 小时前
PHP标签+注释+html混写+变量
android·开发语言·前端·html·php
whoarethenext5 小时前
html初识
前端·html
小小小小宇5 小时前
一个功能相对完善的前端 Emoji
前端
m0_627827525 小时前
vue中 vue.config.js反向代理
前端
Java&Develop5 小时前
onloyoffice历史版本功能实现,版本恢复功能,编辑器功能实现 springboot+vue2
前端·spring boot·编辑器
白泽talk5 小时前
2个小时1w字| React & Golang 全栈微服务实战
前端·后端·微服务