鸿蒙开发(HarmonyOS)组件化浅谈

众所周知,现在组件化在移动开发中是很常见的,那么组件化有哪些好处:

1. 提高代码复用性:组件化允许将应用程序的不同功能模块化,使得这些模块可以在不同的项目中重复使用,从而提高开发效率并减少重复工作。

2. 降低组件间的耦合:通过组件化的规则将代码拆分成不同的模块,实现高内聚、低耦合,使得代码更易于维护,降低了模块间的依赖,减少了潜在的错误和问题。

3. 提升开发效率:组件化使得开发团队可以并行工作,每个团队可以专注于自己的组件,独立开发和维护,这样可以加快开发进度,提高整体的开发效率。

4. 改善代码质量:组件化鼓励开发者编写清晰、模块化的代码,有助于提高代码的可读性和可维护性,从而提升代码质量。

5. 便于扩展和迭代:组件化架构使得添加新功能或改进现有功能变得更加容易,有助于快速响应市场变化和用户需求。

6. 隔离技术栈:不同的组件可以使用不同的技术栈,而不会相互影响,使得技术选型更加灵活。

7. 独立开发/维护/发布:组件化允许每个组件独立开发、维护和发布,使得更新和迭代更加灵活。

8. 提高编译/构建速度:组件化使得编译和构建过程更加高效,因为只需要编译和构建相关的组件,而不是整个项目。

9. 管控代码权限:组件化允许更好地控制代码权限,通过将代码分散到不同的仓库中,可以限制对特定组件的访问和修改。

10. 管理版本变更:组件化使得管理版本变更变得更加容易,因为每个组件都有明确的版本,可以更容易地跟踪和控制版本更新。

组件化是解决单一工程架构开发中问题的有效方法,它通过将大型项目拆分成更小、更易于管理的模块,提高了开发效率和代码质量。然而,组件化也带来了一些挑战,如组件粒度的划分、组件间依赖关系的管理以及跨技术栈通信等。为了实现高质量的组件化项目,需要遵循一些实践规范和原则,如组件拆分原则、组件间依赖管理以及质量保障措施。
 

那么我们在进行鸿蒙开发时如何进行组件化开发呢,接下来我将带大家了解鸿蒙开发中的组件化,项目的目录结构如下

其中features目录下是组件/模块,包含不同的功能分区,entity是项目的主入口也就是hap包,commons目录下有3个har组件,分别是utils:所有的帮助类、uicomponents:项目中需要用到的自定义UI组件等、RouterModule:项目的路由(承载了整个项目跨组件通信的能力)

接下来我们重点说一下RouterModule

不同组件之间想要通信,需要建立路由联系,RouterModule模块的实现主要包含以下步骤:

1. 定义路由表和路由栈

export class RouterModule {// WrappedBuilder支持@Builder描述的组件以参数的形式进行封装存储static builderMap: Map<string, WrappedBuilder<[object]>> = new Map<string, WrappedBuilder<[object]>>();// 初始化路由栈,需要关联Navigation组件static navPathStack: NavPathStack = new NavPathStack();
}

2. 路由表增加路由注册和路由获取方法,业务har模块通过路由注册方法将需要路由的页面组件委托给RouterModule管理,增加路由跳转方法,业务har模块通过调用该方法并指定跳转信息实现模块间路由跳转,完整代码如下

/*** @FileName : RouterModule* @Author : kirk.wang* @Time : 2024/7/10 10:44* @Description : 路由管理*/
import { RouterModel } from '../model/RouterModel';
import Logger from './Logger';export class RouterModule {static builderMap: Map<string, WrappedBuilder<[object]>> = new Map<string, WrappedBuilder<[object]>>();static routerMap: Map<string, NavPathStack> = new Map<string, NavPathStack>();// Registering a builder by name.public static registerBuilder(builderName: string, builder: WrappedBuilder<[object]>): void {RouterModule.builderMap.set(builderName, builder);}// Get builder by name.public static getBuilder(builderName: string): WrappedBuilder<[object]> {const builder = RouterModule.builderMap.get(builderName);if (!builder) {Logger.info('not found builder ' + builderName);}return builder as WrappedBuilder<[object]>;}// Registering a router by name.public static createRouter(routerName: string, router: NavPathStack): void {RouterModule.routerMap.set(routerName, router);}// Get router by name.public static getRouter(routerName: string): NavPathStack {return RouterModule.routerMap.get(routerName) as NavPathStack;}// Jumping to a Specified Page by Obtaining the Page Stack.public static async push(router: RouterModel): Promise<void> {const harName = router.builderName.split('_')[0];// Dynamically import the page to be redirected to.await import(harName).then((ns: ESObject): Promise<void> => ns.harInit(router.builderName));RouterModule.getRouter(router.routerName).pushPath({ name: router.builderName, param: router.param });}// Obtain the page stack and pop it.public static pop(routerName: string): void {// Find the corresponding route stack for pop.RouterModule.getRouter(routerName).pop();}// Get the page stack and clear it.public static clear(routerName: string): void {// Find the corresponding route stack for pop.RouterModule.getRouter(routerName).clear();}// Directly jump to the specified route.public static popToName(routerName: string, builderName: string): void {RouterModule.getRouter(routerName).popToName(builderName);}
}

