# 移动端开发规范总结文档 ## 📋 文档概述 **文档目的**: 为智慧校园移动端开发提供完整的规范性指导文档 **适用项目**: `/mobile/` 目录下的 Vue 2 + uni-app + uview-ui 项目 **最后更新**: 2025-01-03 更新技术栈信息 **维护人员**: wuzilong --- ## 🎯 项目技术架构总览 ### 核心技术栈 ```json { "基础框架": "uni-app 2.x + Vue 2.6.14", "UI组件库": "uview-ui 2.0.38 (Vue 2版本)", "开发语言": "JavaScript (ES6+) + TypeScript 4.5.5", "样式预处理": "Sass 1.90.0", "构建工具": "Vue CLI 5.0.8", "代码规范": "ESLint", "状态管理": "Vuex 3.2.0" } ``` ### 项目特色 - **🎨 现代化设计**: CSS 变量设计系统 + 响应式布局 - **♿ 无障碍访问**: 完整的 ARIA 支持和键盘导航 - **🎭 用户体验**: 动画效果、手势识别、触觉反馈 - **🚀 性能优化**: 智能缓存、组件懒加载、图片优化 - **📱 多端支持**: H5、微信小程序、App、鸿蒙全平台 --- ## 📁 项目结构规范 ### 标准目录结构 ``` mobile/ ├── src/ # 源码目录 │ ├── components/ # 通用组件 │ │ ├── NavBar.vue # 自定义导航栏 │ │ ├── Card.vue # 通用卡片组件 │ │ ├── ServiceItem.vue # 服务项组件 │ │ ├── StatCard.vue # 统计卡片组件 │ │ └── GridLayout.vue # 网格布局组件 │ ├── pages/ # 页面文件 │ │ ├── index/ # 首页 (底部导航) │ │ ├── workbench/ # 办公中心 (底部导航) │ │ └── profile/ # 我的页面 (底部导航) │ ├── utils/ # 工具类库 │ │ ├── request.js # 网络请求工具 │ │ ├── sse.js # SSE实时数据 │ │ ├── device.js # 设备检测 │ │ ├── animations.js # 动画工具 │ │ ├── gestures.js # 手势识别 │ │ └── accessibility.js # 无障碍访问 │ ├── static/ # 静态资源 │ │ ├── tabbar/ # 底部导航图标 │ │ └── fonts/ # 字体文件 │ ├── App.vue # 应用入口 │ ├── main.js # 项目入口 │ ├── pages.json # 页面路由配置 │ ├── manifest.json # 应用配置 │ └── uni.scss # 全局样式变量 ├── package.json # 项目配置 ├── vite.config.js # Vite构建配置 ├── eslint.config.cjs # ESLint配置 └── README.md # 项目说明 ``` ### 文件命名规范 - **页面文件**: `kebab-case` (如: `index.vue`) - **组件文件**: `PascalCase` (如: `NavBar.vue`, `ServiceItem.vue`) - **工具文件**: `camelCase` (如: `request.js`, `animations.js`) - **静态资源**: `kebab-case` (如: `home-active.svg`) --- ## 🎨 页面开发规范 ### 标准页面模板 ```vue ``` ### 页面开发要点 #### ✅ 必须遵循的规范 1. **导航栏配置**: 使用`navigationStyle: "custom"`,采用自定义 NavBar 组件 2. **安全区域适配**: 使用`env(safe-area-inset-bottom)`处理底部安全区域 3. **scroll-view 容器**: 使用`enhanced`属性提升滚动性能 4. **生命周期管理**: 正确使用`onLoad`、`onShow`、`onUnload` 5. **定时器清理**: 在`onUnload`中清理所有定时器和监听器 #### 🎨 样式规范 ```scss // 使用CSS变量系统 .page-container { background-color: var(--background-color); color: var(--text-primary); } // 使用工具类 .flex-center { display: flex; align-items: center; justify-content: center; } // 使用统一间距 .mt-md { margin-top: var(--spacing-md); } .p-lg { padding: var(--spacing-lg); } ``` --- ## 🧩 组件开发规范 ### 通用组件模板 ```vue ``` ### 组件开发要点 #### ✅ 必须遵循的规范 1. **组件命名**: 使用 PascalCase,具有语义化的名称 2. **属性定义**: 提供完整的 props 配置,包含类型和默认值 3. **事件抛出**: 使用`$emit`向父组件传递事件 4. **样式作用域**: 使用`scoped`避免样式污染 5. **无障碍支持**: 添加适当的 ARIA 标签和键盘支持 6. **触觉反馈**: 在交互操作中添加`uni.vibrateShort()` #### 🎯 组件分类 - **基础组件**: NavBar、Card、Button 等 - **业务组件**: ServiceItem、StatCard 等 - **布局组件**: GridLayout 等 - **工具组件**: Loading、Toast 等 --- ## 🌐 API 接口规范 ### 网络请求工具 ```javascript // utils/request.js const baseURL = process.env.NODE_ENV === 'development' ? 'http://localhost:3000/api' : 'https://your-api-domain.com/api'; // 统一请求方法 export const request = (options) => { return new Promise((resolve, reject) => { uni.request({ url: baseURL + options.url, method: options.method || 'GET', data: options.data || {}, header: { 'Content-Type': 'application/json', Authorization: getToken(), ...options.header, }, success: (res) => { if (res.statusCode === 200) { resolve(res.data); } else { reject(res); } }, fail: (error) => { reject(error); }, }); }); }; // API方法封装 export const api = { // 用户登录 login: (data) => request({ url: '/auth/login', method: 'POST', data }), // 获取用户信息 getUserInfo: () => request({ url: '/user/info' }), // 获取首页数据 getHomeData: () => request({ url: '/home/data' }), }; ``` ### 智能缓存策略 ```javascript // 缓存策略配置 export const cacheStrategy = { short: 5 * 60, // 短期缓存 (5分钟) medium: 30 * 60, // 中期缓存 (30分钟) long: 24 * 60 * 60, // 长期缓存 (24小时) }; // 数据缓存工具 export const storage = { set: (key, value, expire) => { try { const data = { value, timestamp: Date.now(), expire: expire ? Date.now() + expire * 1000 : null, }; uni.setStorageSync(key, JSON.stringify(data)); } catch (e) { console.error('缓存设置失败:', e); } }, get: (key) => { try { const data = uni.getStorageSync(key); if (!data) return null; const parsed = JSON.parse(data); // 检查是否过期 if (parsed.expire && Date.now() > parsed.expire) { uni.removeStorageSync(key); return null; } return parsed.value; } catch (e) { console.error('缓存获取失败:', e); return null; } }, }; ``` ### API 调用最佳实践 #### ✅ 推荐做法 ```javascript // 带缓存的API调用 export const api = { getUserInfo: () => { const cacheKey = 'userInfo'; const cached = storage.get(cacheKey); if (cached) { return Promise.resolve(cached); } return request({ url: '/user/info', }).then(data => { storage.set(cacheKey, data, cacheStrategy.medium); return data; }); }, }; // 在页面中使用 methods: { async loadUserInfo() { try { const userInfo = await api.getUserInfo(); this.userInfo = userInfo; } catch (error) { uni.showToast({ title: '获取用户信息失败', icon: 'none' }); } } } ``` --- ## 🎭 用户体验规范 ### 动画效果规范 #### CSS 动画类 ```scss // 全局动画类 (uni.scss) .fade-in { animation: fadeIn var(--transition-medium); } .slide-up { animation: slideUp var(--transition-medium); } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes slideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } ``` #### JavaScript 动画工具 ```javascript // utils/animations.js export const animationUtils = { fadeIn: (element) => { element.style.animation = 'fadeIn 0.3s ease'; }, slideUp: (element) => { element.style.animation = 'slideUp 0.3s ease'; }, scale: (element) => { element.style.animation = 'scale 0.2s ease'; }, }; ``` ### 手势识别规范 ```javascript // utils/gestures.js export const gestureManager = { init: (element, callbacks) => { let startX, startY, startTime; element.addEventListener('touchstart', (e) => { startX = e.touches[0].clientX; startY = e.touches[0].clientY; startTime = Date.now(); }); element.addEventListener('touchend', (e) => { const endX = e.changedTouches[0].clientX; const endY = e.changedTouches[0].clientY; const deltaX = endX - startX; const deltaY = endY - startY; // 判断手势类型 if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 50) { if (deltaX > 0) { callbacks.swipeRight && callbacks.swipeRight(); } else { callbacks.swipeLeft && callbacks.swipeLeft(); } } }); }, }; ``` ### 无障碍访问规范 ```javascript // utils/accessibility.js export const accessibilityUtils = { // 设置焦点 setFocus: (element) => { element.focus(); }, // 添加ARIA标签 addAriaLabel: (element, label) => { element.setAttribute('aria-label', label); }, // 键盘导航支持 enableKeyboardNavigation: (container) => { container.addEventListener('keydown', (e) => { switch (e.key) { case 'Tab': // Tab导航逻辑 break; case 'Enter': case ' ': // 激活元素 e.target.click(); break; case 'Escape': // 取消操作 break; } }); }, }; ``` --- ## 📱 页面配置规范 ### pages.json 标准配置 ```json { "pages": [ { "path": "pages/index/index", "style": { "navigationBarTitleText": "首页", "navigationStyle": "custom" } }, { "path": "pages/workbench/index", "style": { "navigationBarTitleText": "办公中心", "navigationStyle": "custom" } }, { "path": "pages/profile/index", "style": { "navigationBarTitleText": "我的", "navigationStyle": "custom" } } ], "globalStyle": { "navigationBarTextStyle": "white", "navigationBarTitleText": "智慧校园", "navigationBarBackgroundColor": "rgb(93, 135, 255)", "backgroundColor": "#f8f9fa" }, "tabBar": { "color": "#999999", "selectedColor": "rgb(93, 135, 255)", "borderStyle": "white", "backgroundColor": "#ffffff", "height": "60px", "fontSize": "12px", "iconWidth": "22px", "iconHeight": "22px", "list": [ { "pagePath": "pages/index/index", "text": "首页", "iconPath": "static/tabbar/home.svg", "selectedIconPath": "static/tabbar/home-active.svg" }, { "pagePath": "pages/workbench/index", "text": "办公中心", "iconPath": "static/tabbar/workbench.svg", "selectedIconPath": "static/tabbar/workbench-active.svg" }, { "pagePath": "pages/profile/index", "text": "我的", "iconPath": "static/tabbar/profile.svg", "selectedIconPath": "static/tabbar/profile-active.svg" } ] } } ``` ### manifest.json 应用配置 ```json { "name": "智慧校园", "appid": "", "description": "基于Vue3+uniapp的智慧校园移动应用", "versionName": "1.0.0", "versionCode": "100", "transformPx": false, "vueVersion": "3", "app-plus": { "usingComponents": true, "nvueStyleCompiler": "uni-app", "compilerVersion": 3, "splashscreen": { "alwaysShowBeforeRender": true, "waiting": true, "autoclose": true, "delay": 0 }, "distribute": { "android": { "permissions": [ "", "", "", "", "" ] } } }, "mp-weixin": { "appid": "", "setting": { "urlCheck": false }, "usingComponents": true }, "uniStatistics": { "enable": false } } ``` --- ## 🚀 性能优化规范 ### 代码优化策略 #### 组件懒加载 ```javascript // 页面中使用懒加载组件 export default { components: { // 懒加载大型组件 HeavyComponent: () => import('@/components/HeavyComponent.vue'), // 条件加载组件 ConditionalComponent: () => { if (condition) { return import('@/components/ConditionalComponent.vue'); } return null; }, }, }; ``` #### 图片优化 ```vue ``` #### 头像鉴权加载 - 统一从 `/system/user/getInfo` 获取 `avatar`(OSS ID)与 `avatarUrl`(预签名地址)。 - 页面中优先识别 `avatar` 是否为 OSS ID,使用 `uni.request`(`responseType: 'arraybuffer'`)访问 `/resource/oss/download/{ossId}` 并转换为 `Blob URL/Base64`,确保 Token 认证生效。 - 若历史账号仍存储直接 URL,则回退到 `avatar` 或 `avatarUrl` 原始值,最后兜底 `/static/111.png`。 - 首页、详情页等通用区域必需复用该策略,避免直接暴露预签名链接导致过期或越权。 #### 列表优化 ```vue ``` ### 网络优化策略 #### 请求优化 ```javascript // 请求防抖 const debounce = (func, wait) => { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; }; // 在搜索中使用防抖 methods: { handleSearch: debounce(function(keyword) { this.searchData(keyword); }, 300), } ``` #### 缓存优化 ```javascript // 分级缓存策略 export const cacheManager = { // 内存缓存(当前会话) memoryCache: new Map(), // 本地缓存(持久化) setCache: (key, data, expire = cacheStrategy.medium) => { // 内存缓存 cacheManager.memoryCache.set(key, data); // 本地缓存 storage.set(key, data, expire); }, getCache: (key) => { // 优先从内存获取 if (cacheManager.memoryCache.has(key)) { return cacheManager.memoryCache.get(key); } // 从本地存储获取 const data = storage.get(key); if (data) { // 回写内存缓存 cacheManager.memoryCache.set(key, data); } return data; }, }; ``` --- ## 🛠️ 开发工具配置 ### Vite 构建配置 ```javascript // vite.config.js import { defineConfig } from 'vite'; import uni from '@dcloudio/vite-plugin-uni'; export default defineConfig({ plugins: [uni()], server: { port: 3000, open: true, host: '0.0.0.0', }, build: { // 生产环境优化 minify: 'terser', terserOptions: { compress: { drop_console: true, drop_debugger: true, }, }, // 代码分割 rollupOptions: { output: { manualChunks: { 'uview-plus': ['uview-plus'], vue: ['vue'], }, }, }, }, // CSS优化 css: { preprocessorOptions: { scss: { additionalData: '@import "@/uni.scss";', }, }, }, }); ``` ### ESLint 代码规范 ```javascript // eslint.config.cjs module.exports = [ { files: ['src/**/*.{js,vue}'], languageOptions: { ecmaVersion: 2022, sourceType: 'module', }, rules: { // 代码风格 indent: ['error', 2], quotes: ['error', 'single'], semi: ['error', 'never'], // Vue 3 规范 'vue/component-name-in-template-casing': ['error', 'PascalCase'], 'vue/no-unused-components': 'error', 'vue/no-unused-vars': 'error', // 性能相关 'no-console': 'warn', 'no-debugger': 'error', 'no-unused-vars': 'error', }, }, ]; ``` --- ## 📋 开发命令规范 ### 标准开发命令 ```bash # 环境准备 npm install # 安装依赖 npm audit fix # 修复安全漏洞 # 开发调试 npm run dev:h5 # H5开发调试 npm run dev:mp-weixin # 微信小程序开发 npm run dev:app # App开发 # 生产构建 npm run build:h5 # H5生产构建 npm run build:mp-weixin # 微信小程序构建 npm run build:app # App构建 # 代码质量 npm run lint # 代码检查 npm run lint:fix # 自动修复代码问题 # 清理缓存 npm run clean # 清理构建缓存 ``` ### 发布流程 ```bash # 版本发布流程 npm run lint # 1. 代码检查 npm run build:h5 # 2. 构建H5版本 npm run build:mp-weixin # 3. 构建小程序版本 npm run build:app # 4. 构建App版本 ``` --- ## ✅ 最佳实践总结 ### 🎯 核心开发原则 #### 1. 代码规范 - **文件组织**: 按功能模块清晰组织文件结构 - **组件复用**: 提取通用组件,避免重复代码 - **样式管理**: 使用 CSS 变量系统,便于主题切换 - **命名规范**: 采用语义化命名,提高代码可读性 #### 2. 性能优化 - **懒加载**: 合理使用组件和路由懒加载 - **缓存策略**: 实施分级缓存,提升响应速度 - **图片优化**: 使用响应式图片和懒加载 - **代码分割**: 合理分割代码包,减少初始加载 #### 3. 用户体验 - **加载状态**: 添加适当的加载提示 - **错误处理**: 友好的错误提示和降级方案 - **无障碍访问**: 完整的 ARIA 支持和键盘导航 - **触觉反馈**: 在交互操作中添加震动反馈 #### 4. 兼容性保证 - **多端测试**: 确保 H5、小程序、App 各端兼容 - **设备适配**: 支持各种屏幕尺寸和分辨率 - **版本兼容**: 考虑不同系统版本的兼容性 - **性能监控**: 监控各端性能表现 ### 🔄 开发流程 ``` 1. 需求分析 → 确定页面结构和功能 2. 组件设计 → 拆分可复用组件 3. 页面开发 → 按模板编写页面代码 4. 样式调优 → 使用统一的设计规范 5. 功能测试 → 多平台测试确保兼容性 6. 性能优化 → 优化加载速度和用户体验 7. 代码审查 → 确保代码质量和规范性 8. 文档更新 → 同步更新相关文档 ``` ### 🎨 设计理念 - **现代简约**: 清新简洁的界面设计 - **一致性**: 统一的交互体验和视觉风格 - **响应式**: 适配多种屏幕尺寸和设备 - **无障碍**: 友好的无障碍访问支持 - **高性能**: 流畅的动画和快速的响应 --- ## 📚 相关文档 ### 技术文档 - **详细规范**: `.cursor/cursorrules-mobile.md` - **项目说明**: `mobile/README.md` - **API 接口**: `docs/开发文档/API/` ### 学习资源 - **Vue 2 官方文档**: https://v2.cn.vuejs.org/ - **uni-app 官方文档**: https://uniapp.dcloud.net.cn/ - **uview-ui 文档**: https://www.uviewui.com/ ### 工具支持 - **开发工具**: HBuilderX、VSCode - **调试工具**: Vue DevTools、uni-app 调试工具 - **构建工具**: Vite、uni-cli --- ## 🔄 维护更新 ### 更新日志 - **2025-07-02**: 初始版本,基于实际 mobile 工程代码编写 - **规范来源**: 深入分析 mobile 项目架构和代码实现 - **维护人员**: wuzilong ### 后续计划 - 根据实际开发反馈持续优化规范 - 定期检查规范与实际代码的一致性 - 新增功能时同步更新规范文档 - 收集团队使用反馈并改进规范 --- **📌 重要提醒**: 本规范基于实际项目代码制定,所有示例代码均可直接使用。遵循本规范能够确保代码质量,提升开发效率,并为用户提供优秀的移动端体验。 **🎯 目标**: 为智慧校园移动端开发提供完整、准确、实用的规范指导,确保项目的可维护性和可扩展性。