🧩 ArkTS模块系统全解析:组织代码的艺术 🧩

💫 干货分享! 模块化是现代应用开发的基石,掌握ArkTS的模块系统能让你的代码结构更清晰、更易维护。今天就带大家深入了解这个强大的特性~

📌 模块的基本概念:代码的组织单元

模块是程序的基本组织单元,每个模块都有自己的作用域。在模块中创建的声明(变量、函数、类等)在该模块之外默认是不可见的,除非它们被显式导出。这种机制有效地避免了命名冲突,并帮助我们组织大型应用程序。

📌 导出:分享你的代码

🔸 导出声明

使用export关键字可以导出顶层声明,使其在其他模块中可用:

// geometry.ts
export class Point {
  x: number = 0;
  y: number = 0;
  
  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
}

export let Origin = new Point(0, 0);

export function Distance(p1: Point, p2: Point): number {
  return Math.sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y));
}

🔸 默认导出

使用export default可以指定模块的默认导出,每个模块只能有一个默认导出:

// demo.ts
class Demo {
  constructor() {
    // 初始化逻辑
  }
  
  sayHello(): void {
    console.info("Hello from Demo!");
  }
}

export default new Demo(); // 导出Demo类的实例作为默认导出

📌 导入:使用其他模块的代码

🔸 静态导入

静态导入是在模块顶部声明的,在代码执行前就确定了依赖关系。有几种不同的导入方式:

1️⃣ 导入整个模块

// 导入整个模块,并使用命名空间访问其导出
import * as Utils from './utils';

Utils.X; // 访问utils模块导出的X
Utils.Y; // 访问utils模块导出的Y

2️⃣ 导入特定的导出

// 只导入需要的特定导出
import { X, Y } from './utils';

X; // 直接使用X
Y; // 直接使用Y

3️⃣ 导入并重命名

// 导入并重命名,避免命名冲突
import { X as Z, Y } from './utils';

Z; // 使用Z访问utils模块的X
Y; // 直接使用Y
// X; // 错误:X不可见

4️⃣ 导入默认导出

// 导入默认导出
import demo from './demo';

demo.sayHello(); // 输出: Hello from Demo!

🔸 动态导入

动态导入允许根据条件或按需加载模块,它返回一个Promise:

// Calc.ts
export function add(a: number, b: number): number {
  let c = a + b;
  console.info('Dynamic import, %d + %d = %d', a, b, c);
  return c;
}

// Index.ts
// 使用动态导入
import("./Calc").then((obj) => {
  console.info(obj.add(3, 5)); // 输出: Dynamic import, 3 + 5 = 8 然后输出 8
}).catch((err) => {
  console.error("Module dynamic import error: ", err);
});

在异步函数中,可以使用await简化动态导入:

// say.ts
export function hi() {
  console.info('Hello');
}

export function bye() {
  console.info('Bye');
}

// 在异步函数中使用动态导入
async function test() {
  let ns = await import('./say');
  ns.hi(); // 输出: Hello
  ns.bye(); // 输出: Bye
  
  // 也可以解构导入的模块
  let { hi, bye } = await import('./say');
  hi(); // 输出: Hello
  bye(); // 输出: Bye
}

test();

🔸 导入HarmonyOS SDK的开放能力

HarmonyOS SDK提供了丰富的开放能力,可以通过导入相应的模块来使用:

1️⃣ 直接导入接口模块

import UIAbility from '@ohos.app.ability.UIAbility';

2️⃣ 通过Kit导入单个模块

import { UIAbility } from '@kit.AbilityKit';

3️⃣ 通过Kit导入多个模块

import { UIAbility, Ability, Context } from '@kit.AbilityKit';

4️⃣ 导入Kit的所有模块

import * as abilityKit from '@kit.AbilityKit';

// 使用导入的模块
let ability = new abilityKit.UIAbility();

⚠️ 注意:导入Kit的所有模块可能会导入过多无需使用的模块,导致编译后的HAP包太大,占用过多资源,请谨慎使用。

📌 顶层语句:模块的直接执行代码

顶层语句是指在模块最外层编写的语句,不被任何函数、类或块级作用域包裹。这些语句在模块加载时会直接执行。

// logger.ts
console.info("Logger module loaded"); // 顶层语句,模块加载时执行

export function log(message: string): void {
  console.info(`[LOG]: ${message}`);
}

// 导入logger.ts时,会输出"Logger module loaded"

📌 关键字this:上下文引用

在ArkTS中,this关键字只能在类的实例方法中使用,它指向:

  • 调用实例方法的对象
  • 正在构造的对象(在构造函数中)
class Counter {
  count: number = 0;
  
  increment(): void {
    this.count++; // this指向Counter的实例
  }
  