3.RouterModel为了方便在页面跳转直接进行传值,完整代码如下:

/*** @FileName : RouterModel* @Author : kirk.wang* @Time : 2024/9/13 14:33* @Description : 路由信息类,便于跳转时传递更多信息*/
import { RouterModule } from '../utils/RouterModule';export class RouterModel {// 路由页面别名builderName: string = "";routerName: string = "";// 需要传入页面的参数param?: string = "";
}// 创建路由信息,并放到路由栈表中
export function buildRouterModel(routerName: string, builderName: string, param?: string) {let router: RouterModel = new RouterModel();router.builderName = builderName;router.routerName = routerName;router.param = param;RouterModule.push(router);
}

页面跳转实现

路由管理模块RouterModule实现之后,需要使用RouterModule模块实现业务模块harA的页面跳转到业务模块harB的页面功能。主要步骤如下:

  1. 在工程主入口模块Entry.hap中引入RouterModule模块和所有需要进行路由注册的业务har模块。
    // Entry.hap中的oh-package.json5文件"dependencies": {"@ohos/home": "file:../features/home","@ohos/report": "file:../features/report","@ohos/shopcart": "file:../features/shopcart","@ohos/mine": "file:../features/mine","@ohos/utils": "file:../commons/utils","@ohos/routermodule": "file:../commons/RouterModule"}

  2.  在工程主入口模块的首页Navigation组件上关联RouterModule模块的路由栈和路由表。

    import HomeTabs from './HomeTabs'
    import { BuilderNameConstants, buildRouterModel, RouterModule, RouterNameConstants } from '@ohos/routermodule';@Preview
    @Entry
    @Component
    struct Index {@State homeTab: number = 0@State entryHapRouter: NavPathStack = new NavPathStack();aboutToAppear() {if (!this.entryHapRouter) {this.entryHapRouter = new NavPathStack();}RouterModule.createRouter(RouterNameConstants.ENTRY_HAP, this.entryHapRouter)};@BuilderrouterMap(builderName: string, param: object) {// 从RouterModule中获取全局路由表RouterModule.getBuilder(builderName).builder(param);}build() {// 绑定RouterModule中路由栈Navigation(this.entryHapRouter) {Column() {HomeTabs({ currentIndex: this.homeTab })}.backgroundColor('#f1f3f5').width('100%').height('100%')}.navDestination(this.routerMap); // 从RouterModule中获取全局路由表}
    }
  3. 在har中声明需要跳转的页面,并且调用registerBuilder接口将页面注册到RouterModule模块的全局路由表上。以下注册逻辑会在harB的B1页面被首次加载时触发

    // harhome模块的登录页面
    import { CommonConstants } from '@ohos/utils';
    import router from '@ohos.router';
    import { BuilderNameConstants, buildRouterModel, RouterModule, RouterNameConstants, } from '@ohos/routermodule';@Builder
    export function harBuilder(value: object) {NavDestination() {Column() {///...}.backgroundColor($r('app.color.page_background'))}.width('100%').height('100%')}.title('登录').onBackPressed(() => {RouterModule.pop(RouterNameConstants.ENTRY_HAP);return true;})
    }const builderName = BuilderNameConstants.MINE_LOGIN;
    if (!RouterModule.getBuilder(builderName)) {let builder: WrappedBuilder<[object]> = wrapBuilder(harBuilder);RouterModule.registerBuilder(builderName, builder);
    }
    
  4. 在harHome模块中的页面调用RouterModule模块的push方法实现跳转到harMine的Login页面。当harMine的Login页面被首次通过push方法跳转时,会动态加载Login页面,并且触发步骤3中Login页面的路由注册逻辑,把Login页面注册到RouterModule的全局路由表builderMap中。

    import { BuilderNameConstants, buildRouterModel, RouterModule, RouterNameConstants, } from '@ohos/routermodule';@Entry
    @Component
    export struct HomePage {build() {Column() {Button('go loagin', { stateEffect: true, type: ButtonType.Capsule }).width('80%').height(40).margin(20).onClick(() => {buildRouterModel(RouterNameConstants.ENTRY_HAP, BuilderNameConstants.MINE_LOGIN);})}.width('100%').height('100%').backgroundColor('#F1F3F5')}
    }

上述方案,当在entry模块页面上点击跳转到harA模块的页面时序图如下:

具体实现

在harMine的对外导出类Index.ets中定义加载时的初始化函数harInit,该函数对harMine中需要注册路由的页面组件进行加载管理,被调用时将根据不同的路径动态加载不同的页面。as

import { BuilderNameConstants } from '@ohos/routermodule';export { MinePage } from './src/main/ets/components/mainpage/MinePage'export function harInit(builderName: string): void {// 动态引入要跳转的页面switch (builderName) {case BuilderNameConstants.MINE_ORDERLIST:import("./src/main/ets/components/mainpage/OrderListPage");break;case BuilderNameConstants.MINE_LOGIN:import("./src/main/ets/components/mainpage/LoginPage");break;case BuilderNameConstants.MINE_TEST:import("./src/main/ets/components/mainpage/test");break;default:break;}
}

最后在Hap工程的build-profile.jsn5中添加所有的动态库

  "buildOption": {"arkOptions": {"runtimeOnly": {"sources": [],"packages": ["@ohos/home","@ohos/report","@ohos/shopcart","@ohos/mine","@ohos/utils","@ohos/routermodule"]}}},

接下来在点击主页的登录按钮,就能实现从主页跳转到登录页,返回后依然能返回到前面的页面。更多功能同学们可以继续探索,官方文档地址:文档中心

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1540886.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL-DDL/DML(数据定义/操作语言)

数据定义语言(DDL-Data Definition Language) 代表关键字:create ,drop,alter 数据操作语言(DML-Data Manipulation Language) 代表关键字&#xff1a;insert,delete,update 1、表的操作 1.1 创建表 create table 表名(字段1,字段2,字段3.....字段n) [charset字符集][type表类…

使用蒙特卡洛模拟和并查集求解渗透阈值

代码由C语言编写 我使用了三个类&#xff0c;分别如下 第一个类&#xff0c;实现了并查集的基本操作。 #pragma once#include<iostream> #include<vector> using namespace std;class UnionFind { public:vector<int> parent; // 用于存储父节点vector<…

生物信息常用编辑器:轻量高效的VS Code

在生物信息学中&#xff0c;编写和调试代码是日常工作的一部分&#xff0c;选择一个合适的编辑器能极大提升效率。Visual Studio Code&#xff08;简称VS Code&#xff09;是一款轻量、灵活且功能强大的代码编辑器&#xff0c;广受开发者欢迎。本文将为大家介绍VS Code的主要功…

50.面向对象进阶训练-学生类

//定义一个长度为3的数组&#xff0c;存储1-3名学生对象作为初始数据 //学生属性&#xff1a;学号 姓名 年龄&#xff0c;其中学号姓名各不相同 //要求&#xff1a;1.再次添加一个学生对象&#xff0c;并在添加的时候进行学号的唯一性判断//2.添加完毕之后&#xff0c;遍历所有…

企业急于采用人工智能,忽视了安全强化

对主要云提供商基础设施上托管的资产的安全分析显示&#xff0c;许多公司为了急于构建和部署 AI 应用程序而打开安全漏洞。常见的发现包括对 AI 相关服务使用默认且可能不安全的设置、部署易受攻击的 AI 软件包以及不遵循安全强化指南。 这项分析由 Orca Security 的研究人员进…

重回极简:华为如何走向全面智能化?

“人类发现地球只是宇宙一员的时候&#xff0c;也是我们距离群星最遥远的时候。” 这个来自天文领域的喟叹&#xff0c;今天同样出现在行业与企业的智能化之路上。在这个时代坐标上&#xff0c;AI大模型技术极速成熟&#xff0c;AIGC和AI Agent等应用受到了各个行业的巨大期待。…

SpringBoot 数据库表结构文档生成

官方地址&#xff1a;https://github.com/pingfangushi/screw screw 螺丝钉&#xff0c;支持以下数据库 MySQL MariaDB TIDB Oracle SqlServer PostgreSQL Cache DB&#xff08;2016&#xff09; 生产文档支持 html word markdown 开始 添加依赖 <!-- 螺丝钉 --><…

PyCharm部分快捷键冲突问题

1.问题起因 今天在用PyCharm&#xff0c;编写python程序的时候&#xff0c;发现快捷间冲突&#xff0c;随后在CSDN上查找了一些资料&#xff0c;博主第一个说是搜狗输入法冲突&#xff0c;经过其内容尝试之后发现并不是这样啊&#xff0c;然后我有进行了一些资料案例的查询&am…

【第十三章:Sentosa_DSML社区版-机器学习聚类】

目录 【第十三章&#xff1a;Sentosa_DSML社区版-机器学习聚类】 13.1 KMeans聚类 13.2 二分KMeans聚类 13.3 高斯混合聚类 13.4 模糊C均值聚类 13.5 Canopy聚类 13.6 Canopy-KMeans聚类 13.7 文档主题生成模型聚类 13.8 谱聚类 【第十三章&#xff1a;Sentosa_DSML社…

VUE3配置路由(超级详细)

第一步创建vue3的项目

低代码可视化工具--vue条件判断v-if可视化设置-代码生成器

在Vue UniApp中&#xff0c;条件判断通常是通过指令v-if、v-else-if、v-else来实现的。这些机制允许你根据表达式的真假值来决定是否渲染某个元素或元素组&#xff0c;或者执行特定的逻辑。 条件判断说明 v-if 是惰性的&#xff1a;如果在初始渲染时条件为假&#xff0c;则什么…

Obsidian 全部笔记共享配置文件,obsidian仓库-文件夹配置统一化

obsidian仓库-文件夹配置统一化 在每次新建obsidian仓库(vaults)时&#xff0c;仓库的主题和快捷键等都需要重新设置&#xff0c;这是因为每次创建新的仓库时 新仓库的配置文件都是默认配置但是如果通过复制粘贴旧配置文件来达到新仓库的配置和旧仓库一致的话&#xff0c;无法…

Shiro-721—漏洞分析(CVE-2019-12422)

文章目录 Padding Oracle Attack 原理PKCS5填充怎么爆破攻击 漏洞原理源码分析漏洞复现 本文基于shiro550漏洞基础上分析&#xff0c;建议先看上期内容&#xff1a; https://blog.csdn.net/weixin_60521036/article/details/142373353 Padding Oracle Attack 原理 网上看了很多…

cmake--get_filename_component

作用 按照指定的方式获取文件或者目录的信息。 使用 get_filename_component(<variable> <filename> <component>) variable: 用于保存提取的信息。 filename: 指定路径的文件或者目录。 component: 链接1 component DIRECTORY: 提取文件或者目录的父…

Qwen 个人笔记

Qwen 个人笔记 Qwen的整体架构与Llama2类似&#xff0c;如下图所示: 1 Qwen2Config 1.1 Model 1.1.1 初始化 设置了模型的两个属性:padding_idx&#xff08;用于指定填充标记的索引&#xff09;&#xff0c;vocab_size&#xff08;词汇表的大小&#xff09;初始化了模型的…

关于“华为杯”第二十一届中国研究生数学建模竞赛赛题下载及提交作品的重要提醒

↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 各参赛队伍&#xff1a; “华为杯”第二十一届中国研究生数学建模竞赛即将于2024年…

java重点学习-设计模式

十三 设计模式 工厂模式&#xff1a;spring中使用&#xff08;目的是&#xff1a;解耦&#xff09; 1.简单工厂 所有的产品都共有一个工厂&#xff0c;如果新增产品&#xff0c;则需要修改代码&#xff0c;违反开闭原则是一种编程习惯&#xff0c;可以借鉴这种编程思路 2.工厂方…

Transformer模型-5-Multi-Head Attention

上图红色圈中的部分为 Multi-Head Attention&#xff0c;是由多个Self-Attention组成的&#xff0c;虽然Encoder与Decoder中都有Multi-Head Attention&#xff0c;但他们略有区别。Encoder block包含一个 Multi-Head Attention&#xff0c; 而Decoder block包含两个 Multi-Head…

jenkins声明式流水线语法详解

最基本的语法包含 pipeline&#xff1a;所有有效的声明式流水线必须包含在一个 pipeline 块中stages&#xff1a;包含一系列一个或多个stage指令stage&#xff1a;stage包含在stages中进行&#xff0c;比如某个阶段steps&#xff1a;在阶段中具体得执行操作&#xff0c;一个或…

gcc配合cython编译python源代码

以前我们一般用Nuitka或者Pyinstaller来将python源码编译成二进制可执行文件。今天我们学习如何直接用gcc来编译。 很简单的一个python程序&#xff0c;结构如下。包含一个model.py和main.py 步骤1&#xff1a;处理main.py 处理main.py。即主程序入口 cython -D -2 --embe…