首页 > 其他分享 >HarmonyOs DevEco Studio小技巧34--鸿蒙应用架构设计 MVVM模式与三层架构、工程目录迁移

HarmonyOs DevEco Studio小技巧34--鸿蒙应用架构设计 MVVM模式与三层架构、工程目录迁移

时间:2024-11-25 09:31:21浏览次数:9  
标签:架构设计 https DevEco -- brief huawei imageSrc 模块 id

MVVM模式

ArkUI采取MVVM = Model + View + ViewModel模式,其中状态管理模块起到的就是ViewModel的作用,将数据与视图绑定在一起,更新数据的时候直接更新视图。

在 MVVM 模式中:

  • Model(模型):model文件夹用于存储数据模型。它表示组件或其他相关业务逻辑之间传输的数据,是对原始数据的进一步处理。
  • View(视图):view 文件夹用于存储用户界面的相关代码。它是应用程序中用户直接看到和与之交互的部分,专注于展示数据和接收用户输入,不包含任何复杂的业务逻辑。
  • ViewModel(视图模型):作为连接视图和模型的桥梁。它将模型中的数据转换为适合视图展示的形式,并处理视图的用户操作,将其转换为对模型的操作。

MVVM 模式的主要优点包括:

  1. 清晰的职责分离:使得各部分的职责更加明确,便于开发和维护。
  2. 双向数据绑定:视图和视图模型之间的数据可以自动同步,减少了手动更新数据的繁琐工作。
  3. 可测试性:由于各部分职责明确,使得对模型、视图模型的单元测试更加容易编写和执行。
  4. 提高开发效率:开发者可以更专注于各自负责的部分,并且能够快速响应需求变化。

许多现代的前端框架(如 Vue.js、Angular 等)都采用了 MVVM 模式或其变体来构建应用程序。

Step 1

目录结构调整

为了让代码更加清晰,容易维护,我们需要对代码进行分层管理,常见的数据结构放置在model文件夹中,UI组件放置在view文件夹中,并以对应的组件名命名。

Step 2

建立model文件夹

在entry/src/main/ets文件夹下点击右键 - > new(新建) - > Directory(目录)。

 文件夹命名为model。

model文件夹用于存储数据模型。它表示组件或其他相关业务逻辑之间传输的数据,是对原始数据的进一步处理。 

Step 3

创建对应的Class文件

在刚才新建的model文件夹下新建两个ets文件,分别命名为BannerClass和ArticleClass。

一般来说 model文件夹里面 BannerModel 一般是这样的。看公司安排。

//BannerClass
export class BannerClass {
  id: string = '';
  imageSrc: string = '';
  url: string = '';

  constructor(id: string, imageSrc: string, url: string) {
    this.id = id;
    this.imageSrc = imageSrc;
    this.url = url;
  }
}
//ArticleClass 
export class ArticleClass {
  id: string = '';
  imageSrc: string = '';
  title: string = '';
  brief: string = '';
  webUrl: string = '';

  constructor(id: string, imageSrc: string, title: string, brief: string, webUrl: string) {
    this.id = id;
    this.imageSrc = imageSrc;
    this.title = title;
    this.brief = brief;
    this.webUrl = webUrl;
  }
}

 

Step 4

创建view文件夹,用于存储UI组件

在entry/src/main/ets文件夹下点击右键 - > new - > Directory,命名为view,用于存放页面相关的自定义组件。

通过右键单击view文件夹,选择New-> ArkTS file,分别创建EnablementView.ets和TutorialView.ets以及Banner.ets文件。

//EnablementView
import { ArticleClass } from '../model/ArticleClass';
import { bufferToString } from '../util/BufferUtil';

@Component
export struct EnablementView {
  @State enablementList: Array<ArticleClass> = [];

  aboutToAppear(): void {
    this.getEnablementDataFromJSON()
  }

  getEnablementDataFromJSON() {
    getContext(this).resourceManager.getRawFileContent('EnablementData.json').then(value => {
      this.enablementList = JSON.parse(bufferToString(value)) as ArticleClass[];
    })
  }

  build() {
    Column() {
      Text('赋能套件')
        .fontColor('#182431')
        .fontSize(16)
        .fontWeight(500)
        .fontFamily('HarmonyHeiTi-medium')
        .textAlign(TextAlign.Start)
        .padding({ left: 16 })
        .margin({ bottom: 8.5 })
      Grid() {
        ForEach(this.enablementList, (item: ArticleClass) => {
          GridItem() {
            EnablementItem({ enablementItem: item })
          }
        }, (item: ArticleClass) => item.id)
      }
      .rowsTemplate('1fr')
      .columnsGap(8)
      .scrollBar(BarState.Off)
      .height(169)
      .padding({ top: 2, left: 16, right: 16 })

    }
    .margin({ top: 18 })
    .alignItems(HorizontalAlign.Start)
  }
}


