-
1. 本篇适用范围与目的
- 1.1. 适用范围
- 1.2. 目的
-
2. 牛刀小试 - 先看到地球
- 2.1. 创建 Vue3 - TypeScript 工程并安装 cesium
- 2.2. 清理不必要的文件并创建三维地球
- 2.3. 中段解疑 - 奇怪的路径
- 2.4. 打包部署
- 2.5. 有限的优化
- 3.1. CesiumJS 依赖包中的资料说明
- 4.1. 选择 Vite 的理由
- 5.1. 使用 create-vite 在命令行创建工程
- 6.1. 以 CesiumJS 等库为主的看板式工程
这篇主要修正上篇 https://www.cnblogs.com/onsummer/p/16629036.html 中一些插件的变化,并升级开发服务器的版本。
1. 本篇适用范围与目的
1.1. 适用范围
-
使用原生 CesiumJS 依赖做应用开发
-
客户端渲染,因为我不太熟悉 Vue 的服务端渲染,有本篇的介绍后,熟悉 SSR 的读者可以自己接入
-
单页应用,多页应用也可以参考此法
严格使用 Vue3 + TypeScript 的前端项目,包管理器默认使用 pnpm
pnpm add cesium@latest
有想修改源码再自己打包的读者,我觉得应该去看我的源码系列博客。
1.2. 目的
这篇文章更倾向于给读者一些原理,而不是提供一套开箱即用的工具,有能力的读者可以根据这篇文章的原理,结合 Vite 或其它打包工具的 API,写一个专属插件。
2. 牛刀小试 - 先看到地球
第 2 节不需要知道原理,原理和最佳实践请往下阅读 3、4、5 节。
2.1. 创建 Vue3 - TypeScript 工程并安装 cesium
直接上命令行(要联网,配好你的 npm 源),请在任意你方便的地方运行:
pnpm create vite
输入你想要的手动选择 Vue、TypeScript 的模板即可,然后进入工程文件夹,我的工程文件夹叫作 v3ts-cesium-2023
,所以我接下来要安装 CesiumJS:
cd ./v3ts-cesium-2023
pnpm add cesium@1.104
pnpm add
会一并把模板的其它依赖下载下来,所以就不用再执行pnpm install
了。cesium 时指定了版本,是考虑到 很多项目可能不太注意依赖版本管理,所以干脆锁死固定版本。
2.2. 清理不必要的文件并创建三维地球
src/assets 和
src/components
文件夹,并删除全部src/style.css
的代码,改写main.ts
、App.vue
、style.css
如下:// main.ts import { createApp } from 'vue' import App from './App.vue' import './style.css' declare global { interface Window { CESIUM_BASE_URL: string } } createApp(App.mount('#app'
你注意到了,我在
main.ts
中为全局声明了CESIUM_BASE_URL
变量的类型为string
,这在App.vue
中就会用到:<script setup lang="ts"> import { onMounted, ref } from 'vue' import { TileMapServiceImageryProvider, Viewer, buildModuleUrl } from 'cesium' import 'cesium/Build/CesiumUnminified/Widgets/widgets.css' const viewerDivRef = ref<HTMLDivElement>( window.CESIUM_BASE_URL = 'node_modules/cesium/Build/CesiumUnminified/' onMounted(( => { new Viewer(viewerDivRef.value as HTMLElement, { imageryProvider: new TileMapServiceImageryProvider({ url: 'node_modules/cesium/Build/CesiumUnminified/Assets/Textures/NaturalEarthII', } } } </script> <template> <div id="cesium-viewer" ref="viewerDivRef"></div> </template> <style scoped> #cesium-viewer { width: 100%; height: 100%; } </style>
我在
App.vue
组件的mounted hook
中轻松地创建了Viewer
,语法不再赘述。我做了如下几个点让地球显示出来:
- 向
- 引入 CesiumJS 自己的 css,供 Viewer 的各个内置界面小组件(时间轴等)提供 CSS 样式
- 为
Viewer
创建了一个 CesiumJS 自带的离线 TMS 瓦片服务,你可能很奇怪为什么路径是node_modules
起头的,待会解释,这个 TMS 瓦片服务只有 2 级 - 设定
CESIUM_BASE_URL
Viewer
构造参数传递了 div#cesium-viewer
元素的 ref
值,并将其类型 as HTMLElement
,以满足 CesiumJS 的类型
style.css,是一些简单的样式:
/* style.css */
html, body {
padding: 0;
margin: 0;
}
#app {
height: 100vh;
width: 100vw;
}
随后,命令行启动开发服务器:
pnpm dev
在 Vite4 的强大性能加持下,很快就起起来了,这个时候就可以在浏览器看到一个具有两级离线 TMS 瓦片服务的三维地球:
2.3. 中段解疑 - 奇怪的路径
window.CESIUM_BASE_URL = 'node_modules/cesium/Build/CesiumUnminified/'
new TileMapServiceImageryProvider({
url: 'node_modules/cesium/Build/CesiumUnminified/Assets/Textures/NaturalEarthII',
}
这是因为 Vite 开发模式下(pnpm dev
,NODE_ENV
是 development
)是直接把工程根路径(即 vite.config.ts
所在的文件夹)映射到 http://localhost:5173/
这个 URL 上的,所以理所当然填写 CesiumJS 库文件的路径就要从 node_modules
开始写起。
CesiumUnminified 版本(未压缩版本)。
CESIUM_BASE_URL 的含义是,项目运行的根网络路径(这里就是指 Vite 开发服务器的默认地址 http://localhost:5173/
),加上 CESIUM_BASE_URL
后,在这个拼成的路径就能访问到 CesiumJS 的入口文件,即完整版:
http://localhost:5173/node_modules/cesium/Build/CesiumUnminified/Cesium.js(这个指向的是未压缩版的 IIFE 库文件)
同理,自带的 TMS 瓦片数据就存放在 http://localhost:5173/node_modules/cesium/Build/CesiumUnminified/Assets/Textures/NaturalEarthII
地址下,TMS 服务的识别方法就是观察网络请求有无一个 tilemapresource.xml
文件:
2.4. 打包部署
node_modules 了,毕竟 Vite 的开发服务器职责已经在 build 后完成。
- 修改
- 修改
TileMapServiceImageryProvider
的离线 TMS 路径
CESIUM_BASE_URL
为生产环境能访问的 CesiumJS 库文件的地址
在修改之前,需要你把 CesiumJS 的四大静态资源文件夹从 node_modules 中拷贝出来,跟着做就行。
node_modules/cesium/Build/CesiumUnminified/ 这个未压缩版本的文件夹下所有内容,即 Assets
、Widgets
、Workers
、ThirdParty
四个文件夹拷贝到 public/libs/cesium/
下(没有就自己创建一下):
然后修改 CESIUM_BASE_URL
和离线 TMS 的地址:
window.CESIUM_BASE_URL = 'libs/cesium/'
new TileMapServiceImageryProvider({
url: 'libs/cesium/Assets/Textures/NaturalEarthII',
}
此时运行 pnpm dev
,依旧是正常的,只不过静态文件资源已经从 node_modules/cesium/Build/CesiumUnminified/
改到了 public/libs/cesium/
下。
public 目录,public 目录的作用请自己查阅 Vite 文档。
pnpm build 然后 pnpm preview
组合了,打包并使用 http 服务预览构建后的产物:
pnpm build && pnpm preview
我的 CPU 是 i5 13600K
,在 7 秒多的打包后紧接着就启动了 4173 端口的服务:
2.5. 有限的优化
有人也许对 Vite 等打包工具比较熟悉,可以配置分包(修改 vite.config.ts
中的配置参数)来辨别打包后的产物各自的体积:
import { defineConfig, splitVendorChunkPlugin } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(, splitVendorChunkPlugin(],
build: {
rollupOptions: {
output: {
manualChunks: {
cesium: ['cesium']
}
}
}
}
}
这样之后打包的产物就略有不同:
splitVendorChunkPlugin( 不添加到
plugins
数组中也可以生效,但是为了尽可能优化打包产物,还是加上了
cesium 依赖分拆到一个块文件中,并没有实质性改变如下事实:
-
node_modules/cesium/Build/CesiumUnminified/ 下的四个静态资源文件夹
-
CESIUM_BASE_URL,如果切换到 CDN 或内网已有 CesiumJS 在线库资源,这个改起来就麻烦许多
Tree-Shaking 减小大小,事实上也没有必要,CesiumJS 的内部是高度耦合的三维渲染器、各种算法,这种高度集成的算法产物保持一致是比较好的(或许官方未来可能有改变,但是至少现在没有),所以在我这里这 “7秒多” 的打包时间毫无必要,在其它打包工具也是一样的(Webpack等)
所以,我将费点篇幅,先介绍 CesiumJS 包 的基本知识,再介绍一些现代前端工具的常识,最后再介绍我认为最合理最灵活的引入方式。
3. CesiumJS 前置知识
3.1. CesiumJS 依赖包中的资料说明
node_modules 下的 cesium
依赖,是 CesiumJS 打包好的“包”,它具备如下资料:
-
IIFE、
ES-Module
、CommonJS
三种格式,每种格式又有压缩代码版本和未压缩版本,分别存放于node_modules/cesium/Build/Cesium/
、node_modules/cesium/Build/CesiumUnminified/
目录下,各种格式各有用途,如果是 CommonJS 环境下,会引用index.cjs
,而如果是 ES-Module 环境下,会引用index.js
;剩下的Cesium.js
则用在 IIFE 环境下。
node_modules/cesium/Source/ 目录下,含一个出口文件 Cesium.js
和一个 TypeScript 类型定义文件 Cesium.d.ts
,出口文件导出的所有模块,也就是真正的源码均来自子包 @cesium/engine
和 @cesium/widgets
(于 1.100 版本变动,将代码分割于子包中)
应用级别的开发,只需要用到打包后的库程序文件以及 TypeScript 类型定义文件就好了。
IIFE 格式里的压缩版本,即 node_modules/cesium/Build/Cesium/Cesium.js
,这个库文件只有 3.7 MB,gzip 压缩后可小于 1 MB,体积控制很不错。
3.2. 构建后的 CesiumJS 库组成 - 主库文件与四大文件夹
CommonJS、IIFE
、ES-Module
三种格式的库文件,文件名有所不同。
node_modules/cesium/Source/ 的出口文件,以及这个出口文件引自的 @cesium/engine
和 @cesium/widgets
子包的代码模块)并不是完整的 cesium
库,cesium
库还包括:
- 一套
- 一套 css 文件,用于
Viewer
下具有 HTML 界面的内置组件的样式表达,例如时间线等组件 - 一套静态资源文件,用于构造默认场景和内置组件,例如 SkyBox 背景图、图标、离线的两级 TMS 数据等
- 一些第三方库,用于 basis 纹理和 draco 数据解码的 WebAssembly 文件以及配套的 WebWorker 文件
WebWorker
,用于参数几何的生成、ktx2 纹理解码、draco 压缩数据解码等多线程任务
node_modules/cesium/Build/ 下的压缩和未压缩版本文件夹下的 Workers
、Widgets
、Widgets
、Assets
四大文件夹。
3.3. 链接库文件和四大文件夹的 CESIUM_BASE_URL 变量
CESIUM_BASE_URL 的作用,它就是告诉已经运行的 CesiumJS 上哪去找四类静态资源。
window.CESIUM_BASE_URL = 'http://localhost:8888/cesium/1.103.0/'
window.CESIUM_BASE_URL = 'https://cdn.bootcdn.net/ajax/libs/cesium/1.103.0/'
不再赘述。
4. 现代前端工具的基本常识
4.1. 选择 Vite 的理由
setup 函数后,带了个货,即 Vite
的最初始版本,应该是 1.0 时代的东西了,那时还和 Vue 是强依赖的,在 Vite2 时才与具体前端框架解耦。
Vite 真的很快,上一篇还是 Vite3,现在已经到 Vite4 了,这更新速度...虽然在 API 和配置上基本没什么变化,应该在 4.x 算是稳定了。
4.2. 为什么外部化引入(External)一个库
社区在普通前端的实践中经常把 Vue、React、Axios 等不需要打包、可以使用高速 CDN 加速的库都外部化了。
External 化需要一些比较繁琐的配置,如果读者认为不需要外部化,任 Vite 把 CesiumJS 再次打包那几秒钟、十几秒钟也无所谓的话,其实也可以不做这一步。
在之后会使用 vite-plugin-externals
插件(注意,有 s 结尾)完成外部化。
4.3. TypeScript 类型提示
cesium 包自带了类型文件,位于 node_modules/cesium/Source/Cesium.d.ts
,你也可以在其 package.json
中找到类型字段。
import { Viewer } from 'cesium'
这也是官方推荐的导入方法,这样导入是具备 TS 类型提示的。
- 如果你在用 Volar 插件来智能提示
- 没有安装
typescript
到开发依赖 - 安装了 typescript 到开发依赖但是工程没有使用开发依赖的 ts,而使用了 VSCode 自己的 ts,这个用
Ctrl + Shift + P
切换一下 ts 版本即可(搜索“Select Typescript” 或直接搜 “Typescript” 选择版本即可),会写入.vscode/settings.json
文件 - 上述问题都排除了,也许是
tsconfig.json
没有包括目标d.ts
文件 - 也有可能某个库压根就没有自带
d.ts
,也没有对应的类型库
.vue
文件,那么你需要去 Vue 官方文档中配置下 “take over” 模式
4.4. 开发服务器的路径与代码中的路径问题
这是一个新手问题,新手在开发工具(例如 Webpack、Vite)的滋润下能非常熟练地从各种地方 import 各种各样的资源,例如 ts、js、json、图片图标、less/css/sass 等资源模块。
import Logo from '@/assets/svg/logo.svg'
这样的路径大概率是配置好 @
指向工程下的 src
目录。
import { ref } from 'vue'
这些看似“不就是这样的吗”的导入实际上是开发工具做的努力。
import Duck from './data/duck.gltf'
import Ball from '@/assets/model/duck.glb'
幸运的是对 glTF 模型已经有了 vite 插件,但是我仍然不推荐你这样引入。
理清楚导入问题后,还有一个新手常犯的问题是把“源码相对路径”当作“运行时的路径”,假设有这么一个代码文件 src/views/home.vue
中创建了一个 3DTiles 数据集对象:
// src/views/home.vue
Cesium3DTileset.fromUrl({
url: '../assets/tileset.json'
}
有的新手把数据就放在了上一级的 src/assets/tileset.json
路径下。这犯了 2 个低级错误:
- 认为相对于当前代码文件的
- 认为 CesiumJS 会帮你处理路径问题
../assets/tileset.json
数据文件路径在运行时也能正常读取
此处塞一行防爬虫文字,原文出自 @岭南灯火,常驻知乎博客园,其余博客社交平台基本有号,想找原文请劳烦搜索一下~~
5. 教程(原理)正文
5.1. 使用 create-vite 在命令行创建工程
这个参考 2.1 和 2.2 小节即可。
5.2. 指定版本安装 cesium
pnpm add cesium
那么在 package.json
中,cesium 依赖的版本号(首次 add 时的最新版)前面就会多一个 ^
:
{
"dependencies": {
"cesium": "^1.104.0"
}
}
除非手动 update
,即 pnpm update cesium@具体版本
,否则 ^
后面的版本号是不会改变的。
- pnpm 是
- npm 是
package-lock.json
- yarn 是
yarn.lock
pnpm-lock.yaml
5.3. 包管理工具锁文件的取舍
这小节可以与 5.2 一起看。锁文件的作用是把各个依赖包的具体版本锁死。
如果 package.json 中各个依赖包的版本都是确定的,项目负责人也能管理起依赖的版本控制,那么其实可以不需要锁文件。
不需要锁文件,且我在安装依赖时明确指定了具体版本(主要是 cesium)。
.npmrc 文件,并写入此配置:
package-lock=false
对于 yarn,则是创建 .yarnrc
文件并写入:
--install.no-lockfile true
如果项目有要求,或者版本管理比较差,我建议还是把锁文件留着并提交到 git 记录中,但是 cesium 的版本,我还是强烈建议确定版本安装:
pnpm add cesium@1.104
5.4. 使用插件外部化 CesiumJS
原理、原因在 4.2 小节,这里主要讲配置。
- 插件① -
rollup-plugin-external-globals
rollup-plugin-external-globals 插件就可以完成打包时外部化:
pnpm add rollup-plugin-external-globals -D
然后是用法:
import { defineConfig, splitVendorChunkPlugin } from 'vite'
import vue from '@vitejs/plugin-vue'
import externalGlobals from 'rollup-plugin-external-globals'
export default defineConfig({
plugins: [vue(, splitVendorChunkPlugin(],
build: {
rollupOptions: {
externalGlobals({
cesium: 'Cesium'
},
},
},
}
也可以用 Vite 插件:
- 插件② -
vite-plugin-externals
(注意有个 s 结尾)
pnpm add vite-plugin-externals -D
用法:
import { defineConfig, splitVendorChunkPlugin } from 'vite'
import vue from '@vitejs/plugin-vue'
import { viteExternalsPlugin } from 'vite-plugin-externals'
export default defineConfig({
plugins: [
vue(,
splitVendorChunkPlugin(,
vitePluginExternals({
// key 是要外部化的依赖名,value 是全局访问的名称,这里填写的是 'Cesium'
// 意味着外部化后的 cesium 依赖可以通过 window['Cesium'] 访问;
// 支持链式访问,参考此插件的文档
cesium: 'Cesium',
}
],
}
上面两个插件任选一个均可,只不过 vite-plugin-externals
在开发模式也会起作用,而 rollup-plugin-external-globals
只会在生产模式(NODE_ENV = production
条件,即构建打包时)对 rollup 起作用。
vite-plugin-externals 插件,因为它两种模式都能起作用。再次启动 pnpm dev
,打开浏览器发现找不到模块:
Vite 启动后会有一个依赖预构建的过程,打开
node_modules/.vite/deps
目录,这里就是预构建的各种代码中导入的依赖包
vitePluginExternals({
cesium: 'Cesium',
}, {
disableInServe: true, // 开发模式时不外部化
}
执行 pnpm build
后,提升显著:
pnpm preview 时,依然会找不到从 cesium
依赖导入的类(注意端口,是 preview 默认的 4173):
cesium 依赖,所以打包后的应用找不到 CesiumJS 的类和 API 了。
怎么办呢?
创建了 Vue3 + TypeScript 项目,并已经在第 2 节通过手动拷贝的方式把四个静态资源文件夹拷贝到 public/libs/cesium/
目录下,配置好了 CESIUM_BASE_URL
让 CesiumJS 能访问到这些静态资源,并成功看到了具有离线 TMS 瓦片的三维地球
那么现在遇到了什么问题?
- 打包后的页面因为外部化
cesium
找不到 CesiumJS 库
如何解决问题?
index.html 不就行了吗?请紧接着 5.5 小节一起解决问题:
5.5. 使用插件自动在 index.html 引入 Cesium.js 库文件
node_modules/cesium/Build/Cesium/Cesium.js 这个压缩版的 IIFE 格式库程序文件复制到 public/libs/cesium/
下,然后在工程入口文件 index.html
中添加一行 script
标签引入库文件:
<head>
<script src="libs/cesium/Cesium.js"></script>
</head>
但是,如果是自己手动写这个标签,执行打包时会收到 Vite 的一句警告:
有很多插件可以修改 index.html
:
vite-plugin-html-config
vite-plugin-insert-html
vite-plugin-html
我这里以 vite-plugin-insert-html
插件为例,在 index.html
的 <head>
标签下插入这个 script
标签:
import { defineConfig, splitVendorChunkPlugin } from 'vite'
import vue from '@vitejs/plugin-vue'
import { insertHtml, h } from 'vite-plugin-insert-html'
export default defineConfig({
plugins: [
vue(,
splitVendorChunkPlugin(,
viteExternalsPlugin({
cesium: 'Cesium',
},
insertHtml({
head: [
h('script', {
src: 'libs/cesium/Cesium.js'
}
]
}
],
}
这样打包时就是绝对完美的消息了:
- 四大静态文件的复制
- CesiumJS 库文件的复制
巧的是,这些资源文件都可以从 cesium
包内拷贝,压缩版的 node_modules/cesium/Build/Cesium
,非压缩版的 node_modules/cesium/Build/CesiumUnminified
,请读者紧接着看 5.6 小节:
5.6. 四大静态文件夹与库文件的拷贝(CDN或独立部署了 CesiumJS 库可省略此步)
有很多可选插件,静态文件复制的插件在 Webpack 也有,叫作 CopyWebpackPlugin
,在 Vite 中我选用 vite-plugin-static-copy
插件:
import { viteStaticCopy } from 'vite-plugin-static-copy'
export default defineConfig({
plugins: [
vue(,
splitVendorChunkPlugin(,
viteExternalsPlugin({
cesium: 'Cesium',
},
viteStaticCopy({
targets: [
{
src: 'node_modules/cesium/Build/CesiumUnminified/Cesium.js',
dest: 'libs/cesium/'
},
{
src: 'node_modules/cesium/Build/CesiumUnminified/Assets/*',
dest: 'libs/cesium/Assets/'
},
{
src: 'node_modules/cesium/Build/CesiumUnminified/ThirdParty/*',
dest: 'libs/cesium/ThirdParty/'
},
{
src: 'node_modules/cesium/Build/CesiumUnminified/Workers/*',
dest: 'libs/cesium/Workers/'
},
{
src: 'node_modules/cesium/Build/CesiumUnminified/Widgets/*',
dest: 'libs/cesium/Widgets/'
},
]
},
insertHtml({
head: [
h('script', {
src: 'libs/cesium/Cesium.js'
}
]
},
], // End of plugins
}
这个 target 中很多路径都是相同的,可以通过数组计算完成,这里就留给读者自己改进了。dest
是打包后的根路径的相对路径。
你都能看到这四个静态文件夹的复制步骤。
5.7. 额外优化 - 使用环境变量配置 CESIUM_BASE_URL 并适配其它配置
-
自动复制四个静态资源文件夹、自动在 index.html 注入 CesiumJS 库文件的 script 标签以加载 CesiumJS
无论开发或生产环境,外部化了 CesiumJS,让 Vite 不再打包 cesium
依赖,大大减少打包时间、减少应用代码体积(从构建产物中剥离 cesium 库)
node_modules 下的 cesium
其实已经没有必要走 5.6 的静态文件复制了,而且注入 index.html
的主库文件需要修改。
bootcdn 上的 CesiumJS 为例,既然 Vite 内置了不同环境文件的解析的函数 loadEnv
(参考 Vite 官方文档 - 使用环境变量),我就分 development
和 production
简单讲一讲。
- 开发模式(
- 生产模式(
NODE_ENV = production
),使用 bootcdn 上的 CDN 链接
NODE_ENV = development
),使用 node_modules
下的 cesium
依赖,复制四个静态文件和库文件
vite.config.ts(注意,默认导出改成了函数):
import { defineConfig, type PluginOption, splitVendorChunkPlugin, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import { viteExternalsPlugin } from 'vite-plugin-externals'
import { insertHtml, h } from 'vite-plugin-insert-html'
import { viteStaticCopy } from 'vite-plugin-static-copy'
export default defineConfig((context => {
const mode = context.mode
const envDir = 'env' // 环境变量文件的文件夹,相对于项目的路径,也可以用 nodejs 函数拼接绝对路径
const isProd = mode === 'production'
const env = loadEnv(mode, envDir
const cesiumBaseUrl = env['VITE_CESIUM_BASE_URL']
// 默认 base 是 '/'
const base = '/'
const plugins: PluginOption[] = [
vue(,
splitVendorChunkPlugin(,
viteExternalsPlugin({
cesium: 'Cesium', // 外部化 cesium 依赖,之后全局访问形式是 window['Cesium']
},
insertHtml({
head: [
// 生产模式使用 CDN 或已部署的 CesiumJS 在线库链接,开发模式用拷贝的库文件,根据 VITE_CESIUM_BASE_URL 自动拼接
h('script', {
// 因为涉及前端路径访问,所以开发模式最好显式拼接 base 路径,适配不同 base 路径的情况
src: isProd ? `${cesiumBaseUrl}Cesium.js` : `${base}${cesiumBaseUrl}Cesium.js`
}
]
}
]
if (!isProd {
// 开发模式,复制 node_modules 下的 cesium 依赖
const cesiumLibraryRoot = 'node_modules/cesium/Build/CesiumUnminified/'
const cesiumLibraryCopyToRootPath = 'libs/cesium/' // 相对于打包后的路径
const cesiumStaticSourceCopyOptions = ['Assets', 'ThirdParty', 'Workers', 'Widgets'].map((dirName => {
return {
src: `${cesiumLibraryRoot}${dirName}/*`, // 注意后面的 * 字符,文件夹全量复制
dest: `${cesiumLibraryCopyToRootPath}${dirName}`
}
}
plugins.push(
viteStaticCopy({
targets: [
// 主库文件,开发时选用非压缩版的 IIFE 格式主库文件
{
src: `${cesiumLibraryRoot}Cesium.js`,
dest: cesiumLibraryCopyToRootPath
},
// 四大静态文件夹
...cesiumStaticSourceCopyOptions
]
},
}
return {
base,
envDir,
mode,
plugins,
}
}
为了 ts 能提示 import.meta.env.MODE
,需要在 src/vite-env.d.ts
中补充类型定义(参考 Vite 文档):
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_APP_TITLE: string
// 更多环境变量...
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
并且告诉 TypeScript 要用由 vite/client
提供的 import.meta
类型,在 tsconfig.node.json
的 compilerOptions
中添加:
{
"compilerOptions": {
"types": ["vite/client"]
}
}
如果是旧版本的 Vite 创建的模板,你可以添加在 tsconfig.json
对应的位置中。
5.9. 额外优化 - 使用 gzip 预先压缩打包产物
vite-plugin-compression 插件:
import compress from 'vite-plugin-compression'
// 使用见插件官方文档
在开发模式这玩意儿没起作用,就不细谈了。
5.8. 如何共享 CesiumJS 的 Viewer 对象
shallowRef 或 shallowReactive
来解决:
<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>
当然也可以用 Vue 的 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>
这个 CESIUM_VIEWER
是一个 Symbol
,来自 src/symbol/index.ts
:
export const CESIUM_VIEWER = Symbol('CESIUM_VIEWER'
如果业务界面组件与地图组件是兄弟组件或父子,那只能用三种方式传递 Viewer 对象:
- defineExpose
- 层层事件冒泡至父级组件,或者使用全局事件库(如 mitt)
- 使用全局状态 pinia 或 vuex
一定要避免响应式劫持。
6. 探究 CesiumJS 等库的前端组件封装
6.1. 以 CesiumJS 等库为主的看板式工程
这种工程有一个特点,就是地图场景会占满浏览器窗口的全部尺寸,并且不可在高度和宽度上出现滚动条。
Viewer,OpenLayers 是 Map
等。
子组件,在 Vue 中,就有 slot 的设计。
RouteView 也应作为 slot 编写在地图组件中) 。
provide/inject 实现地图核心对象的下发和注入。在 React 中使用 useContext
下发也是类似的。
6.2. 后台管理系统式工程
props 下传给地图,单一地显示上级操作接收来的数据。这种地图组件设计就比较简单,只需设计好 props
的数据结构,在组件挂载时创建核心对象并显示接收到的数据即可。
7. 示例工程下载
微云链接