# 解决 Astro + Vite + Cesium 打包问题

6 min read
Table of Contents

问题描述

在使用 Astro + Vite + Cesium 构建博客项目时,发现了一个奇怪的问题:

  • 生产构建时:Cesium 没有正确打包进 dist 文件夹
  • 开发运行时:一切正常,没有任何问题

这导致生产环境可能无法正常运行,但开发环境却完全正常。

问题分析

1. vite-plugin-cesium 的构建行为

通过查看 vite-plugin-cesium 的源码,发现了问题的根源:

// vite-plugin-cesium 在构建时的行为
if (rebuildCesium) {
// 重新构建 Cesium,打包进 bundle
userConfig.build = {
assetsInlineLimit: 0,
chunkSizeWarningLimit: 5000,
rollupOptions: {
output: {
intro: `window.CESIUM_BASE_URL = ${JSON.stringify(CESIUM_BASE_URL)};`
}
}
}
} else {
// 默认行为:将 Cesium 设为外部依赖
userConfig.build = {
rollupOptions: {
external: ["cesium"],
plugins: [externalGlobals({ cesium: "Cesium" })]
}
}
}

问题所在

  • 默认情况下(rebuildCesium: false),Cesium 被配置为外部依赖
  • 这意味着 Cesium 不会被打包,而是期望从外部加载(如 CDN 或全局 script)
  • 但是 HTML 中没有引入 Cesium.js 的 script 标签
  • 静态资源(Assets、Workers、Widgets 等)也没有被复制到 dist 目录

2. Astro 构建流程的差异

Astro 的构建流程与纯 Vite 项目不同:

  • vite-plugin-cesiumtransformIndexHtml hook 可能没有被正确执行
  • closeBundle hook 中的资源复制逻辑可能没有运行

3. 开发模式为什么正常?

开发模式下正常是因为:

  • Vite 的 dev server 会从 node_modules 直接加载 Cesium
  • vite-plugin-cesiumconfigureServer hook 会设置中间件来服务 Cesium 资源
  • 所以开发时一切正常,但构建后缺少这些资源

解决方案

步骤一:创建自定义 Astro 集成

创建一个自定义的 Astro 集成来处理 Cesium 资源的复制和配置:

src/integrations/cesium.ts
import type { AstroIntegration } from 'astro'
import fs from 'fs/promises'
import path from 'path'
export function cesiumIntegration(): AstroIntegration {
return {
name: 'cesium-integration',
hooks: {
'astro:build:done': async ({ dir }) => {
const cesiumBuildPath = path.join(
process.cwd(),
'node_modules/cesium/Build/Cesium'
)
const cesiumDistPath = path.join(dir.pathname, 'cesium')
// 确保目标目录存在
await fs.mkdir(cesiumDistPath, { recursive: true })
// 复制 Cesium 的静态资源
const resources = ['Assets', 'ThirdParty', 'Workers', 'Widgets', 'Cesium.js']
const copyRecursive = async (src: string, dest: string) => {
const stat = await fs.stat(src)
if (stat.isDirectory()) {
await fs.mkdir(dest, { recursive: true })
const entries = await fs.readdir(src)
for (const entry of entries) {
await copyRecursive(path.join(src, entry), path.join(dest, entry))
}
} else {
await fs.copyFile(src, dest)
}
}
for (const resource of resources) {
const src = path.join(cesiumBuildPath, resource)
const dest = path.join(cesiumDistPath, resource)
try {
await fs.access(src)
await copyRecursive(src, dest)
console.log(`✓ Copied Cesium ${resource} to ${dest}`)
} catch (err) {
console.warn(
`⚠ Skipped Cesium ${resource}: ${err instanceof Error ? err.message : 'unknown error'}`
)
}
}
},
'astro:config:setup': ({ injectScript }) => {
// 注入 Cesium 的全局配置脚本
injectScript('head-inline', `
window.CESIUM_BASE_URL = '/cesium/';
`)
},
},
}
}

步骤二:创建 CesiumLoader 组件

创建一个组件来动态加载 Cesium.js:

src/components/CesiumLoader.astro
---
// CesiumLoader 组件:在需要 Cesium 的页面加载 Cesium.js
// 只在客户端加载,避免 SSR 问题
---
<script>
// 检查是否已经加载了 Cesium
if (typeof window !== 'undefined' && !window.Cesium) {
// 动态加载 Cesium.js
const script = document.createElement('script')
script.src = '/cesium/Cesium.js'
script.async = true
script.onload = () => {
console.log('Cesium loaded successfully')
}
script.onerror = () => {
console.error('Failed to load Cesium.js')
}
document.head.appendChild(script)
}
</script>

步骤三:更新配置文件

astro.config.mjs 中添加集成:

import { cesiumIntegration } from './src/integrations/cesium'
export default defineConfig({
// ... 其他配置
integrations: [
// ... 其他集成
cesiumIntegration(),
],
vite: {
plugins: [tailwindcss(), cesium()],
// ... 其他配置
},
})

在需要 Cesium 的页面中使用:

---
title: 'init cesium with react'
---
import CesiumLoader from '~/components/CesiumLoader.astro'
import InitMapDemo from '~/components/mdx/InitMapDemo'
<CesiumLoader />
<InitMapDemo client:load id={'fs'}/>

开发模式问题修复

在解决打包问题后,还遇到了开发模式下的问题:

问题

  • Vite 依赖优化缓存过期(504 Outdated Optimize Dep)
  • React 运行在生产模式但死代码消除未应用

解决方案

  1. 更新 optimizeDeps 配置
vite: {
optimizeDeps: {
include: ['cesium', 'clsx', 'react-syntax-highlighter'],
exclude: []
},
}
  1. 添加清理缓存的脚本
{
"scripts": {
"dev:clean": "rm -rf node_modules/.vite && astro dev --host"
}
}
  1. 使用清理后的开发命令
Terminal window
npm run dev:clean

最终效果

  • Cesium 资源正确复制到 dist/cesium/
  • HTML 中正确注入 CesiumLoader 的 script 标签
  • CESIUM_BASE_URL 正确设置为 /cesium/
  • 开发模式依赖优化正常工作
  • 生产构建和开发运行都正常

总结

这个问题的核心在于:

  1. 插件适配问题vite-plugin-cesium 主要针对纯 Vite 项目设计,在 Astro 中需要额外处理
  2. 构建流程差异:Astro 的构建流程与 Vite 不同,某些 hook 可能不会按预期执行
  3. 开发/生产差异:开发模式和生产模式的资源加载方式不同

通过创建自定义集成和组件,我们成功解决了这些问题,确保了 Cesium 在 Astro 项目中能够正常工作。

My avatar

Thanks for reading my blog post! Feel free to check out my other posts or contact me via the social links in the footer.


More Posts

Comments