第四章 ArkTS语言基础(下):类与接口、装饰器、模块化
好,咱们接着往下聊。上一章我们把ArkTS的基础类型和函数过了一遍,这一章要深入一点,聊聊类、接口、装饰器,还有模块化。这些东西在实际开发中天天用,尤其是装饰器,可以说是鸿蒙车机开发的灵魂。
4.1 类与接口:面向对象的基础
ArkTS的类,说白了就是TypeScript那一套,但加了一些鸿蒙自己的约束。我个人习惯把类当作「蓝图」,用来描述车机上的一个实体,比如一个车载应用、一个导航指令、或者一个多媒体播放器。
4.1.1 定义一个类
class CarMediaPlayer {
private currentTrack: string = '';
private volume: number = 50;
// 构造函数
constructor(track: string) {
this.currentTrack = track;
}
// 方法
public play(): void {
console.log(`正在播放: ${this.currentTrack}`);
}
public setVolume(vol: number): void {
if (vol >= 0 && vol <= 100) {
this.volume = vol;
} else {
console.warn('音量范围是0-100');
}
}
}
嗯,这里要注意:ArkTS对访问修饰符要求比较严格。我在项目中遇到过,如果不写 private 或 public,默认就是 public,但官方推荐显式声明。你想想看,车机系统里数据安全很重要,不该暴露的属性和方法一定要藏好。
4.1.2 接口:定义契约
接口就是一份「合同」。你实现了某个接口,就必须遵守它的约定。这在多人协作开发车机应用时特别有用。
interface IMediaControl {
play(): void;
pause(): void;
next(): void;
previous(): void;
}
class MusicPlayer implements IMediaControl {
play(): void {
console.log('播放音乐');
}
pause(): void {
console.log('暂停音乐');
}
next(): void {
console.log('下一首');
}
previous(): void {
console.log('上一首');
}
}
我曾经在做一个车载音乐应用时,团队里三个人同时开发不同的播放器模块。如果没有接口约束,每个人写的播放器方法名都不一样,那后期联调就是一场灾难。接口就是用来避免这种混乱的。
I 开头,比如 INavigation、IVoiceControl。这是行业惯例,一看就知道是个接口。
4.2 装饰器:鸿蒙开发的灵魂
装饰器是ArkTS最核心的特性之一。说白了,它就是给类、属性、方法打上一个「标签」,让框架知道该怎么处理它们。在鸿蒙车机开发中,@Component 和 @State 是最常用的两个。
4.2.1 @Component:声明一个UI组件
任何你想在屏幕上显示的东西,都需要用 @Component 装饰。它告诉编译器:这是一个UI组件,需要被渲染。
@Component
struct SpeedDisplay {
build() {
Text('当前车速: 60 km/h')
.fontSize(24)
.fontColor(Color.White)
}
}
你看,@Component 后面跟着一个 struct,这是ArkTS特有的写法。我刚开始接触时也觉得奇怪,为什么不用 class?后来才明白,struct 在性能上更轻量,适合频繁刷新的UI场景。
4.2.2 @State:让数据驱动UI
车机上的数据是动态变化的,比如车速、导航距离、音乐进度。用 @State 装饰的变量,一旦值变了,UI会自动刷新。这就是所谓的「响应式编程」。
@Component
struct SpeedPanel {
@State currentSpeed: number = 0;
build() {
Column() {
Text(`当前车速: ${this.currentSpeed} km/h`)
.fontSize(28)
Button('加速')
.onClick(() => {
this.currentSpeed += 10;
})
}
}
}
@State 装饰的变量变化时,UI才会重新渲染。普通变量变了,UI不会动。这一点我踩过坑——曾经把一个状态变量忘了加 @State,调试了半天才发现UI不更新。
4.2.3 其他常用装饰器
| 装饰器 | 作用 | 使用场景 |
|---|---|---|
| @Prop | 父组件传给子组件的单向数据 | 导航信息传递 |
| @Link | 父子组件双向绑定 | 设置页面同步 |
| @Watch | 监听状态变化 | 车速超限报警 |
| @Builder | 封装重复UI片段 | 按钮、卡片复用 |
为什么会需要这么多装饰器?因为车机场景太复杂了。比如导航页面,父组件要传目的地给子组件,用 @Prop;设置页面里,音量调节需要双向同步,用 @Link。每种装饰器都有它最适合的舞台。
@State 变量同时用在多个子组件中,结果数据一改,所有组件都重新渲染,导致界面卡顿。后来改用 @Prop 单向传递,性能明显提升。记住:@State 不要滥用,只在真正需要响应式的地方使用。
4.3 模块化与导入导出
一个车机应用少说也有几十个页面,几百个组件。如果所有代码都写在一个文件里,那维护起来就是噩梦。模块化就是用来解决这个问题的。
4.3.1 export:把东西拿出去
你想让别的文件用你的代码,就得用 export 把它暴露出去。
// utils/speed-utils.ts
export function kmhToMph(kmh: number): number {
return kmh * 0.621371;
}
export const MAX_SPEED_LIMIT = 120;
export class SpeedCalculator {
static isOverLimit(speed: number): boolean {
return speed > MAX_SPEED_LIMIT;
}
}
4.3.2 import:把东西拿进来
别的文件想用这些功能,就用 import 引入。
// pages/dashboard.ts
import { kmhToMph, SpeedCalculator } from '../utils/speed-utils';
let speedMph = kmhToMph(100);
if (SpeedCalculator.isOverLimit(100)) {
console.warn('超速了!');
}
嗯,这里有个细节要注意:路径要用相对路径,以 ./ 或 ../ 开头。我见过新手直接写文件名,结果编译报错找不到模块。
4.3.3 默认导出 vs 命名导出
| 导出方式 | 语法 | 导入方式 | 适用场景 |
|---|---|---|---|
| 默认导出 | export default class |
import X from '...' |
一个文件只导出一个主要功能 |
| 命名导出 | export function |
import { X } from '...' |
一个文件导出多个功能 |
4.4 综合实战:一个简单的车速仪表盘
把今天学的知识串起来,写一个简单的车速仪表盘组件。
// interfaces/vehicle.ets
export interface IVehicleData {
speed: number;
rpm: number;
fuelLevel: number;
}
// utils/format.ets
export function formatSpeed(speed: number): string {
return `${speed.toFixed(0)} km/h`;
}
// components/SpeedGauge.ets
import { IVehicleData } from '../interfaces/vehicle';
import { formatSpeed } from '../utils/format';
@Component
export struct SpeedGauge {
@State vehicleData: IVehicleData = {
speed: 0,
rpm: 0,
fuelLevel: 100
};
build() {
Column() {
Text(formatSpeed(this.vehicleData.speed))
.fontSize(48)
.fontWeight(FontWeight.Bold)
Text(`转速: ${this.vehicleData.rpm} rpm`)
.fontSize(20)
ProgressBar({ value: this.vehicleData.fuelLevel, total: 100 })
.width('80%')
}
.padding(20)
.backgroundColor('#1a1a2e')
}
}
你看,这个例子把接口、类、装饰器、模块化全用上了。接口定义了数据结构,工具函数负责格式化,组件用 @State 管理状态,最后通过 export 暴露出去给其他页面使用。
说实话,刚开始学这些概念时,我也觉得有点抽象。但当你真正开始写一个车机应用,比如导航页面、音乐播放器、车辆设置面板,你会发现这些知识就是地基。地基打牢了,上面盖什么楼都稳。
下一章我们聊聊ArkTS的UI构建,也就是怎么用这些组件拼出漂亮的车机界面。到时候你会看到,今天学的装饰器会频繁出现,尤其是 @State 和 @Builder,几乎每个页面都会用到。