@Component
struct EnablementItem {
  @Prop enablementItem: ArticleClass;

  build() {
    Column() {
      Image($r(this.enablementItem.imageSrc))
        .width('100%')
        .objectFit(ImageFit.Cover)
        .height(96)
        .borderRadius({
          topLeft: 16,
          topRight: 16
        })
      Text(this.enablementItem.title)
        .height(19)
        .width('100%')
        .fontSize(14)
        .textAlign(TextAlign.Start)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .maxLines(1)
        .fontWeight(400)
        .padding({ left: 12, right: 12 })
        .margin({ top: 8 })
      Text(this.enablementItem.brief)
        .height(32)
        .width('100%')
        .fontSize(12)
        .textAlign(TextAlign.Start)
        .textOverflow({ overflow: TextOverflow.Ellipsis })
        .maxLines(2)
        .fontWeight(400)
        .fontColor('rgba(0, 0, 0, 0.6)')
        .padding({ left: 12, right: 12 })
        .margin({ top: 2 })
    }
    .width(160)
    .height(169)
    .borderRadius(16)
    .backgroundColor(Color.White)
  }
}
//TutorialView 
import { ArticleClass } from '../model/ArticleClass';
import { bufferToString } from '../util/BufferUtil';

@Component
export struct TutorialView {
  @State tutorialList: Array<ArticleClass> = [];

  aboutToAppear(): void {
    this.getTutorialDataFromJSON()
  }

  getTutorialDataFromJSON() {
    getContext(this).resourceManager.getRawFileContent('TutorialData.json').then(value => {
      this.tutorialList = JSON.parse(bufferToString(value)) as ArticleClass[];
    })
  }

  build() {
    Column() {
      Text('入门教程')
        .fontColor('#182431')
        .fontSize(16)
        .fontWeight(500)
        .fontFamily('HarmonyHeiTi-medium')
        .textAlign(TextAlign.Start)
        .padding({ left: 16 })
        .margin({ bottom: 8.5 })

      List({ space: 12 }) {
        ForEach(this.tutorialList, (item: ArticleClass) => {
          ListItem() {
            TutorialItem({ tutorialItem: item })
          }
        }, (item: ArticleClass) => item.id)
      }
      .scrollBar(BarState.Off)
      .padding({ left: 16, right: 16 })
    }
    .margin({ top: 18 })
    .alignItems(HorizontalAlign.Start)
  }
}

@Component
struct TutorialItem {
  @Prop tutorialItem: ArticleClass;

  build() {
    Row() {
      Column() {
        Text(this.tutorialItem.title)
          .height(19)
          .width('100%')
          .fontSize(14)
          .textAlign(TextAlign.Start)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
          .maxLines(1)
          .fontWeight(400)
          .margin({ top: 4 })
        Text(this.tutorialItem.brief)
          .height(32)
          .width('100%')
          .fontSize(12)
          .textAlign(TextAlign.Start)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
          .maxLines(2)
          .fontWeight(400)
          .fontColor('rgba(0, 0, 0, 0.6)')
          .margin({ top: 5 })
      }
      .height('100%')
      .layoutWeight(1)
      .alignItems(HorizontalAlign.Start)
      .margin({ right: 12 })

      Image($r(this.tutorialItem.imageSrc))
        .height(64)
        .width(108)
        .objectFit(ImageFit.Cover)
        .borderRadius(16)
    }
    .width('100%')
    .height(88)
    .borderRadius(16)
    .backgroundColor(Color.White)
    .padding(12)
    .alignItems(VerticalAlign.Top)
  }
}
//Banner 
import { BannerClass } from '../model/BannerClass';
import { bufferToString } from '../util/BufferUtil';

@Component
export struct Banner {
  @State bannerList: BannerClass[] = [];

  aboutToAppear(): void {
    this.getBannerDataFromJSON();
  }

  getBannerDataFromJSON() {
    getContext(this).resourceManager.getRawFileContent('BannerData.json').then(value => {
      this.bannerList = JSON.parse(bufferToString(value)) as BannerClass[];
    })
  }

