最近在指导学弟学妹做毕业设计时发现一个挺普遍的现象很多同学用 ArkTS 开发的应用功能虽然实现了但代码结构一团乱麻所有逻辑都堆在 UI 页面里稍微改点需求就牵一发而动全身更别提后续维护和扩展了。这其实就是典型的“Demo 思维”只求功能跑通忽略了软件工程中至关重要的高内聚、低耦合原则。今天我就以构建一个“校园服务应用”整合课表查询与通知推送为例分享一下如何用 ArkTS 从零开始搭建一个结构清晰、易于维护的毕业设计项目。1. 背景痛点为什么你的毕业设计只是“玩具”很多同学的项目都存在以下几个通病导致其离一个“合格”的毕业设计相去甚远逻辑混杂难以维护所有网络请求、数据处理、业务逻辑和 UI 渲染代码都写在Component装饰的页面文件中。一个文件动辄几百行查找和修改bug如同大海捞针。缺乏状态管理数据流混乱页面间传递数据靠层层prop透传或者滥用AppStorage和LocalStorage导致数据更新来源不明状态同步困难。网络请求裸奔错误处理缺失直接在aboutToAppear或按钮事件里写http.createHttp().request没有统一的封装。错误处理如网络异常、服务器错误要么没有要么每个页面重复写一遍。无视性能体验卡顿大量同步操作阻塞主线程图片不经处理直接加载列表滚动卡顿应用冷启动缓慢这些都会让答辩老师对你的技术能力产生质疑。毫无测试稳定性存疑整个项目没有单元测试或集成测试功能是否正常全靠手动点击。一旦修改代码根本无法快速验证是否引入了新问题。2. 技术选型为什么是 ArkTS对于鸿蒙生态下的毕业设计技术栈选择很关键。我们对比一下传统 Web 前端 (Vue/React) 小程序虽然生态成熟但无法直接利用鸿蒙系统的原生能力如分布式软总线、原子化服务性能有上限且作为毕业设计技术新颖性不足。纯 Java/JS 开发早期的鸿蒙开发方式在声明式 UI 和开发效率上已不占优势且 ArkTS 正是它们的演进和整合。ArkTS它是鸿蒙生态的“亲儿子”语言基于 TypeScript提供了声明式 UI 范式。其优势在于性能更优通过方舟编译器生成高效机器码UI 渲染性能接近原生。开发高效声明式语法让 UI 描述更直观状态管理State,Prop,Link与 UI 自动绑定减少胶水代码。能力全面直接、便捷地调用所有鸿蒙系统 API为应用添加分布式、原子化等特色功能铺平了道路。类型安全TypeScript 的强类型系统能在编码阶段发现大量潜在错误这对大型项目或团队协作至关重要。因此选择 ArkTS 不仅能做出功能完备的应用更能体现你对前沿技术栈的掌握为毕业设计增加含金量。3. 核心实现构建清晰的分层架构我们的目标是构建一个“课表查询通知推送”的应用。核心是设计一个分层架构通常可以划分为数据模型层Model、服务/逻辑层Service/ViewModel、视图层View。3.1 项目结构与分层src/main/ets/ ├── entryability ├── pages │ └── Index.ets // 视图层-主页面 ├── model │ ├── Course.ts // 数据模型-课程 │ └── Notification.ts // 数据模型-通知 ├── service │ ├── ApiService.ts // 网络请求封装 │ ├── CourseService.ts // 课程业务逻辑 │ └── NotificationService.ts // 通知业务逻辑 ├── utils │ ├── http.ts // 网络请求底层封装 │ ├── storage.ts // 数据持久化工具 │ └── logger.ts // 日志工具 └── viewmodel ├── CourseViewModel.ts // 课程视图模型状态管理 └── NotificationViewModel.ts // 通知视图模型3.2 网络模块封装 (utils/http.ts)这是基础中的基础。我们不应该在每个需要网络请求的地方都创建HttpClient实例。// utils/http.ts import http from ohos.net.http; import { logger } from ./logger; // 自定义日志工具 class HttpRequest { private client: http.HttpClient; constructor() { this.client http.createHttp(); } // 统一的请求方法 async requestT(url: string, options: http.HttpRequestOptions): PromiseT { try { const response await this.client.request(url, options); const result JSON.parse(response.result as string); // 假设后端统一返回格式 { code: 0, data: T, message: success } if (result.code 0) { return result.data as T; } else { // 业务逻辑错误 logger.error(API Error: ${url}, result.message); throw new Error(result.message || Request failed); } } catch (error) { // 网络或解析错误 logger.error(Network Error: ${url}, error); throw new Error(Network request failed); } finally { // 重要释放资源避免内存泄漏 this.client.destroy(); } } // 封装 GET 请求 getT(url: string, params?: Recordstring, string): PromiseT { const fullUrl params ? ${url}?${new URLSearchParams(params).toString()} : url; return this.request(fullUrl, { method: http.RequestMethod.GET }); } // 封装 POST 请求 postT(url: string, data: object): PromiseT { return this.request(url, { method: http.RequestMethod.POST, header: { Content-Type: application/json }, extraData: JSON.stringify(data) }); } } export const httpRequest new HttpRequest();3.3 视图模型与状态管理 (viewmodel/CourseViewModel.ts)这是连接视图和数据的桥梁负责管理页面状态和业务逻辑。// viewmodel/CourseViewModel.ts import { Course } from ../model/Course; import { CourseService } from ../service/CourseService; export class CourseViewModel { // 使用 State 装饰器管理状态当状态变更时依赖它的UI会自动更新 State courseList: ArrayCourse []; State isLoading: boolean false; State errorMessage: string ; private courseService: CourseService; constructor() { this.courseService new CourseService(); } // 加载课程数据 async loadCourses(week: number): Promisevoid { // 防止重复加载 if (this.isLoading) { return; } this.isLoading true; this.errorMessage ; try { const data await this.courseService.fetchCourses(week); this.courseList data; } catch (error) { this.errorMessage 加载失败: ${error.message}; logger.error(Failed to load courses, error); } finally { this.isLoading false; } } // 其他业务方法如添加、删除课程等 addCourse(course: Course): void { // 先更新本地状态提供即时反馈 this.courseList [...this.courseList, course]; // 再异步同步到服务器 this.courseService.syncAddCourse(course).catch(e { // 如果同步失败可以回滚或提示用户 logger.error(Sync add course failed, e); }); } }3.4 视图层 (pages/Index.ets)视图层只关心如何展示数据和接收用户交互将复杂的逻辑委托给 ViewModel。// pages/Index.ets import { CourseViewModel } from ../viewmodel/CourseViewModel; Entry Component struct Index { // 在组件中实例化或通过依赖注入获取 ViewModel private courseVM: CourseViewModel new CourseViewModel(); build() { Column({ space: 20 }) { // 标题 Text(我的课表).fontSize(30).fontWeight(FontWeight.Bold) // 加载状态提示 if (this.courseVM.isLoading) { LoadingProgress().width(50).height(50) Text(加载中...) } // 错误信息提示 if (this.courseVM.errorMessage) { Text(this.courseVM.errorMessage) .fontColor(Color.Red) .fontSize(14) } // 课程列表 List({ space: 10 }) { ForEach(this.courseVM.courseList, (item: Course) { ListItem() { CourseItem({ course: item }) // 使用子组件进一步解耦 } }, (item: Course) item.id.toString()) } .layoutWeight(1) // 占据剩余空间 .width(100%) // 刷新按钮 Button(刷新课表) .onClick(() { // 视图层只触发动作具体逻辑在ViewModel中 this.courseVM.loadCourses(1); }) .width(80%) } .padding(20) .width(100%) .height(100%) .onPageShow(() { // 页面显示时加载数据 this.courseVM.loadCourses(1); }) } } // 独立的课程项子组件 Component struct CourseItem { Prop course: Course; // 通过Prop接收数据 build() { Row({ space: 10 }) { Column({ space: 5 }) { Text(this.course.name).fontSize(18).fontWeight(FontWeight.Medium) Text(${this.course.time} ${this.course.location}).fontSize(14).fontColor(Color.Gray) } .layoutWeight(1) .alignItems(HorizontalAlign.Start) } .padding(15) .backgroundColor(Color.White) .borderRadius(12) .shadow({ radius: 5, color: #00000010, offsetX: 0, offsetY: 2 }) .width(100%) } }4. 性能与安全不可忽视的环节4.1 启动耗时优化延迟加载与按需加载对于非首屏必需的模块如某些复杂工具库、子页面使用动态导入import()或路由懒加载。资源优化图片使用合适的格式WebP和尺寸避免大图直接渲染。可以使用Image组件的alt和objectFit属性。避免主线程阻塞将耗时的同步计算如复杂数据转换放入TaskPool任务池中异步执行。合理使用aboutToAppear不要在这里执行大量同步操作或网络请求。数据加载可以稍晚一点优先保证页面框架渲染出来。4.2 数据隐私与安全敏感信息存储用户的登录令牌、个人信息等必须使用安全的持久化方案如ohos.security.huks硬件密钥库或经过加密后存入Preferences。切勿明文存储在AppStorage或文件中。网络传输安全确保所有 API 请求都使用 HTTPS。对敏感请求参数可以考虑进行非对称加密。权限最小化在module.json5中只申请应用必需的最小权限并在使用前动态检查 (abilityAccessCtrl) 和向用户解释用途。5. 生产避坑指南资源释放像HttpClient、文件句柄、订阅的事件监听器必须在组件销毁aboutToDisappear或使用完毕后及时释放/取消订阅否则会导致内存泄漏。异步竞态处理快速连续点击按钮触发多次网络请求时可能导致数据显示错乱。解决方法在 ViewModel 中设置isLoading锁或使用AbortController取消之前的请求。设备兼容性鸿蒙设备屏幕尺寸和形态多样。UI 布局应多使用弹性布局Flex、百分比、layoutWeight避免固定尺寸。使用媒体查询(ohos.mediaquery) 来适配不同设备。列表渲染优化ForEach渲染长列表时务必提供稳定的、唯一的key生成函数帮助 ArkUI 高效复用组件节点。对于超长列表考虑实现分页或虚拟滚动。状态管理陷阱State用于组件内部状态Prop用于父子组件单向同步Link用于双向同步Provide和Consume用于跨组件层级传递。理解其区别避免滥用AppStorage造成全局状态污染。调试与日志开发阶段善用console和logger但上线前务必移除或关闭无关的console.log。可以封装一个日志工具根据编译环境开关日志级别。总结与动手建议通过以上步骤我们构建的不仅仅是一个功能性的“校园服务应用”更是一个具备良好架构、易于测试和扩展的工程化项目。这种分层设计使得数据层变更不影响UI。业务逻辑可以独立进行单元测试。UI组件可以高度复用。你的毕业设计完全可以参照这个模式进行重构。试着分析你当前的项目能否将混杂在页面里的网络请求抽离成独立的Service能否将页面状态和逻辑提取到ViewModel中是否有可以复用的UI组件能被抽象出来有哪些性能瓶颈可以优化如图片、列表最后思考一下扩展性如果未来要增加“成绩查询”、“校园卡充值”甚至“分布式设备协同”功能按照现在的架构你需要修改多少地方一个优秀的毕业设计应该能让答辩老师清晰地看到你对软件设计原则的理解和应用而 ArkTS 为你提供了实现这些原则的优秀工具。动手重构吧你会发现代码质量提升后开发和维护的心情都会变得愉悦。