鸿蒙 ArkTS 实战进阶:从核心组件到面向对象编程一篇通

鸿蒙 ArkTS 实战进阶:从核心组件到面向对象编程一篇通

前言

在上一篇《鸿蒙 ArkTS 实战全解:从基础布局到完整页面》中,我们掌握了 ArkTS 的标准页面结构、基础组件和响应式状态管理,已经能够写出简单可用的页面。这周我们继续深入,一方面攻克两个移动端开发中使用频率最高 的容器组件 ------ScrollTabs,解决绝大多数页面的滚动和导航需求;另一方面正式进入 ArkTS 的面向对象编程世界,学习 classinterface 和泛型,这是我们从 "能写代码" 到 "写好代码" 的关键转折点。

一、UI 组件实战篇:掌握滚动与导航的精髓

1.1 Scroll 组件:所有长页面的基础

Scroll 是 ArkUI 中最基础的可滚动容器,当子组件的尺寸超过父组件视口时自动提供滚动能力。它适用于静态内容展示、少量动态内容和需要自定义滚动行为的场景。

核心属性速查表
属性 类型 默认值 说明
scrollDirection ScrollDirection Vertical 滚动方向:垂直 / 水平 / 自由
enableScrollInteraction boolean true 是否允许用户手动滚动
edgeEffect EdgeEffect Spring 边缘效果:弹簧回弹 / 无
scrollBar BarState Auto 滚动条状态:自动 / 始终显示 / 隐藏
实战用法
typescript 复制代码
// 1. 基础垂直滚动
Scroll() {
  Column({ space: 12 }) {
    ForEach(Array.from({ length: 20 }, (_, i) => i), (item) => {
      Text(`列表项 ${item}`)
        .width('100%')
        .height(60)
        .backgroundColor('#f5f5f5')
        .borderRadius(8)
        .textAlign(TextAlign.Center)
    })
  }
  .padding(16)
}
.width('100%')
.height('100%')

// 2. 使用 Scroller 控制器实现程序化滚动
private scroller: Scroller = new Scroller();

// 滚动到顶部
this.scroller.scrollToEdge(Edge.Top, { animation: true });

// 滚动到指定位置
this.scroller.scrollTo({
  yOffset: 500,
  animation: { duration: 300, curve: Curve.EaseInOut }
});
常见业务场景实现
  • 上拉加载更多 :监听 onReachEnd 事件,在滚动到底部时触发数据加载
  • 下拉刷新 :配合官方 Refresh 组件使用,几行代码即可实现标准下拉刷新效果
  • 吸顶效果 :监听 onDidScroll 事件,根据滚动偏移量动态改变组件的位置和层级

💡 最佳实践 :超过 50 条数据的长列表不要用 Scroll,一定要用 List + LazyForEach 实现虚拟化滚动,否则会严重影响性能。

1.2 Tabs 组件:移动端导航的灵魂

Tabs 是实现标签页切换的核心组件,由 Tabs 容器、TabBar 标签栏和 TabContent 内容页三部分组成。它广泛应用于底部导航、顶部分类切换和页面分段展示等场景。

核心属性速查表
属性 类型 默认值 说明
barPosition BarPosition Start 标签栏位置:顶部 / 底部
index number 0 默认选中的标签索引
scrollable boolean true 是否允许左右滑动切换
barMode BarMode Fixed 标签栏模式:固定宽度 / 可滚动
实战用法:底部导航栏(最常用场景)
typescript 复制代码
@State currentIndex: number = 0;
private tabController: TabController = new TabController();

Tabs({ controller: this.tabController, barPosition: BarPosition.End }) {
  TabContent() {
    HomePage()
  }
  .tabBar(this.buildTabItem("首页", $r('app.media.icon_home'), 0))
  .lazy(true) // 关键:非首页启用懒加载
  .keepAlive(true) // 关键:加载后保活

  TabContent() {
    CategoryPage()
  }
  .tabBar(this.buildTabItem("分类", $r('app.media.icon_category'), 1))
  .lazy(true)
  .keepAlive(true)

  TabContent() {
    MinePage()
  }
  .tabBar(this.buildTabItem("我的", $r('app.media.icon_mine'), 2))
  .lazy(true)
  .keepAlive(true)
}
.width('100%')
.height('100%')
.onChange((index) => {
  this.currentIndex = index;
})

@Builder
buildTabItem(title: string, icon: Resource, index: number) {
  Column({ space: 4 }) {
    Image(icon)
      .width(24)
      .height(24)
      .fillColor(this.currentIndex === index ? Color.Blue : Color.Gray)
    Text(title)
      .fontSize(12)
      .fontColor(this.currentIndex === index ? Color.Blue : Color.Gray)
  }
  .width('100%')
  .height(56)
  .justifyContent(FlexAlign.Center)
}

💡 最佳实践 :所有非首页的 TabContent 都必须设置 lazy: true,避免页面初始化时一次性渲染所有内容,大幅提升首屏加载速度。对于频繁切换的页面,同时设置 keepAlive: true,减少重复渲染开销。

二、面向对象编程篇:写出可维护的代码

2.1 Class 类:封装数据与行为

类是面向对象编程的基本单位,用于封装数据和操作数据的方法。在 ArkTS 中,我们大量使用类来封装数据模型、工具类和业务逻辑。

基本语法
typescript 复制代码
// 定义一个用户类
class User {
  // 成员属性
  name: string;
  age: number;

  // 构造函数
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  // 成员方法
  sayHello(): void {
    console.log(`你好,我是${this.name},今年${this.age}岁`);
  }
}

// 使用类
const user = new User("张三", 20);
user.sayHello(); // 输出:你好,我是张三,今年20岁
继承
typescript 复制代码
// 学生类继承自用户类
class Student extends User {
  studentId: string;