  build() {
    Swiper() {
      ForEach(this.bannerList, (item: BannerClass) => {
        Image($r(item.imageSrc))
          .objectFit(ImageFit.Contain)
          .width('100%')
          .borderRadius(16)
          .padding({ top: 11, left: 16, right: 16 })
      }, (item: BannerClass) => item.id)
    }
    .autoPlay(true)
    .loop(true)
    .indicator(
      new DotIndicator()
        .color('#1a000000')
        .selectedColor('#0A59F7'))
  }
}

这里导入了一个自定义工具类 在   '../util/BufferUtil'

import { util } from '@kit.ArkTS';

export function bufferToString(buffer: Uint8Array): string {
  let textDecoder = util.TextDecoder.create('utf-8', {
    ignoreBOM: true
  });
  let resultPut = textDecoder.decodeToString(buffer);
  return resultPut;
}

 Step 5

基本上创建的包都准备好了。最后把资源都加上去就好了

1、启动页的代码

//index
import { Banner } from '../view/Banner';
import { EnablementView } from '../view/EnablementView';
import { TutorialView } from '../view/TutorialView';

@Entry
@Component
struct Index {
  @State message: string = '快速入门';

  build() {
    Column() {
      Text(this.message)
        .fontSize(24)
        .fontWeight(700)
        .width('100%')
        .textAlign(TextAlign.Start)
        .padding({ left: 16 })
        .fontFamily('HarmonyHeiTi-Bold')
        .lineHeight(33)
      Scroll() {
        Column() {
          Banner()
          EnablementView()
          TutorialView()
        }
      }
      .layoutWeight(1)
      .scrollBar(BarState.Off)
      .align(Alignment.TopStart)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F1F3F5')
  }
}

