Vue 3 + Vite 项目部署后报错排查实录
背景
最近将一个 Vue 3 + Vite + Element Plus + Leafer 的项目部署到生产环境后,遇到了一系列 ReferenceError: Cannot access 'X' before initialization 的错误。本文记录了完整的排查过程和解决方案。
错误现象
第一次报错
1 | Uncaught ReferenceError: Cannot access 'It' before initialization |
第二次报错(修复后)
1 | Uncaught ReferenceError: Cannot access 'It' before initialization |
第三次报错(继续修复后)
1 | Uncaught (in promise) ReferenceError: Cannot access 't' before initialization |
问题分析
这类错误通常表示在代码执行时访问了尚未初始化的变量,常见原因包括:
- 循环依赖 - 模块 A 依赖模块 B,模块 B 又依赖模块 A
- 代码分割导致的加载顺序问题 - 不同 chunk 之间的依赖关系不正确
- SSR/构建时访问浏览器 API - 在服务端渲染或构建时访问了
window等浏览器对象 - 异步组件和 Suspense 的组合问题 - 异步加载的组件在初始化时依赖未就绪
排查过程
第一步:检查 window 对象访问
首先怀疑是组件中直接访问了 window.innerWidth 来判断移动端,这在 SSR 或构建时会导致错误。
问题代码:
1 | // 多个组件中都存在 |
修复方案:
1 | const isMobile = ref(false); |
涉及组件:
Sidebar.vueIconToolbar.vueElement.vueFill.vue
第二步:检查 Element Plus 加载方式
发现 main.ts 中 Element Plus 是异步加载的:
1 | // 问题代码 |
这会导致组件在渲染时 Element Plus 可能还未加载完成。
修复方案:
1 | import ElementPlus from "element-plus"; |
第三步:移除 Suspense 组件
项目中大量使用了 Suspense 包裹异步组件,但 Suspense 与 Element Plus 的骨架屏组件 (el-skeleton) 组合使用时,在打包后可能出现初始化顺序问题。
移除的 Suspense:
App.vue中的LeaferCanvas和StylesDialogSidebar.vue中的el-segmented、IconToolbar和动态组件
同时将所有异步组件改为同步导入:
1 | // 之前 |
第四步:调整代码分割策略
Vite 配置中的 manualChunks 将代码分割成了多个 chunk,但分割方式导致了依赖问题:
原配置(有问题):
1 | manualChunks: (id) => { |
修复方案:
1 | manualChunks: (id) => { |
关键改动:
- 将
leafer-core和leafer-plugins合并为leafer - 移除了 Vue 组件的单独打包,让组件和它们的依赖打包在一起
总结
根本原因
- 代码分割不当 - 将相互依赖的模块分割到不同 chunk,导致加载顺序问题
- 异步加载时机 - Element Plus 异步加载导致组件初始化时依赖未就绪
- Suspense 副作用 - Suspense 与异步组件、Element Plus 骨架屏的组合在打包后出现问题
最佳实践
避免在计算属性中直接访问浏览器 API
1
2
3
4
5
6
7
8// ❌ 错误
const isMobile = computed(() => window.innerWidth <= 768);
// ✅ 正确
const isMobile = ref(false);
onMounted(() => {
isMobile.value = window.innerWidth <= 768;
});核心库同步加载
UI 框架等核心依赖应该在应用启动时就同步加载完成,避免运行时依赖缺失。谨慎使用代码分割
- 相互依赖的模块应该打包在一起
- 避免将组件和它们的 UI 框架依赖分割到不同 chunk
- 使用
manualChunks时要考虑模块间的依赖关系
Suspense 使用注意
- 生产环境中谨慎使用 Suspense 包裹异步组件
- 避免在 Suspense 中使用第三方 UI 组件作为 fallback
最终配置
vite.config.ts:
1 | export default defineConfig({ |
main.ts:
1 | import { createApp } from "vue"; |
结语
这类初始化错误在开发环境往往不会出现,因为开发时模块是按需加载的,而生产环境打包后的代码分割和加载顺序可能导致问题。遇到此类错误时,建议:
- 检查浏览器 API 的访问时机
- 检查代码分割配置
- 检查异步组件和依赖的加载顺序
- 使用 source map 定位压缩后的代码位置
希望本文对遇到类似问题的开发者有所帮助!