
一、好的架构不是一开始就设计出来的很多新手拿到需求就直接写页面从第一个组件开始堆代码写到一半发现组件之间传参乱成一团、数据流无法追踪、换个需求要改十几个文件。问题不在编码能力而在起点——没有先想清楚数据和界面怎么组织。架构设计的本质是回答三个问题数据存在哪里、组件之间怎么通信、状态变化后界面怎么更新。把这三个问题理清楚再动笔代码自然有条理后期维护也轻松很多。二、模块化拆分按职责还是按功能模块拆分有两种思路按职责分和按功能分。按职责分是把应用分成数据层、业务层、界面层所有数据操作放在一起、所有业务逻辑放在一起、所有 UI 组件放在一起。这种方式适合逻辑复杂、团队分工明确的大型项目但中小型应用容易出现数据层和界面层之间的调用链路过长问题。按功能分是把相关的数据、逻辑、界面打包成一个完整的模块比如笔记模块包含笔记的数据结构、增删改查逻辑、列表和详情页面。这种方式更直观改一个功能只需要在一个模块内工作适合快速迭代的中小型应用。实际项目中两种思路可以结合用按功能分的大框架用按职责分的思路设计每个功能模块内部。鸿蒙应用的标准工程结构已经给出了这个分层entry/src/main/ets/ ├── entryability/ # 入口Ability负责应用生命周期 ├── pages/ # 页面一个页面一个文件 ├── components/ # 可复用UI组件 ├── models/ # 数据模型和TypeScript接口 ├── services/ # 业务逻辑层API调用、数据处理 ├── utils/ # 工具函数 └── store/ # 跨组件共享的状态管理三、数据模型先定义形状再写逻辑很多开发者习惯边写边定义数据结构这很容易导致数据形状散落在代码各处改一个字段要全局搜索。正确的做法是先把所有数据模型集中定义在models目录下形成一份全局的类型契约。// models/NoteModel.ets// 笔记主体数据exportinterfaceNoteData{id:numbertitle:stringcontent:stringtags:string[]isPinned:booleanisArchived:booleancreatedAt:numberupdatedAt:number}// 笔记列表筛选条件exportinterfaceNoteFilter{keyword:stringtag:string|nullshowArchived:boolean}// 导出任务配置exportinterfaceExportConfig{format:markdown|json|txtincludeArchived:boolean}// 用户配置exportinterfaceUserSettings{theme:light|dark|autofontSize:numberautoSave:boolean}在 ArkTS 中接口定义放在单独文件里需要用的地方import进来。这种集中定义的方式带来一个额外的好处TypeScript 的智能提示会完整覆盖所有数据字段写代码时不容易拼错字段名。四、服务层把业务逻辑从 UI 中抽离业务逻辑不应该写在组件内部。组件的职责是渲染界面和处理用户交互业务规则、数据校验、API 调用都应该抽到服务层。这样同一个业务逻辑可以被多个页面复用测试时也更容易单独验证逻辑是否正确。// services/NoteService.etsimport{NoteData,NoteFilter,ExportConfig}from../models/NoteModel.etsclassNoteService{// 从存储读取笔记列表getNoteList():NoteData[]{constrawAppStorage.getstring(note_list)if(!raw)return[]try{returnJSON.parse(raw)asNoteData[]}catch{return[]}}// 根据筛选条件过滤笔记filterNotes(notes:NoteData[],filter:NoteFilter):NoteData[]{returnnotes.filter(note{// 关键词匹配标题和内容if(filter.keyword){constqfilter.keyword.toLowerCase()if(!note.title.toLowerCase().includes(q)!note.content.toLowerCase().includes(q)){returnfalse}}// 标签过滤if(filter.tag!note.tags.includes(filter.tag)){returnfalse}// 归档笔记if(!filter.showArchivednote.isArchived){returnfalse}returntrue})}// 创建新笔记createNote(title:string):NoteData{constnote:NoteData{id:Date.now(),title:title||无标题,content:,tags:[],isPinned:false,isArchived:false,createdAt:Date.now(),updatedAt:Date.now()}returnnote}// 导出笔记为 MarkdownexportToMarkdown(notes:NoteData[],config:ExportConfig):string{constfilteredthis.filterNotes(notes,{keyword:,tag:null,showArchived:config.includeArchived})returnfiltered.map(n#${n.title}\n\n**标签**:${n.tags.join(, )||无}\n**更新时间**:${newDate(n.updatedAt).toLocaleString()}\n\n${n.content}\n\n---\n).join(\n)}}exportconstnoteServicenewNoteService()业务逻辑抽离之后组件代码变得非常简洁只需要调用服务方法并更新状态// pages/NoteList.etsimport{noteService}from../services/NoteService.etsEntryComponentstruct NoteList{Statenotes:NoteData[][]Statefilter:NoteFilter{keyword:,tag:null,showArchived:false}aboutToAppear(){this.notesnoteService.getNoteList()}getfilteredNotes():NoteData[]{returnnoteService.filterNotes(this.notes,this.filter)}addNote(){constnewNotenoteService.createNote(新笔记)this.notes.unshift(newNote)AppStorage.setOrCreate(note_list,JSON.stringify(this.notes))}}五、组件通信从父子到跨层级ArkUI 的组件通信有几种典型场景父子组件通过属性和方法通信、兄弟组件通过父组件中转、跨层级的深层组件通信。父子通信最简单父组件给子组件传数据用Prop子组件通知父组件用回调函数// 父组件StateselectedId:number0ListItem({note:note,isSelected:this.selectedIdnote.id,onTap:(){this.selectedIdnote.id}})// 子组件Propnote:NoteDataPropisSelected:booleanonTap:()void// 子组件内部触发父组件回调.onClick(()this.onTap())跨层级通信有两种方案。方案一是状态提升把需要共享的状态放到最近的公共祖先组件中通过层层传递到达目标组件。这种方式简单直接但层级深了之后传参链变得很长代码维护困难。方案二是用AppStorage或LocalStorage作为全局状态容器任何组件都可以直接读写不需要层层传递。缺点是状态来源不够直观调试时需要追踪哪里改了值。// 方案二在任意组件中直接访问全局状态aboutToAppear(){constsettingsAppStorage.getUserSettings(user_settings)if(settings)this.settingssettings}// 任何地方修改全局生效AppStorage.setOrCreate(user_settings,this.settings)推荐的做法是分层管理页面级状态用State管理跨页面共享但不需要持久化的状态用AppStorage需要持久化的数据放存储服务中。六、完整工程示例把上述思路整合起来一个最小可用的笔记应用架构如下// models/NoteModel.ets exportinterfaceNoteData{id:number;title:string;content:stringtags:string[];isPinned:boolean;isArchived:booleancreatedAt:number;updatedAt:number}// services/NoteService.ets import{NoteData}from../models/NoteModel.etsclassNoteService{getList():NoteData[]{constrawAppStorage.getstring(notes)returnraw?JSON.parse(raw)asNoteData[]:[]}save(notes:NoteData[]){AppStorage.setOrCreate(notes,JSON.stringify(notes))}}exportconstnoteServicenewNoteService()// pages/NoteList.ets import{noteService}from../services/NoteService.etsEntryComponentstruct NoteList{Statenotes:NoteData[][]aboutToAppear(){this.notesnoteService.getList()}build(){Column(){Row(){Text(共${this.notes.length}条).fontSize(14).fontColor(#888)Blank()Button(新建).onClick((){this.notes.unshift({id:Date.now(),title:新笔记,content:,tags:[],isPinned:false,isArchived:false,createdAt:Date.now(),updatedAt:Date.now()})noteService.save(this.notes)})}.padding(16)List({space:8}){ForEach(this.notes,(note:NoteData){ListItem().child(Row({space:12}){Column(){Text(note.title).fontSize(16).fontWeight(FontWeight.Medium)}.layoutWeight(1)Text(note.tags.join(,)||无标签).fontSize(12).fontColor(#007DFF)}.padding(14).backgroundColor(#FFF).borderRadius(10))})}.padding({left:16,right:16})}.width(100%).height(100%).backgroundColor(#F5F5F5)}}七、实战建议架构没有标准答案只有适合和不适合。中小型应用不需要过度设计三个模块models、services、pages足够支撑日常迭代。业务变复杂后再按需拆分服务层、引入状态管理库。判断要不要抽成一个服务的标准很简单如果这个逻辑会被两个以上的地方调用就应该抽出来。孤立的页面逻辑放在组件里即可不必强行模块化。组件拆分不是越细越好。把一个组件拆成十个原子组件名称取来取去开发体验反而变差。优先拆复用率最高的公共部分比如笔记卡片、列表项、操作按钮组其他业务耦合强的部分保持内聚。基于 HarmonyOS NEXTAPI 12。