 2、图片资源 下面有图片资源

3、Json文件

//BannerData.json
[
  {
    "id": 0,
    "imageSrc": "app.media.banner_pic0",
    "url": "https://developer.huawei.com/consumer/cn/training/course/video/C101718352529709527"
  },
  {
    "id": 1,
    "imageSrc": "app.media.banner_pic1",
    "url": "https://developer.huawei.com/consumer/cn/"
  },
  {
    "id": 2,
    "imageSrc": "app.media.banner_pic2",
    "url": "https://developer.huawei.com/consumer/cn/deveco-studio/"
  },
  {
    "id": 3,
    "imageSrc": "app.media.banner_pic3",
    "url": "https://developer.huawei.com/consumer/cn/arkts/"
  },
  {
    "id": 4,
    "imageSrc": "app.media.banner_pic4",
    "url": "https://developer.huawei.com/consumer/cn/arkui/"
  },
  {
    "id": 5,
    "imageSrc": "app.media.banner_pic5",
    "url": "https://developer.huawei.com/consumer/cn/sdk"
  }
]
//EnablementData.json
[
  {
    "id": 1,
    "imageSrc": "app.media.enablement_pic1",
    "title": "HarmonyOS第一课",
    "brief": "基于真实的开发场景,提供向导式学习,多维度融合课程等内容,给开发者提供全新的学习体验。",
    "webUrl": "https://developer.huawei.com/consumer/cn/doc/harmonyos-video-courses/video-tutorials-0000001443535745"
  },
  {
    "id": 2,
    "imageSrc": "app.media.enablement_pic2",
    "title": "开发指南",
    "brief": "提供系统能力概述、快速入门,用于指导开发者进行场景化的开发。指南涉及到的知识点包括必要的背景知识、符合开发者实际开发场景的操作任务流(开发流程、开发步骤、调测验证)以及常见问题等。",
    "webUrl": "https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/application-dev-guide-0000001630265101"
  },
  {
    "id": 3,
    "imageSrc": "app.media.enablement_pic3",
    "title": "最佳实践",
    "brief": "针对新发布特性及热点特性提供详细的技术解析和开发最佳实践。",
    "webUrl": "https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/topic-architecture-0000001678045510"
  },
  {
    "id": 4,
    "imageSrc": "app.media.enablement_pic4",
    "title": "Codelabs",
    "brief": "以教学为目的的代码样例及详细的开发指导,帮助开发者一步步地完成指定场景的应用开发并掌握相关知识。Codelabs将最新的鸿蒙生态应用开发技术与典型场景结合,让开发者快速地掌握开发高质量应用的方法。同时支持互动式操作,通过文字、代码和效果联动为开发者带来更佳的学习体验。",
    "webUrl": "https://developer.huawei.com/consumer/cn/doc/harmonyos-codelabs/codelabs-0000001443855957"
  },
  {
    "id": 5,
    "imageSrc": "app.media.enablement_pic5",
    "title": "Sample",
    "brief": "面向不同类型的开发者提供的鸿蒙生态应用开发优秀实践,每个Sample Code都是一个可运行的工程,为开发者提供实例化的代码参考。",
    "webUrl": "https://developer.huawei.com/consumer/cn/doc/harmonyos-samples/samples-0000001162414961"
  },
  {
    "id": 6,
    "imageSrc": "app.media.enablement_pic6",
    "title": "API参考",
    "brief": "面向开发者提供鸿蒙系统开放接口的全集,供开发者了解具体接口使用方法。API参考详细地描述了每个接口的功能、使用限制、参数名、参数类型、参数含义、取值范围、权限、注意事项、错误码及返回值等。",
    "webUrl": "https://developer.huawei.com/consumer/cn/doc/harmonyos-references/development-intro-0000001580026066"
  },
  {
    "id": 7,
    "imageSrc": "app.media.enablement_pic7",
    "title": "FAQ",
    "brief": "开发者常见问题的总结,开发者可以通过FAQ更高效地解决常见问题。FAQ会持续刷新,及时呈现最新的常见问题。",
    "webUrl": "https://developer.huawei.com/consumer/cn/doc/harmonyos-faqs/faqs-development-0000001753952202"
  },
  {
    "id": 8,
    "imageSrc": "app.media.enablement_pic8",
    "title": "开发者论坛",
    "brief": "和其他应用开发者交流技术、共同进步。",
    "webUrl": "https://developer.huawei.com/consumer/cn/forum/home?all=1"
  }
]
//TutorialData.json
[
  {
    "id": 1,
    "imageSrc": "app.media.tutorial_pic1",
    "title": "Step1 开发入门:Hello World",
    "brief": "本篇教程实现了快速入门——一个用于了解和学习HarmonyOS的应用程序 。",
    "webUrl": "https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_Next-HelloWorld"
  },
  {
    "id": 2,
    "imageSrc": "app.media.tutorial_pic2",
    "title": "Step2 使用Swiper构建运营位",
    "brief": "Swiper组件提供滑动轮播显示的能力。Swiper本身是一个容器组件,当设置了多个子组件后,可以对这些子组件进行轮播显示。",
    "webUrl": "https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_Next-SwiperBanner"
  },
  {
    "id": 3,
    "imageSrc": "app.media.tutorial_pic3",
    "title": "Step3 创建Item视图",
    "brief": "Item定义子组件相关特征。相关组件支持使用条件渲染、循环渲染、懒加载等方式生成子组件。",
    "webUrl": "https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_Next-BuildItem"
  },
  {
    "id": 4,
    "imageSrc": "app.media.tutorial_pic4",
    "title": "Step4 网格和列表组件的使用",
    "brief": "网格和列表组件中,当Item达到一定数量,内容超过屏幕大小时,可以自动提供滚动功能,适合用于呈现同类数据类型或数据类型集",
    "webUrl": "https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_Next-GridAndList"
  },
  {
    "id": 5,
    "imageSrc": "app.media.tutorial_pic5",
    "title": "Step5 应用架构设计基础——MVVM框架",
    "brief": "ArkUI采取MVVM = Model + View + ViewModel模式,将数据与视图绑定在一起,数据更新时候会直接更新视图。",
    "webUrl": "https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_Next-BasicArchitectureDesignPart1"
  },
  {
    "id": 6,
    "imageSrc": "app.media.tutorial_pic6",
    "title": "Step6 应用架构设计基础——三层架构",
    "brief": "ArkWeb(方舟Web)提供了Web组件,用于在应用程序中显示Web页面内容,为开发者提供页面加载、页面交互、页面调试等能力。",
    "webUrl": "https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_Next-BasicArchitectureDesignPart2"
  },
  {
    "id": 7,
    "imageSrc": "app.media.tutorial_pic7",
    "title": "Step7 ArkWeb页面适配",
    "brief": "基于Web组件实现了快速入门案例中的课程学习界面,帮助开发者了解如何加载网络界面、本地界面以及如何进行网络权限的配置。",
    "webUrl": "https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_Next-ArkwebPageAdaptation"
  },
  {
    "id": 8,
    "imageSrc": "app.media.tutorial_pic8",
    "title": "Step8 通过结构数据构建页面",
    "brief": "在该教程中会根据对知识地图界面进行界面实现的分析以及相应的数据结构的设计,然后逐步实现知识地图中的两个界面。通过该章节,开发者可以了解到数据如何驱动UI更新。",
    "webUrl": "https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_Next-DataDrivenUIUpdates"
  },
  {
    "id": 9,
    "imageSrc": "app.media.tutorial_pic9",
    "title": "Step9 设置组件导航",
    "brief": "该教程会在“数据驱动UI更新”教程完成了知识地图相关界面开发的基础上,对代码进行修改,实现从知识地图页到知识地图详情页的组件路由导航,同时该教程中会讲解到Tabs的开发。",
    "webUrl": "https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_Next-SettingUpComponentNavigation"
  },
  {
    "id": 10,
    "imageSrc": "app.media.tutorial_pic10",
    "title": "Step10 原生智能:AI语音朗读",
    "brief": "为您的文章详情页添加文本转语音服务,朗读文章简介,手机在无网状态下系统应用无障碍(屏幕朗读)接入文本转语音能力,提供语音播报。",
    "webUrl": "https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_Next-TTS"
  },
  {
    "id": 11,
    "imageSrc": "app.media.tutorial_pic11",
    "title": "Step11 一次开发,多端部署",
    "brief": "通过对快速入门整体页面的分析,得出关键的界面适配点以及需要适配的关键组件,然后通过对关键适配点的逐步适配,完成整个应用的一多开发。",
    "webUrl": "https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_Next-DevelopOnceDeployAnywhere"
  },
  {
    "id": 12,
    "imageSrc": "app.media.tutorial_pic12",
    "title": "Step12 原生互联:分布式流转",
    "brief": "基于分布式流转相关能力,使得整个应用可以在不同设备间进行无缝的流转。该章节内容会涉及到流转的相关前提,如何进行流转的权限配置以及实现应用可流转。",
    "webUrl": "https://developer.huawei.com/consumer/cn/codelabsPortal/carddetails/tutorials_Next-DistributedFlow"
  }
]

End  

预览器不支持,仅支持真机或模拟器

因为预览器并不支持获取rawfile目录下的文件,所以无法成功获取到保存在rawfile目录下的json文件里的内容。请用真机/模拟器测试,预览器仅支持简单页面的预览。 

MVVM 模式具有以下优点:

