1. 架构概述

1.1 设计原则
- 组件化:UI拆分为可复用的独立组件,遵循单一职责原则
- 模块化:功能按职责分离(Services、Stores、Composables),每个模块独立可维护
- 状态管理集中化:使用Pinia统一管理应用状态,确保数据流清晰
- 路由懒加载:按需加载页面,优化首屏性能和用户体验
- 类型安全:全面使用TypeScript,编译时类型检查,减少运行时错误
- 国际化支持:基于vue-i18n实现多语言切换,支持中英文
1.2 技术栈
- 框架: Vue 3.5+ (Composition API)
- 构建工具: Vite 8.0+ (快速开发服务器和优化构建)
- 状态管理: Pinia 2.3+ (轻量级类型安全状态管理)
- 路由: Vue Router 4.5+ (SPA路由管理)
- UI组件库: Element Plus 2.9+ (企业级UI组件)
- 地图引擎: MapLibre GL 5.5+ (开源矢量地图渲染)
- HTTP客户端: Axios 1.9+ (请求拦截和统一错误处理)
- 国际化: Vue I18n 9.14+ (多语言支持)
- CSS预处理器: Sass 1.87+ (样式变量和混入)
- 图标: Element Plus Icons + 自定义SVG图标系统
1.3 架构分层
┌─────────────────────────────────────────┐
│ Presentation Layer │
│ (Views & Components - UI展示) │
├─────────────────────────────────────────┤
│ Business Logic Layer │
│ (Composables - 业务逻辑) │
├─────────────────────────────────────────┤
│ State Management Layer │
│ (Pinia Stores - 状态管理) │
├─────────────────────────────────────────┤
│ Service Layer │
│ (API Services - 数据访问) │
├─────────────────────────────────────────┤
│ Infrastructure Layer │
│ (Router, HTTP Client, i18n, Utils) │
└─────────────────────────────────────────┘
数据流向:
用户交互 → Component → Composable → Store → Service → API
↓
Response
↓
Store更新 → Composable响应 → Component重新渲染
2. 完整目录结构
web/
├── src/
│ ├── main.ts # 应用入口(初始化Pinia、Router、i18n、Element Plus)
│ ├── App.vue # 根组件(路由视图容器)
│ ├── style.css # 全局样式
│ │
│ ├── router/ # 路由配置
│ │ ├── index.ts # 路由实例创建和守卫
│ │ ├── routes.ts # 路由定义(懒加载)
│ │ └── guards.ts # 路由守卫(可选)
│ │
│ ├── stores/ # Pinia状态管理
│ │ ├── index.ts # Store导出
│ │ ├── app.store.ts # 应用全局状态(主题、语言、侧边栏)
│ │ ├── geojson.store.ts # GeoJSON文件状态(列表、上传状态)
│ │ ├── layer.store.ts # 图层状态(列表、选中、过滤)
│ │ ├── style.store.ts # 样式状态(当前编辑、模板)
│ │ └── icon.store.ts # 图标状态(列表、上传、选择)
│ │
│ ├── services/ # API服务层
│ │ ├── index.ts # 服务导出
│ │ ├── http.service.ts # HTTP客户端封装(Axios + 拦截器)
│ │ ├── geojson.service.ts # GeoJSON文件API
│ │ ├── layer.service.ts # 图层管理API
│ │ ├── style.service.ts # 样式配置API
│ │ ├── tile.service.ts # MVT瓦片URL生成
│ │ └── icon.service.ts # 图标管理API
│ │
│ ├── views/ # 页面视图(路由级别组件)
│ │ ├── index.ts # 视图导出
│ │ ├── LayerMapView.vue # 图层管理与地图预览(首页)
│ │ ├── DataUploadView.vue # 数据上传页面
│ │ ├── StyleEditorView.vue # 样式编辑页面
│ │ ├── IconManagerView.vue # 图标管理页面
│ │ └── WorkspaceSettingsView.vue # 工作空间设置页面
│ │
│ ├── components/ # 公共组件
│ │ ├── layout/ # 布局组件
│ │ │ ├── AppHeader.vue # 应用头部导航
│ │ │ ├── AppSidebar.vue # 侧边栏菜单
│ │ │ ├── AppFooter.vue # 应用底部信息
│ │ │ ├── MainLayout.vue # 主布局容器
│ │ │ └── index.ts # 组件导出
│ │ │
│ │ ├── common/ # 通用组件
│ │ │ ├── LoadingSpinner.vue # 加载动画
│ │ │ ├── EmptyState.vue # 空状态展示
│ │ │ ├── ErrorBoundary.vue # 错误边界
│ │ │ ├── ConfirmDialog.vue # 确认对话框
│ │ │ ├── FileUploader.vue # 文件上传组件
│ │ │ ├── SearchBox.vue # 搜索框组件
│ │ │ ├── Pagination.vue # 分页组件
│ │ │ └── index.ts # 组件导出
│ │ │
│ │ ├── layer/ # 图层相关组件
│ │ │ ├── LayerList.vue # 图层列表容器
│ │ │ ├── LayerCard.vue # 图层卡片展示
│ │ │ ├── LayerSearch.vue # 图层搜索框
│ │ │ ├── CreateLayerDialog.vue # 创建图层对话框
│ │ │ └── index.ts # 组件导出
│ │ │
│ │ ├── map/ # 地图相关组件
│ │ │ ├── MapContainer.vue # MapLibre地图容器
│ │ │ ├── MapControls.vue # 地图控制按钮(缩放、复位)
│ │ │ ├── LayerVisibilityControl.vue # 图层显隐控制面板
│ │ │ ├── FeaturePopup.vue # 要素属性弹窗
│ │ │ ├── MapLegend.vue # 地图图例
│ │ │ ├── ScaleBar.vue # 比例尺
│ │ │ └── index.ts # 组件导出
│ │ │
│ │ ├── style/ # 样式相关组件
│ │ │ ├── StyleEditor.vue # 样式编辑器主组件
│ │ │ ├── PointStyleConfig.vue # 点样式配置
│ │ │ ├── LineStyleConfig.vue # 线样式配置
│ │ │ ├── PolygonStyleConfig.vue # 面样式配置
│ │ │ ├── StylePreview.vue # 样式实时预览
│ │ │ ├── ColorPicker.vue # 颜色选择器
│ │ │ ├── IconSelector.vue # 图标选择器
│ │ │ └── index.ts # 组件导出
│ │ │
│ │ ├── icon/ # 图标相关组件
│ │ │ ├── IconGrid.vue # 图标网格展示
│ │ │ ├── IconCard.vue # 图标卡片
│ │ │ ├── IconUploadDialog.vue # 图标上传对话框
│ │ │ ├── IconPreview.vue # 图标预览
│ │ │ └── index.ts # 组件导出
│ │ │
│ │ └── geojson/ # GeoJSON相关组件
│ │ ├── GeoJSONFileList.vue # GeoJSON文件列表
│ │ └── index.ts # 组件导出
│ │
│ ├── composables/ # 组合式函数(业务逻辑封装)
│ │ ├── index.ts # Composables导出
│ │ ├── useLayers.ts # 图层业务逻辑(CRUD、搜索)
│ │ ├── useMap.ts # 地图核心逻辑(初始化、图层管理)
│ │ ├── useMapControls.ts # 地图控制逻辑(缩放、平移)
│ │ ├── useFileUpload.ts # 文件上传逻辑(验证、进度)
│ │ ├── useStyleEditor.ts # 样式编辑逻辑(配置、预览)
│ │ ├── useDebounce.ts # 防抖工具函数
│ │ ├── useLocalStorage.ts # 本地存储工具
│ │ └── useTranslation.ts # 国际化翻译工具
│ │
│ ├── types/ # TypeScript类型定义
│ │ ├── index.ts # 类型导出
│ │ ├── api.types.ts # API响应/请求类型
│ │ ├── layer.types.ts # 图层类型(Layer、LayerType等)
│ │ ├── geojson.types.ts # GeoJSON类型(Feature、Geometry等)
│ │ ├── style.types.ts # 样式类型(StyleConfig、PointStyle等)
│ │ ├── icon.types.ts # 图标类型(Icon、IconMetadata)
│ │ ├── map.types.ts # 地图类型(MapConfig、ViewState)
│ │ ├── workspace.types.ts # 工作空间类型
│ │ ├── common.types.ts # 通用类型(UUID、DateTime等)
│ │ └── index.ts # 类型导出
│ │
│ ├── utils/ # 工具函数
│ │ ├── index.ts # 工具函数导出
│ │ ├── logger.ts # 日志工具(分级日志)
│ │ ├── format.ts # 格式化工具(文件大小、日期)
│ │ ├── validator.ts # 验证工具(GeoJSON、文件类型)
│ │ ├── geojson.ts # GeoJSON处理工具
│ │ ├── style.ts # 样式处理工具
│ │ ├── file.ts # 文件处理工具
│ │ ├── constants.ts # 常量定义
│ │ └── helpers.ts # 辅助函数
│ │
│ ├── i18n/ # 国际化配置
│ │ ├── index.ts # i18n实例创建
│ │ ├── locales/ # 语言包
│ │ │ ├── zh-CN.ts # 中文语言包
│ │ │ └── en-US.ts # 英文语言包
│ │ └── messages.ts # 消息键值定义
│ │
│ └── assets/ # 静态资源
│ ├── styles/ # 样式文件
│ │ ├── main.scss # 主样式(导入所有样式)
│ │ ├── variables.scss # SCSS变量(颜色、间距、字体)
│ │ ├── mixins.scss # SCSS混入(Flex、响应式)
│ │ └── reset.scss # 样式重置
│ │
│ ├── images/ # 图片资源
│ │ └── logo.png # Logo图片
│ │
│ └── icons/ # SVG图标
│ ├── upload.svg
│ ├── delete.svg
│ ├── edit.svg
│ └── preview.svg
│
├── public/ # 公共资源(不经过Vite构建)
│ ├── favicon.ico # 网站图标
│ ├── favicon.svg # SVG图标
│ └── icons.svg # 图标精灵
│
├── .env # 环境变量(通用)
├── .env.development # 开发环境变量
├── .env.production # 生产环境变量
├── package.json # 依赖配置
├── tsconfig.json # TypeScript配置
├── tsconfig.app.json # 应用TS配置
├── tsconfig.node.json # Node TS配置
├── vite.config.ts # Vite配置(代理、代码分割)
├── index.html # HTML模板
└── README.md # Web端说明
3. 核心模块说明
3.1 应用入口
src/main.ts
职责:
- 创建Vue应用实例
- 注册全局插件(Pinia、Vue Router、i18n、Element Plus)
- 注册Element Plus图标组件
- 配置全局错误处理器
- 导入全局样式
- 挂载应用到DOM
关键代码:
typescript
const app = createApp(App);
app.use(createPinia());
app.use(router);
app.use(i18n);
app.use(ElementPlus);
// 注册所有Element Plus图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component);
}
app.config.errorHandler = (err, instance, info) => {
// 全局错误处理
};
app.mount('#app');
src/App.vue
职责:
- 根组件
- 路由视图容器 (
<router-view>) - 提供全局布局结构
3.2 路由配置
src/router/index.ts
职责:
- 创建路由实例(createRouter)
- 配置路由历史模式(createWebHistory)
- 注册全局路由守卫(可选)
- 导出路由实例
src/router/routes.ts
职责:
- 定义所有路由规则
- 配置路由元信息(title、icon、keepAlive)
- 路由懒加载配置(
() => import('@/views/...'))
路由列表:
| 路径 | 组件 | 说明 |
|---|---|---|
/ |
LayerMapView | 图层管理与地图预览(首页) |
/upload |
DataUploadView | 数据上传 |
/style/:layerId |
StyleEditorView | 样式编辑 |
/icons |
IconManagerView | 图标管理 |
/settings |
WorkspaceSettingsView | 工作空间设置 |
3.3 状态管理 (Pinia Stores)
src/stores/app.store.ts
管理内容:
- 应用主题(light/dark)
- 语言设置(zh-CN/en-US)
- 侧边栏展开/折叠状态
- 全局加载状态
src/stores/geojson.store.ts
管理内容:
- GeoJSON文件列表
- 文件上传状态(uploading、progress)
- 文件删除操作
- 文件解析结果
src/stores/layer.store.ts
管理内容:
- 图层列表(支持分页、搜索过滤)
- 当前选中图层
- 图层创建/编辑/删除状态
- 图层启用/停用状态
- 图层加载状态
关键特性:
- 支持图层搜索过滤
- 自动同步后端状态
- 提供丰富的actions(fetchLayers、createLayer、updateLayer等)
src/stores/style.store.ts
管理内容:
- 当前编辑的样式配置
- 样式模板列表
- 样式预览状态
- 样式保存/重置操作
src/stores/icon.store.ts
管理内容:
- 图标列表(网格展示)
- 图标上传状态
- 图标选择状态
- 图标删除操作
3.4 API服务层
src/services/http.service.ts
职责:
- Axios实例配置(baseURL、timeout)
- 请求拦截器(添加Token、Request ID)
- 响应拦截器(统一错误处理、日志记录)
- HTTP错误分类处理(4xx、5xx、网络错误)
关键特性:
- 自动添加Request ID用于追踪
- 统一错误消息提示(ElMessage)
- 支持请求/响应日志(开发环境)
- 错误重试机制(可选)
src/services/geojson.service.ts
提供方法:
uploadFile(file: File)- 上传GeoJSON文件deleteFile(id: string)- 删除GeoJSON文件listFiles()- 获取文件列表getFileById(id: string)- 获取单个文件信息
src/services/layer.service.ts
提供方法:
createLayer(data: CreateLayerDTO)- 创建图层updateLayer(id: string, data: UpdateLayerDTO)- 更新图层deleteLayer(id: string)- 删除图层listLayers(params?: ListParams)- 获取图层列表(支持分页、搜索)getLayerById(id: string)- 获取单个图层toggleLayerStatus(id: string)- 切换图层状态
src/services/style.service.ts
提供方法:
saveStyle(layerId: string, styleConfig: StyleConfig)- 保存样式getStyle(layerId: string)- 获取图层样式getDefaultStyle(geometryType: string)- 获取默认样式updateStyle(layerId: string, styleConfig: StyleConfig)- 更新样式
特点:
- 样式配置深度合并
- 支持样式模板应用
- 自动验证样式配置合法性
src/services/tile.service.ts
提供方法:
getTileUrl(layerId: string, z: number, x: number, y: number)- 生成MVT瓦片URLgetSourceConfig(layerId: string)- 获取MapLibre数据源配置
src/services/icon.service.ts
提供方法:
uploadIcon(file: File)- 上传图标(SVG/PNG)deleteIcon(id: string)- 删除图标listIcons()- 获取图标列表getIconById(id: string)- 获取单个图标
3.5 页面视图
src/views/LayerMapView.vue
功能:
- 左侧:图层管理面板
- 右侧:地图预览
- 集成LayerList和MapContainer组件
src/views/DataUploadView.vue
功能:
- 文件拖拽上传
- 上传进度显示
- 上传结果反馈
src/views/StyleEditorView.vue
功能:
- 图层选择
- 样式可视化配置
- 实时预览
- 保存/取消操作
src/views/IconManagerView.vue
功能:
- 图标网格展示
- 图标上传
- 图标删除
- 图标预览
src/views/WorkspaceSettingsView.vue
功能:
- 工作空间路径显示
- 工作空间路径修改
- 磁盘使用情况统计
3.6 公共组件
布局组件 (components/layout/)
- AppHeader.vue: 应用顶部导航栏
- AppSidebar.vue: 左侧导航菜单
- AppFooter.vue: 应用底部信息
- MainLayout.vue: 主布局容器,包含header、sidebar、footer
通用组件 (components/common/)
- LoadingSpinner.vue: 全局加载动画
- EmptyState.vue: 空数据状态展示
- ErrorBoundary.vue: 错误捕获和展示
- ConfirmDialog.vue: 二次确认对话框
- FileUploader.vue: 通用文件上传组件
图层组件 (components/layer/)
- LayerList.vue: 图层列表容器
- LayerCard.vue: 图层卡片展示
- LayerItem.vue: 图层列表单项
- LayerStatusSwitch.vue: 启用/停用开关
- LayerSearchBox.vue: 搜索过滤框
- CreateLayerDialog.vue: 创建图层对话框
地图组件 (components/map/)
- MapContainer.vue: MapLibre GL地图容器
- MapControls.vue: 地图缩放、平移控制按钮
- LayerVisibilityControl.vue: 图层显隐控制面板
- FeaturePopup.vue: 点击要素显示属性
- MapLegend.vue: 地图图例说明
样式组件 (components/style/)
- StyleEditor.vue: 样式编辑器主组件
- PointStyleConfig.vue: 点样式配置面板
- LineStyleConfig.vue: 线样式配置面板
- PolygonStyleConfig.vue: 面样式配置面板
- StylePreview.vue: 样式实时预览
- ColorPicker.vue: 颜色选择器
- IconSelector.vue: 图标选择器
- StyleTemplateList.vue: 预设样式模板列表
图标组件 (components/icon/)
- IconGrid.vue: 图标网格布局
- IconCard.vue: 单个图标卡片
- IconUploadDialog.vue: 图标上传对话框
- IconPreview.vue: 图标放大预览
上传组件 (components/upload/)
- UploadZone.vue: 拖拽上传区域
- UploadProgress.vue: 上传进度条
- UploadResult.vue: 上传成功/失败结果
3.7 组合式函数 (Composables)
架构定位:Composables是业务逻辑层,封装可复用的状态和逻辑,连接Components和Stores/Services。
src/composables/useLayers.ts
提供功能:
- 图层列表获取和刷新
- 图层创建/更新/删除操作封装
- 图层状态切换(active/inactive)
- 图层搜索过滤逻辑
- 选中图层管理
使用示例:
typescript
const { layers, loading, fetchLayers, createLayer, deleteLayer } = useLayers();
src/composables/useMap.ts
提供功能:
- MapLibre GL地图实例初始化
- 地图事件监听(click、move、zoom)
- MVT图层添加到地图
- 图层显隐控制
- 要素点击查询和Popup显示
- 地图视图控制(flyTo、fitBounds)
- 图例数据生成
关键特性:
- 单例模式管理地图实例
- 自动处理图层样式更新
- 支持动态添加/移除图层
- 性能优化(防抖、节流)
src/composables/useMapControls.ts
提供功能:
- 地图缩放控制(zoomIn、zoomOut)
- 地图复位(resetView)
- 全屏切换
- 地图截图(可选)
src/composables/useFileUpload.ts
提供功能:
- 文件选择和验证(类型、大小)
- 文件上传进度跟踪
- 上传结果处理(成功/失败)
- 拖拽上传支持
- 多文件上传队列管理
src/composables/useStyleEditor.ts
提供功能:
- 样式配置状态管理
- 样式实时预览(同步到地图)
- 样式保存/重置/取消
- 样式模板应用
- 颜色/图标选择器集成
src/composables/useDebounce.ts
提供功能:
- 防抖函数封装
- 用于搜索输入优化(减少API请求)
- 窗口resize事件优化
使用示例:
typescript
const debouncedSearch = useDebounce(searchQuery, 300);
src/composables/useLocalStorage.ts
提供功能:
- 本地存储读写封装
- 持久化用户偏好(主题、语言、侧边栏状态)
- 自动JSON序列化/反序列化
- 默认值支持
src/composables/useTranslation.ts
提供功能:
- vue-i18n翻译函数封装
- 简化t()调用
- 支持动态语言切换
3.8 类型定义
src/types/api.types.ts
定义内容:
- ApiResponse
- ApiError
- PaginationParams
src/types/layer.types.ts
定义内容:
- Layer
- LayerType ('single' | 'group')
- LayerStatus ('active' | 'inactive')
- CreateLayerDTO
- UpdateLayerDTO
src/types/geojson.types.ts
定义内容:
- GeoJSONFile
- GeometryType
- FeatureCollection
src/types/style.types.ts
定义内容:
- StyleConfig
- PointStyle
- LineStyle
- PolygonStyle
- StyleTemplate
src/types/icon.types.ts
定义内容:
- Icon
- IconMetadata
src/types/common.types.ts
定义内容:
- DateTime
- UUID
- Status
3.9 工具函数
src/utils/format.ts
提供函数:
formatFileSize(bytes)- 格式化文件大小formatDateTime(date)- 格式化日期时间truncateText(text, length)- 文本截断
src/utils/validator.ts
提供函数:
validateGeoJSON(file)- 验证GeoJSON文件validateIcon(file)- 验证图标文件isValidURL(url)- URL验证
src/utils/geojson.ts
提供函数:
parseGeoJSON(content)- 解析GeoJSONgetGeometryType(geojson)- 获取几何类型countFeatures(geojson)- 统计要素数量
src/utils/style.ts
提供函数:
mergeStyles(base, override)- 合并样式getDefaultStyle(geometryType)- 获取默认样式validateStyleConfig(style)- 验证样式配置
src/utils/file.ts
提供函数:
readFileAsText(file)- 读取文件为文本readFileAsArrayBuffer(file)- 读取文件为ArrayBuffergetFileNameWithoutExtension(filename)- 获取无扩展名文件名
src/utils/constants.ts
定义常量:
- MAX_FILE_SIZE
- ALLOWED_FILE_TYPES
- DEFAULT_MAP_CONFIG
- API_BASE_URL
3.10 样式文件
src/assets/styles/main.scss
内容:
- 全局样式
- 导入variables、mixins、reset
src/assets/styles/variables.scss
内容:
- 颜色变量
- 字体变量
- 间距变量
- 断点变量
src/assets/styles/mixins.scss
内容:
- Flex布局混入
- 文本省略混入
- 响应式混入
src/assets/styles/reset.scss
内容:
- CSS Reset
- Element Plus样式覆盖
4. 模块依赖关系
4.1 依赖层次
main.ts
├── router/ (路由配置)
├── stores/ (状态管理)
├── i18n/ (国际化)
└── App.vue (根组件)
└── views/ (页面视图)
├── components/ (UI组件)
├── composables/ (业务逻辑)
├── services/ (API服务)
├── stores/ (状态访问)
└── utils/ (工具函数)
4.2 依赖方向规则
- Views → Components、Composables、Services、Stores
- Components → Composables、Utils、Types(不直接访问Services/Stores)
- Composables → Services、Stores、Utils(封装业务逻辑)
- Services → Types、Utils、HTTP Client(纯数据访问)
- Stores → Services、Types(状态管理和持久化)
- Utils → 无依赖(纯函数)
原则:
- 单向依赖,禁止循环依赖
- 高层模块依赖低层模块
- 通过Composables解耦Components和Services/Stores
5. 路由设计
5.1 路由列表
| 路径 | 组件 | 说明 |
|---|---|---|
/ |
LayerMapView | 图层管理与地图预览(首页) |
/upload |
DataUploadView | 数据上传 |
/style/:layerId |
StyleEditorView | 样式编辑 |
/icons |
IconManagerView | 图标管理 |
/settings |
WorkspaceSettingsView | 工作空间设置 |
5.2 路由元信息
typescript
{
title: '页面标题',
icon: '图标名称',
requiresAuth: false, // 未来扩展
keepAlive: true // 是否缓存
}
6. 状态管理设计
6.1 Store组织结构
app.store.ts # 全局应用状态
├── theme
├── language
└── sidebarCollapsed
geojson.store.ts # GeoJSON文件状态
├── fileList
├── uploading
└── uploadProgress
layer.store.ts # 图层状态
├── layerList
├── selectedLayer
├── loading
└── filters
style.store.ts # 样式状态
├── currentStyle
├── styleTemplates
└── previewEnabled
icon.store.ts # 图标状态
├── iconList
├── selectedIcon
└── uploading
6.2 Store交互流程
用户操作 → Component → Composable → Service → API
↓
Store (更新状态)
↓
Component (响应式更新)
7. 组件通信策略
7.1 Props Down, Events Up
- 父子组件:props传递数据,emit触发事件
7.2 Provide / Inject
- 跨层级组件:地图实例、全局配置
7.3 Pinia Store
- 全局状态:图层列表、用户偏好
7.4 Event Bus (mitt)
- 非相关组件:地图事件通知
8. 性能优化策略
8.1 路由懒加载
实现方式:
typescript
const LayerMapView = () => import('@/views/LayerMapView.vue')
效果:首屏只加载必要代码,减少初始包体积
8.2 代码分割 (Vite Rollup)
配置 (vite.config.ts):
typescript
manualChunks(id) {
if (id.includes('node_modules')) {
if (id.includes('vue') || id.includes('vue-router') || id.includes('pinia')) {
return 'vendor'; // Vue核心库
}
if (id.includes('element-plus')) {
return 'element'; // UI组件库
}
if (id.includes('maplibre-gl')) {
return 'maplibre'; // 地图引擎
}
}
}
效果:第三方库单独打包,利用浏览器缓存
8.3 组件懒加载
实现方式:
typescript
const MapContainer = defineAsyncComponent(() =>
import('@/components/map/MapContainer.vue')
)
适用场景:大型组件(如地图容器)、非首屏组件
8.4 防抖和节流
应用场景:
- 搜索输入:300ms防抖,减少API请求
- 窗口resize:200ms防抖,避免频繁重绘
- 地图移动事件:节流处理,控制Popup更新频率
实现:
typescript
const debouncedSearch = useDebounce(searchQuery, 300);
8.5 图片优化
- SVG图标使用精灵图 (
icons.svg) - 图标懒加载(Intersection Observer)
- Element Plus图标按需引入
8.6 MapLibre性能优化
- MVT瓦片按需加载(视口内瓦片)
- 图层显隐控制(隐藏图层不渲染)
- 要素查询优化(限制返回数量)
- 地图事件防抖(move、zoom事件)
8.7 状态管理优化
- Pinia持久化(localStorage)减少重复请求
- 图层列表分页加载(避免一次性加载大量数据)
- 计算属性缓存(computed)避免重复计算
9. 开发规范
9.1 命名规范
- 文件命名 :
- 组件:PascalCase (
LayerList.vue) - 工具/服务:camelCase (
http.service.ts) - Stores:camelCase +
.store.ts(layer.store.ts) - Composables:camelCase +
use前缀 (useLayers.ts)
- 组件:PascalCase (
- 变量命名 :camelCase (
layerList、isLoading) - 常量命名 :UPPER_SNAKE_CASE (
MAX_FILE_SIZE、API_BASE_URL) - 类型命名 :PascalCase (
Layer、CreateLayerDTO)
9.2 组件设计原则
- 单一职责:每个组件只负责一个功能
- 可复用性:通用逻辑提取到Composables
- 可测试性:业务逻辑与UI分离
- Props向下,Events向上:父子组件通信标准模式
- 组件大小:单个组件不超过300行(超过则拆分)
9.3 Composition API使用规范
- 优先使用
<script setup>语法 - 逻辑按功能分组(非选项API的data/methods分组)
- Composables返回值使用解构赋值
- 响应式数据明确标注(ref/reactive)
示例:
typescript
<script setup lang="ts">
import { ref, computed } from 'vue';
import { useLayers } from '@/composables';
const { layers, loading, fetchLayers } = useLayers();
const searchQuery = ref('');
const filteredLayers = computed(() => {
return layers.value.filter(l => l.name.includes(searchQuery.value));
});
</script>
9.4 样式规范
- 使用SCSS预处理器
- BEM命名规范(
.layer-list__item--active) - 避免全局样式污染(使用scoped或CSS Modules)
- 使用CSS变量管理主题色
- 响应式设计(移动优先)
9.5 TypeScript规范
- 严格模式(strict: true)
- 禁止使用
any类型(使用unknown或具体类型) - 接口命名以
I开头或使用Type别名 - DTO类型用于API请求/响应
- 泛型提高代码复用性
9.6 代码组织
- 每个函数不超过50行(超过则拆分)
- 每个文件不超过500行(超过则拆分模块)
- 导入顺序:Vue → 第三方库 → 内部模块 → 样式
- 使用绝对路径导入(
@/components/...)
10. 测试策略
10.1 单元测试
- 工具函数测试
- Composables测试
- Store测试
10.2 组件测试
- 组件渲染测试
- 用户交互测试
10.3 E2E测试
- 关键业务流程测试
- 使用Cypress或Playwright
11. 国际化支持
11.1 语言包结构
locales/
├── zh-CN.ts
└── en-US.ts
11.2 使用方式
typescript
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
t('layer.create') // "创建图层"
12. 环境配置
12.1 Vite配置 (vite.config.ts)
关键配置项:
typescript
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');
return {
base: env.VITE_BASE_URL || '/', // 应用基础路径
resolve: {
alias: {
'@': path.resolve(__dirname, './src'), // 路径别名
},
},
server: {
port: 5173,
host: '0.0.0.0',
proxy: {
'/api': {
target: 'http://localhost:3000', // 后端代理
changeOrigin: true,
},
},
},
build: {
outDir: 'dist',
sourcemap: false, // 生产环境不生成source map
chunkSizeWarningLimit: 1000,
rollupOptions: {
output: {
manualChunks(id) {
// 代码分割配置
},
},
},
},
};
});
12.2 环境变量
.env.development (开发环境)
env
VITE_BASE_URL=/geoai-geojson-mvt
VITE_API_BASE_URL=http://localhost:3000
VITE_APP_TITLE=GeoAI MVT Manager (Dev)
.env.production (生产环境)
env
VITE_BASE_URL=/geoai-geojson-mvt
VITE_API_BASE_URL=/api # 使用相对路径,由Express提供服务
VITE_APP_TITLE=GeoAI MVT Manager
注意:
- 只有
VITE_前缀的变量会暴露给客户端 - 生产环境API使用相对路径,由后端Express提供代理
- 开发环境通过Vite Proxy代理到后端服务器
13. 构建与部署
13.1 开发环境
bash
cd web
npm install # 安装依赖
npm run dev # 启动开发服务器
# 访问 http://localhost:5173/geoai-geojson-mvt
特性:
- 热模块替换 (HMR) - 代码修改即时生效
- TypeScript类型检查
- ESLint实时检查(可选)
- API请求代理到后端 (localhost:3000)
13.2 生产构建
bash
npm run build # 构建生产版本
# 输出到 dist/ 目录
构建产物:
dist/
├── index.html # 入口HTML
├── favicon.ico # 网站图标
├── icons.svg # 图标精灵
└── assets/
├── css/
│ └── index.[hash].css # 样式文件(带hash)
├── js/
│ ├── index.[hash].js # 应用代码
│ ├── vendor.[hash].js # Vue核心库
│ ├── element.[hash].js # Element Plus
│ └── maplibre.[hash].js # MapLibre GL
└── images/ # 图片资源
优化:
- 代码压缩和混淆
- CSS压缩和自动前缀
- 静态资源带内容hash(长期缓存)
- Tree Shaking移除未使用代码
- Source map不生成(减小体积)
13.3 部署方式
方式1:独立部署(开发/测试)
bash
npm run preview # 本地预览生产构建
# 访问 http://localhost:4173
方式2:集成到后端(生产)
- 构建产物复制到
server/client/目录 - Express提供静态文件服务
- 打包时由
build/package.js自动处理
部署流程:
bash
# 1. 构建前端
cd web && npm run build
# 2. 复制到后端
cp -r dist/* ../server/client/
# 3. 后端打包
cd ../build && npm run package
13.4 Nginx配置(可选)
nginx
server {
listen 80;
server_name your-domain.com;
root /var/www/geoai-mvt-web;
index index.html;
location / {
try_files $uri $uri/ /index.html; # SPA路由支持
}
location /api {
proxy_pass http://localhost:3000; # 后端代理
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
14. 总结
本前端架构设计遵循以下核心原则:
14.1 架构优势
- 组件化: UI拆分为细粒度可复用组件,遵循单一职责原则
- 模块化: 功能按职责清晰分离(Services、Stores、Composables、Components)
- 类型安全: 全面TypeScript覆盖,编译时类型检查,减少运行时错误
- 状态管理: Pinia集中管理全局状态,数据流清晰可追踪
- 性能优化: 路由懒加载、代码分割、防抖节流、MapLibre瓦片按需加载
- 可维护性: 清晰的目录结构、命名规范、Composition API逻辑复用
- 国际化: vue-i18n支持多语言切换,易于扩展
- 开发体验: Vite HMR快速热更新、TypeScript智能提示、Element Plus组件库
14.2 技术亮点
- Composition API: 逻辑复用和组织的最佳实践
- Composables模式: 封装业务逻辑,连接UI和数据层
- Vite构建: 极速开发服务器和优化生产构建
- 代码分割: 第三方库单独打包(vendor、element、maplibre)
- MapLibre集成: 专业GIS地图渲染,MVT瓦片高效加载
- 响应式设计: SCSS变量和混入,支持主题切换
14.3 关键决策
为什么选择Pinia而非Vuex?
- 更轻量和现代化
- 更好的TypeScript支持
- 更简洁的API(无需mutations)
- Vue 3官方推荐
为什么使用Composables?
- 替代Mixins,避免命名冲突
- 更好的类型推断
- 逻辑复用更灵活
- 符合Composition API理念
为什么选择MapLibre GL?
- 开源免费(Mapbox GL JS的fork)
- 完整的MVT支持
- 活跃的社区和维护
- 与Mapbox API兼容
14.4 可扩展性
当前架构为以下扩展预留空间:
- ✅ 更多地图图层类型(热力图、聚类图等)
- ✅ 高级样式编辑器(表达式、数据驱动样式)
- ✅ 用户认证和权限管理
- ✅ 协作编辑功能
- ✅ 离线地图支持(Service Worker)
- ✅ 移动端适配(响应式布局)
- ✅ 插件系统(自定义工具栏、面板)
14.5 性能指标
构建产物大小(生产环境):
- 总大小: ~1-2 MB (压缩后)
- vendor chunk: ~300 KB (Vue核心)
- element chunk: ~400 KB (UI组件)
- maplibre chunk: ~500 KB (地图引擎)
- app chunk: ~200 KB (应用代码)
运行时性能:
- 首屏加载: < 2s (3G网络)
- 地图初始化: < 500ms
- 图层切换: < 100ms
- 搜索响应: < 300ms (含防抖)