  constructor(initialValue: number) {
    this.count = initialValue; // this指向正在构造的对象
  }
}

⚠️ 使用限制:

  • 不支持this类型
  • 不支持在函数和类的静态方法中使用this
class A {
  n: number = 0;
  
  f1(arg1: this) {} // 错误:不支持this类型
  
  static f2(arg1: number) {
    this.n = arg1;  // 错误:不支持在类的静态方法中使用this
  }
}

function foo(arg1: number) {
  this.n = arg1;    // 错误:不支持在函数中使用this
}

📌 注解:元数据的力量

注解(Annotation)是一种特殊的语言特性,它通过添加元数据来改变应用声明的语义。在ArkTS中,注解以@符号开头。

🔸 注解的声明与使用

// 声明注解
@interface ClassAuthor {
  authorName: string
}

// 使用注解
@ClassAuthor({authorName: "Bob"})
class MyClass {
  // 类的实现
}

🔸 注解字段的类型限制

注解字段仅限于以下类型:

  • number
  • boolean
  • string
  • 枚举
  • 以上类型的数组
@interface DocumentInfo {
  title: string;
  version: number = 1;
  isPublic: boolean = true;
  tags: string[] = [];
  status: DocumentStatus; // 枚举类型
}

enum DocumentStatus {
  DRAFT,
  REVIEW,
  PUBLISHED
}

🔸 注解的使用规则

  1. 注解必须放置在声明之前
  2. 注解名称必须以@为前缀,且@和名称之间不允许有空格
  3. 多个注解可以应用于同一个声明
  4. 当前仅允许对类声明和方法声明使用注解
@ClassPreamble({authorName: "John", revision: 2})
@MyAnno() // 多个注解应用于同一个类
class MyClass {
  @MethodAnno({data: 123})
  foo(): void {
    // 方法实现
  }
  
  @MethodAnno({data: 456})
  static bar(): void {
    // 静态方法实现
  }
}

🔸 注解的导入导出

注解也可以被导入导出:

// annotations.ts
export @interface MyAnno {}
export @interface ClassAuthor {
  name: string;
}

// app.ts
import { MyAnno } from './annotations';
import * as anno from './annotations';

@MyAnno
@anno.ClassAuthor({name: "Alice"})
class App {
  // 类的实现
}

🔸 注解的限制

  1. 不允许在抽象类或抽象方法上使用注解
  2. 不允许在类的getter和setter方法中添加注解
  3. 同一个实体不能重复使用同一注解
  4. 子类不会继承基类的注解
// 错误示例
@MyAnno // 错误:不允许在抽象类上使用注解
abstract class Base {
  @MyAnno // 错误:不允许在抽象方法上使用注解
  abstract foo(): void;
}

class Child extends Base {
  // 子类不会继承基类的注解
  foo(): void {
    // 实现
  }
}

💡 模块化编程的最佳实践

  1. 单一职责原则:每个模块应该只负责一个功能或相关功能的集合
  2. 明确的导出:只导出模块的公共API,保持内部实现的私有性
  3. 避免循环依赖:模块之间的循环依赖会导致难以理解和维护的代码
  4. 合理使用动态导入:对于大型依赖或条件加载的模块,使用动态导入提高应用性能
  5. 组织模块结构:按功能、特性或层次组织模块,使项目结构清晰

🌈 模块化的应用场景

🔸 功能模块化

将不同功能拆分为独立模块:

// auth.ts - 认证相关功能
export function login(username: string, password: string): boolean {
  // 实现登录逻辑
  return true;
}

// user.ts - 用户相关功能
import { login } from './auth';

export class User {
  // 用户相关实现
}

🔸 UI组件模块化

将UI组件封装为独立模块:

// button.ts - 按钮组件
export class Button {
  // 按钮组件实现
}

// dialog.ts - 对话框组件
export class Dialog {
  // 对话框组件实现
}

// app.ts - 应用主模块
import { Button } from './button';
import { Dialog } from './dialog';

// 使用组件

🔸 服务模块化

将后端服务封装为独立模块:

// api.ts - API服务
export async function fetchData(url: string): Promise<any> {
  // 实现数据获取逻辑
}

// cache.ts - 缓存服务
export class Cache {
  // 缓存实现
}

// app.ts - 应用主模块
import { fetchData } from './api';
import { Cache } from './cache';

// 使用服务

💫 小结: ArkTS的模块系统为我们提供了强大的代码组织工具,通过导入导出机制,我们可以构建模块化、可维护的应用程序。结合注解特性,可以为代码添加丰富的元数据,进一步增强代码的表达能力。

❤️ 如果觉得有用,请点赞收藏,你的支持是我持续创作的动力!

#编程学习 #ArkTS #模块化编程 #HarmonyOS #技术干货 #前端开发

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