  1. 低耦合性:视图、模型和视图模型之间的分离降低了它们之间的耦合程度。这使得各部分可以独立开发、测试和维护,提高了代码的可复用性和可扩展性。

  2. 双向数据绑定:视图和视图模型之间的数据可以自动同步,减少了大量手动的数据更新操作。当模型中的数据发生变化时,视图会自动更新;反之,当用户在视图中进行操作时,相关数据也能自动同步到模型中。

  3. 更好的可测试性:由于 MVVM 模式将业务逻辑从视图中分离出来,使得对模型和视图模型的单元测试变得更加容易和高效。

  4. 提高开发效率:开发人员可以专注于各自负责的部分,即视图开发者专注于界面设计,后端开发者专注于数据处理和业务逻辑,提高了开发效率。

  5. 清晰的架构:明确的分层结构使得代码组织更加清晰,易于理解和维护,降低了项目的复杂度。

  6. 适应变化:当需求发生变更时,比如界面的调整或业务逻辑的修改,只需在对应的层进行修改,减少了对整个系统的影响范围。

  7. 促进团队协作:清晰的职责划分有助于不同角色的开发人员在项目中更好地协作,提高团队的整体效率。


三层架构

前面我们介绍了MVVM的目录组织方式,一般适用于单个模块内文件组织,为了更好地适配复杂应用的开发,建议采用三层架构的方式对整个应用的功能进行模块化,实现高内聚、低耦合开发。

一次开发,多端部署”推荐在应用开发过程中使用如下的“三层工程结构”

DevEco Studio创建出的默认工程仅包含一个的entry类型的模块,如果直接使用如下所示的平级目录进行模块管理,工程逻辑结构较混乱且模块间的依赖关系不够清晰,不利于多人开发和维护。

HarmonyOS应用的分层架构主要包括三个层次:产品定制层、基础特性层和公共能力层,为开发者构建了一个清晰、高效、可扩展的设计架构。

三层工程结构如下:

  • commons(公共能力层):用于存放公共基础能力集合(如工具库、公共配置等)。commons层可编译成一个或多个HAR包或HSP包,只可以被products和features依赖,不可以反向依赖。common:作为项目底层支撑,存放项目工具类、公共组件,网络请求底层封装、常量字符串等等。可以建har包或者hsp包,推荐hsp包。