  constructor(name: string, age: number, studentId: string) {
    super(name, age); // 调用父类构造函数
    this.studentId = studentId;
  }

  // 重写父类方法
  sayHello(): void {
    super.sayHello(); // 调用父类方法
    console.log(`我的学号是${this.studentId}`);
  }
}
在 ArkTS 中的应用

最常见的用法是封装数据模型:

typescript 复制代码
// 定义一个商品数据模型
class Product {
  id: number;
  name: string;
  price: number;
  imageUrl: string;

  constructor(id: number, name: string, price: number, imageUrl: string) {
    this.id = id;
    this.name = name;
    this.price = price;
    this.imageUrl = imageUrl;
  }

  // 格式化价格
  getFormattedPrice(): string {
    return `¥${this.price.toFixed(2)}`;
  }
}

// 在组件中使用
@State productList: Product[] = [
  new Product(1, "华为手机", 3999, "https://example.com/phone.jpg"),
  new Product(2, "华为平板", 2499, "https://example.com/tablet.jpg")
];

2.2 Interface 接口:定义契约

接口用于定义对象的结构和类型,它不包含具体实现,只描述对象应该具有哪些属性和方法。接口是 TypeScript/ArkTS 最强大的特性之一,能够帮助我们在编译时发现类型错误。

基本语法
typescript 复制代码
// 定义一个用户接口
interface IUser {
  name: string;
  age: number;
  sayHello(): void;
}

// 实现接口
class User implements IUser {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  sayHello(): void {
    console.log(`你好,我是${this.name}`);
  }
}
可选属性和只读属性
typescript 复制代码
interface IProduct {
  readonly id: number; // 只读属性,创建后不能修改
  name: string;
  price: number;
  description?: string; // 可选属性,可以不存在
}
在 ArkTS 中的应用
  1. 定义组件参数类型
typescript 复制代码
interface ICardProps {
  title: string;
  content: string;
  onClick?: () => void;
}

@Component
struct CustomCard {
  @Prop props: ICardProps;

  build() {
    Column() {
      Text(this.props.title)
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
      Text(this.props.content)
        .fontSize(14)
        .margin({ top: 8 })
    }
    .padding(16)
    .backgroundColor(Color.White)
    .borderRadius(8)
    .onClick(() => {
      this.props.onClick?.();
    })
  }
}
  1. 定义 API 返回值类型
typescript 复制代码
interface IApiResponse<T> {
  code: number;
  message: string;
  data: T;
}

interface IUserInfo {
  id: number;
  username: string;
  avatar: string;
}

// 网络请求返回值类型
type UserInfoResponse = IApiResponse<IUserInfo>;

2.3 泛型接口与泛型类:编写通用代码

泛型允许我们在定义接口、类或函数时不指定具体类型,而是在使用时再指定。这大大提高了代码的复用性和灵活性。

泛型接口
typescript 复制代码
// 定义一个通用的列表响应接口
interface IListResponse<T> {
  list: T[];
  total: number;
  page: number;
  pageSize: number;
}

// 使用时指定具体类型
type ProductListResponse = IListResponse<Product>;
type UserListResponse = IListResponse<User>;
泛型类
复制代码
// 定义一个通用的栈类
class Stack<T> {
  private items: T[] = [];

  // 入栈
  push(item: T): void {
    this.items.push(item);
  }

  // 出栈
  pop(): T | undefined {
    return this.items.pop();
  }

  // 获取栈顶元素
  peek(): T | undefined {
    return this.items[this.items.length - 1];
  }

  // 判断栈是否为空
  isEmpty(): boolean {
    return this.items.length === 0;
  }
}

// 使用数字栈
const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
console.log(numberStack.pop()); // 输出:2

// 使用字符串栈
const stringStack = new Stack<string>();
stringStack.push("a");
stringStack.push("b");
console.log(stringStack.pop()); // 输出:b

💡 最佳实践:当你发现自己在写多个结构相同但类型不同的类或接口时,就应该考虑使用泛型来重构代码,消除重复。

三、本周总结

这周我们完成了从 "基础组件使用" 到 "面向对象编程" 的跨越。掌握了 ScrollTabs 这两个核心组件,我们已经能够开发出绝大多数移动端常见的页面布局;而学习了类、接口和泛型,我们的代码开始变得更加结构化、可维护和可复用。

ArkTS 的学习曲线非常友好,它既保留了 JavaScript/TypeScript 的灵活性,又加入了强类型和面向对象的特性,非常适合移动端开发。只要我们一步一个脚印,把基础打扎实,很快就能开发出高质量的鸿蒙应用。

相关推荐
book01212 小时前
华为ensp学习日志 记2026
学习·华为·智能路由器
wechat_Neal3 小时前
Google AAOS 2026发布深度解析与对中国车企出海的战略启示
人工智能·microsoft·华为·汽车
不羁的木木4 小时前
HarmonyOS文件基础服务(Core File Kit)实战演练02-环境搭建与基础配置
华为·harmonyos
不羁的木木4 小时前
ArkWeb实战学习笔记04-JavaScript与Native通信
笔记·学习·harmonyos
Goway_Hui4 小时前
【 鸿蒙原生应用开发--ArkUI--005 】PomodoroApp 番茄钟应用开发教程
华为·harmonyos
Goway_Hui5 小时前
【鸿蒙原生应用开发--ArkUI--004】NotesApp - 笔记应用教程
harmonyos
想你依然心痛5 小时前
HarmonyOS 6(API 23)智能体驱动的沉浸式AR深海科考探索舱
华为·ar·harmonyos·智能体
Goway_Hui6 小时前
【鸿蒙原生应用开发--ArkUI--002】CalculatorApp - 计算器应用教程
华为·harmonyos