  • features(基础特性层):用于存放基础特性集合(如应用中相对独立的各个功能的UI及业务逻辑实现等)。各个feature高内聚、低耦合、可定制,供产品灵活部署。不需要单独部署的feature通常编译为HAR包或HSP包,供products或其它feature使用。需要单独部署的feature通常编译为Feature类型的HAP包,和products下Entry类型的HAP包进行组合部署。features层可以横向调用及依赖common层,同时可以被products层不同设备形态的HAP所依赖,但是不能反向依赖products层。features:基础业务,根据业务功能创建,比如:项目的功能模块,新车模块/我的模块/消息模块/二手车模块,推荐hsp包。

  • products(产品定制层):用于针对不同设备形态进行功能和特性集成。products层各个子目录各自编译为一个Entry类型的HAP包,作为应用主入口。products层不可以横向调用。product:应用的入口,满足同设备的适配,例如:phone模块、watch模块,建hap包

拓展选择合适的包类型

HAP、HAR、HSP三者的功能和使用场景总结对比如下:

Module类型包类型说明
AbilityHAP应用的功能模块,可以独立安装和运行,必须包含一个entry类型的HAP,可选包含一个或多个feature类型的HAP。
Static LibraryHAR

静态共享包,编译态复用。

- 支持应用内共享,也可以发布后供其他应用使用。

- 作为二方库,发布到OHPM私仓,供公司内部其他应用使用。

- 作为三方库,发布到OHPM中心仓,供其他应用使用。

- 多包(HAP/HSP)引用相同的HAR时,会造成多包间代码和资源的重复拷贝,从而导致应用包膨大。

- 注意:编译HAR时,建议开启混淆能力,保护代码资产。

Shared LibraryHSP

动态共享包,运行时复用。

- 当前仅支持应用内共享。

- 当多包(HAP/HSP)同时引用同一个共享包时,采用HSP替代HAR,可以避免HAR造成的多包间代码和资源的重复拷贝,从而减小应用包大小。

HAP、HSP、HAR支持的规格对比如下,其中“√”表示是,“×”表示否。

开发者可以根据实际场景所需的能力,选择相应类型的包进行开发。在后续的章节中还会针对如何使用HAPHARHSP分别展开详细介绍。

规格HAPHARHSP
支持在配置文件中声明UIAbility组件与ExtensionAbility组件××
支持在配置文件中声明pages页面×
支持包含资源文件与.so文件
支持依赖其他HAR文件
支持依赖其他HSP文件
支持在设备上独立安装运行××

说明

  • HAR虽然不支持在配置文件中声明pages页面,但是可以包含pages页面,并通过命名路由的方式进行跳转。
  • 由于HSP仅支持应用内共享,如果HAR依赖了HSP,则该HAR文件仅支持应用内共享,不支持发布到二方仓或三方仓供其他应用使用,否则会导致编译失败。
  • HAR和HSP均不支持循环依赖,也不支持依赖传递。

核心

  1. hap 可以作为入口,放到 Product
  2. hsp 多个地方应用不会增加容量消耗
  3. har 可以单独发布,如果要考虑单独发布的模块,可以用 har
  4. 三层架构:hap+ n hsp

 工程目录迁移

为了实现三层架构,需要完成从单模块工程到多模块工程的拆分和目录调整。

之前的代码结构如下,我们所有的业务都在一个entry模块中,此时再新增功能,就需要修改或者新增这个模块内容。例如要开发map和learning对应的功能时,就需要修改这个entry模块的内容,为了让模块更加内聚减少耦合,所以需要对工程进行目录结构拆分,实现模块化。

接下来开始介绍如何进行工程的目录迁移,如想了解模块的创建和使用可以参考开发静态共享包har,如何使用har包可以参考har包的使用。 

Step 1

首先在工程目录Project下,创建三个文件夹commons、features、products。

需要注意这里创建选择的是Directory,而不是选择Module

Step 2

接下来我们创建对应的模块,从页面功能出发,可以发现由三个功能部分组成,需要实现首页、课程学习、知识地图这三个功能页面,从功能上拆分可以分出quickstart、map、learning三个feature模块,用来承载这三个不同的业务功能。

创建features层模块

在features目录下创建三个模块,分别是quickstart、map、learning

右击features文件夹,选择创建Module,进入页面如图

创建时,选择Shared Library (HSP)

分别创建quickstart、map、learning,结果如右图

创建commons层模块

在commons文件夹中,创建一个模块basic,这个模块存放一些公用的工具或者ui组件,在需要用到时使用

这里也可以把utils和components(公共组件) 都建一个模块,看公司需求把

 

将entry模块放入products层

products层主要根据产品定制层内结构,这里我们需要将工程名改成phone,通过右键选择refactor-> rename,选择Rename module

修改完后,将其拖入products文件夹中,结果如右图

Step 3

原来entry模块中的功能,都只是quickstart模块对应的功能,需要将entry对应的文件复制到quickstart模块中

将phone模块中model、util、view移入quickstart模块的ets目录中

将phone模块中rawfile移入quickstart模块的resources资源目录中

在每个包创建了一个index文件  (统一资源管理目录)

//model
export * from './ArticleClass'

export * from './BannerClass'
//utils
export * from './BufferUtil'
//view
export * from "./Banner"

export * from "./EnablementView"

export * from "./TutorialView"

 最后向外暴露这个包

Step 5

接下来需要让内容能够让应用正常运行,还需要进行具体代码的调整

在phone模块的oh-package.json5文件中,写入以下依赖内容,这里需要注意不是工程级别的oh-package.json,需要先找到phone模块

在oh-package.json5文件中,dependencies中写入对quickstart模块的依赖,因为后续还会使用map和learning模块的内容,这里一并配置好依赖,方便后续使用。

{
  "name": "phone",
  "version": "1.0.0",
  "description": "Please describe the basic information.",
  "main": "",
  "author": "",
  "license": "",
  "dependencies": {
    "quickstart": "file:../../features/quickstart",
    "learning": "file:../../features/learning",
    "map": "file:../../features/map"
  }
}

End

直接启动报错

解决bug 

 

三层工程架构的好处:

一、模块性与可维护性

  • 功能划分清晰

    • commons 层:将公共基础能力集中放置,如工具库和公共配置。开发人员在维护和更新这些公共部分时,能够快速定位相关代码。例如,当需要修改网络请求的底层封装逻辑或者更新常量字符串时,只需要在 commons 层的特定位置进行操作,而不会影响到其他层的功能实现。
    • features 层:把各个相对独立的功能模块的 UI 和业务逻辑分开存放,使得每个功能(如新车模块、我的模块等)像一个独立的小单元。这种高内聚的设计使得每个功能的维护和更新都可以独立进行,降低了代码的复杂性。如果要对消息模块进行功能升级或者修复 Bug,只需要关注该模块对应的代码部分。
    • products 层:针对不同设备形态进行定制,使得每个设备入口(如 phone 模块、watch 模块)的功能集成清晰明了。当需要对特定设备的应用入口进行适配或功能调整时,开发人员可以在 products 层相应的子目录中进行操作。
  • 便于团队协作

    • 在大型项目开发中,不同的团队或开发人员可以专注于不同的层次。例如,一个团队负责 commons 层的公共工具开发和维护,另一个团队专注于 features 层的各个功能模块开发,还有一个团队处理 products 层的设备适配和主入口集成。这样的分工明确,能够提高开发效率,减少不同开发人员之间的代码冲突。

二、复用性

  • commons 层复用

    • 作为公共基础能力层,commons 层的工具库和公共配置可以被 features 层和 products 层复用。例如,网络请求封装在 commons 层完成后,无论是 features 层中的新车模块还是 products 层的 phone 模块入口,都可以直接使用这个网络请求功能,避免了重复开发相同的功能代码。这种复用性不仅提高了开发效率,还保证了代码的一致性。
  • features 层复用

    • features 层的各个功能模块如果编译为 HAR 包或 HSP 包,可以供其他功能模块或者产品入口使用。比如,二手车模块中经过良好测试的车辆详情展示组件,如果以合适的包形式发布,新车模块也可以复用这个组件,减少了开发成本并且能够保证功能的稳定性和一致性。

三、灵活性与可定制性

  • features 层灵活组合

    • features 层的各个功能模块具有低耦合的特性,使得产品可以根据实际需求灵活部署这些功能。例如,对于一个汽车交易应用,在某些特定的市场版本中,可以选择只包含新车模块和我的模块,而在其他版本中可以添加二手车模块和消息模块。这种灵活性使得产品能够快速适应不同的业务场景和用户需求。
  • products 层定制化入口

    • products 层针对不同设备形态进行功能和特性集成,能够为不同设备(如手机、手表)提供定制化的应用入口。例如,在手表设备上,可以只集成最核心的功能模块,并且对 UI 进行适合手表屏幕尺寸的适配;而在手机设备上,可以提供完整的功能集合和更丰富的 UI 展示。这种定制化能够提高用户体验,并且使得应用在不同设备上都能发挥最佳性能。

四、层次依赖的合理性

  • 单向依赖保证稳定性
    • 这种三层架构规定了明确的依赖方向,commons 层只被 features 和 products 层依赖,features 层可以依赖 commons 层并且被 products 层依赖,products 层不能反向依赖其他层。这种单向依赖关系使得代码的结构更加稳定,避免了循环依赖可能导致的各种问题。例如,如果允许 products 层反向依赖 features 层或者 commons 层,当对下层代码进行修改时,可能会引起上层代码的连锁反应,导致整个应用的稳定性受到影响。

 

标签:架构设计,https,DevEco,--,brief,huawei,imageSrc,模块,id
From: https://blog.csdn.net/hqy1989/article/details/144008578

相关文章

  • OpenCV-Python 特征匹配
    37.1Brute-Force匹配的基础蛮力匹配器是很简单的。首先在第一幅图像中选取一个关键点然后依次与第二幅图像的每个关键点进行(描述符)距离测试,最后返回距离最近的关键点。对于BF匹配器,我们首先要使用cv2.BFMatcher()创建一个BFMatcher对象。它有两个可选参数。第......
  • 程序员都看对眼的开发平台
    让业务人员来评价低代码平台好不好用,参考性并不大。毕竟让无编程经验的人来评价一款开发产品,问他开发效率高不高,也问不出来个所以。但如果连专业的程序员都赞不绝口的低代码平台,那其参考性可就不一般了。就在上周,一家做多端的软件公司老板找到我,他听说我们还和那几大运营商有......
  • 一句我不会用啊,气晕多少人
    听说不少公司的员工对新系统那叫一个抗拒,原因竟然是“学会了”就都是他一个人的活,我也是哭笑不得通过这个现象,我也明白一个道理:很多公司的系统看来还是不够清晰明了,让人一看就会。所以啊,下次再听到有人抱怨“学会用了就得干一辈子”,开发团队们是不是该觉得脸上火辣辣的?毕竟,系......
  • AI编写标书!
    AI助攻“标哥”们:标书编写的新神器    话说,每个售前部门里,总有那么一群苦逼的“标哥”,整天穿梭于“加班地狱”和“熬夜深渊”之间,连周末都像小龙虾一样被剥得干干净净。尤其是遇到项目竞标时,那更是神兵天降的时刻——标书一出,江湖哀鸣!其中最让人崩溃的,非技术应答部分......
  • SPRINGMVC WEBUPLOADER 分片上传
    前端:vue2,vue3,vue-cli,webuploader,html5后端:SpringBoot数据库:MySQL,Oracle,SQLServer,达梦,人大金仓,国产化数据库协议:HTTPWebServer:Tomcat,Resin服务器:Linux,国产化系统功能:大文件上传,断点续传,秒传,加密传输,加密存储,文件夹上传,文件夹层级结构技术:支持第三方软件集......
  • 从零开始学习黑客技术,看这一篇就够了
    ......
  • 从零开始学习黑客技术,看这一篇就够了
    ......
  • 你有没有自己写过一套UI库?说下遇到哪些难点?
    如果我要设计一个UI库,我预见会遇到以下难点:跨浏览器兼容性:这是一个前端开发永恒的难题。不同的浏览器对CSS和JavaScript的解释和支持程度不同,导致相同的代码在不同的浏览器中呈现效果可能不一致。需要大量的测试和调整以确保UI库在各种主流浏览器(Chrome,Firefox,Safari,Ed......
  • 你有签过保密协议吗?说说你对保密协议的理解
    我理解保密协议(NDA-Non-DisclosureAgreement),特别是在前端开发领域的含义,通常指保护以下几种信息:公司代码和专有技术:这包括源代码、算法、架构设计、开发流程、内部工具和库等。前端开发中,这可能涉及到公司自研的UI组件库、构建工具配置、或创新的交互逻辑。泄露这些信息......
  • 说说你对富文本的理解,你有用过哪些富文本编辑器呢?
    我对富文本的理解是,它是一种比纯文本更丰富的文本格式,允许包含格式化元素,例如字体样式(粗体、斜体、下划线)、字体大小、颜色、列表(有序和无序)、表格、图像、链接、视频、甚至是代码块等等。这与纯文本形成对比,纯文本只包含字符、空格和换行符,没有任何格式化信息。从前端开发的角......