1.开发基础

目录

  1. 开发环境准备
  2. ArkTS 语言
  3. ArkUI-基础
  4. ArkUI-高级

1. 开发环境准备

在开始写代码前,你需要安装IDE。


1.开发环境搭建(超详细版)

1.1 系统要求与准备工作

软件预依赖

  • Node.js:版本 14.x ~ 18.x
  • OpenJDK:DevEco Studio 内置,无需单独配置
  • Python:3.8+(可选,某些工具链需要)

1.2 下载 DevEco Studio

步骤一:访问下载地址

打开浏览器,进入 地址

步骤二:选择版本

你会看到两个版本:

  • DevEco Studio(正式版):面向商业项目,稳定。
  • DevEco Studio(Beta版):包含最新特性,学习用推荐。

初学者建议选择最新正式版。

步骤三:下载对应系统版本

点击下载按钮,根据你的操作系统选择:


1.3 安装 DevEco Studio(Windows 为例)

步骤一:运行安装程序

双击下载的 deveco-studio-x.x.x.xxx.exe,如果弹出用户账户控制,点击“是”。

步骤二:欢迎界面

点击 Next 继续。

步骤三:选择安装路径

配置项 建议
DevEco Studio 安装路径 保持默认,或改为其他盘符如 D:\harmony
SDK 存储路径 强烈建议放在非系统盘,如 D:\harmony\sdk
模拟器路径 强烈建议放在非系统盘,如 D:\harmony\emulator

步骤四:选择安装选项

勾选 所有选项

步骤五:开始安装

点击 Install,等待进度条走完(约 5-10 分钟,视电脑性能而定)。

步骤六:完成安装

勾选 Run DevEco Studio,点击 Finish,首次启动。


1.4 首次启动与配置向导

步骤一:导入设置(可选)

如果是首次安装,选择 Do not import settings,点击 OK。

步骤二:隐私协议

阅读并勾选 I agree...,点击 Agree

1.5 创建你的第一个项目

步骤一:新建项目

在欢迎页点击 Create Project(或菜单栏 File → New → Create Project)。

步骤二:选择模板

选择 Empty Ability(空白应用模板),点击 Next

步骤三:配置项目信息

配置项 填写内容
Project name MyFirstApp(项目名称,英文,可自定义)
Bundle name com.example.myfirstapp(应用包名,格式:域名反写)
Save location 选择项目保存路径,建议放在代码专用目录
Compile SDK 选择最高的 API 版本
Model entry(默认)
Device type 勾选 Phone(手机)

点击 Finish,项目就创建好了。

步骤四:认识项目目录结构

MyFirstApp/
├── AppScope/               # 应用全局配置
│   └── app.json5           # 应用包名、版本号等
├── entry/                  # 主模块
│   └── src/
│       └── main/
│           ├── ets/        # ArkTS 源码(主要写代码的地方)
│           │   ├── entryability/
│           │   │   └── EntryAbility.ets  # 应用入口
│           │   └── pages/
│           │       └── Index.ets         # 默认首页(我们的主战场)
│           ├── resources/  # 资源文件(图片、字符串等)
│           │   └── base/
│           │       ├── media/    # 图片资源
│           │       ├── element/  # 字符串、颜色等
│           │       └── profile/  # 配置文件
│           └── module.json5      # 模块配置(权限、设备类型等)
└── hvigor/                 # 构建配置文件

1.6 配置模拟器(本地模拟器)

方式一:使用本地模拟器(推荐)

步骤一:打开设备管理器
菜单栏:工具→ 设备管理器。

步骤二:安装模拟器镜像

  • 点击 Virtual Device Manager 标签页
  • 点击右上角 New Emulator
  • 选择设备类型:Phone
  • 点击 Next

步骤三:下载系统镜像

  • 选择一个 API 版本(与项目的 Compile SDK 保持一致)
  • 点击下载图标 ⬇️,下载系统镜像(约 2-3GB)
  • 下载完成后,点击 Next
  • 给模拟器起个名字,点击 Finish

步骤四:启动模拟器

  • 在设备管理器中,点击模拟器右侧的 ▶️ 启动按钮
  • 首次启动需要 2-5 分钟,请耐心等待
  • 看到手机桌面即表示启动成功

1.7 运行你的第一个程序

步骤一:选择目标设备

在 DevEco Studio 顶部工具栏,设备下拉列表中选择:

  • 本地模拟器名称
  • 或你的真机设备名称

步骤二:运行

点击设备列表右侧的 绿色三角形 ▶️ Run 按钮(或按快捷键 Shift + F10)。

步骤三:观察构建过程

底部 Build 窗口会显示编译构建进度:

> hvigor Building...
> hvigor Finished :entry:default@CompileArkTS...
> hvigor BUILD SUCCESSFUL

出现 BUILD SUCCESSFUL 表示构建成功,应用会自动安装到模拟器/真机上。

步骤四:看到成果

模拟器/真机上会显示默认的 Index.ets 页面内容:

Hello World

恭喜你,环境搭建成功!🎉


1.8 常见问题排查

问题现象 可能原因 解决方案
下载 SDK 失败/速度慢 网络问题 检查网络,或配置代理;尝试更换下载镜像源
模拟器启动黑屏/卡死 显卡驱动问题 更新显卡驱动;检查 BIOS 是否开启虚拟化技术(VT-x/AMD-V)
运行按钮灰色不可点 没有选择设备 确保模拟器已启动或真机已连接
hvigor BUILD FAILED 代码或配置错误 仔细查看 Build 窗口中的错误信息,逐条排查

2. ArkTS 语言

ArkTS 是鸿蒙生态的主力开发语言,它在 TypeScript 的基础上进行了拓展和优化。

第二章:ArkTS 核心语法详解

2.1 变量与常量

变量声明(let)

在ArkTS中,使用let关键字声明可变的变量:

// 常量必须在声明时初始化
    const PI: number = 3.14159;
    const APP_NAME = "我的应用";
    const MAX_SIZE = 100;

    // 以下代码会报错
    // PI = 3.14;  // 错误:常量不可重新赋值

    class User{
      name:string
      age:number

      constructor( name:string, age:number) {
        this.name = name
        this.age = age
      }
    }
    // 对象类型的常量,其属性可以修改,但引用不可变
    const USER:User = new User('李四',30);
    console.log('age1:',USER.age)
    USER.age = 31;
    console.log('age2:',USER.age)
// USER =  new User(王五",25 )  // 错误:不能重新赋值常量引用

2.2 数据类型

基础类型

数字类型(number),字符串类型(string),布尔类型(boolean),空值(void)

// 数字类型(整数和浮点数统一使用 number)
let integer: number = 42;
let float: number = 3.14;
let hex: number = 0xFF;      // 十六进制
let binary: number = 0b1010; // 二进制
let octal: number = 0o744;   // 八进制

// 字符串类型
let singleQuotes: string = '单引号字符串';
let doubleQuotes: string = "双引号字符串";
let backticks: string = `模板字符串,支持 ${integer} 的插值`;

// 布尔类型
let isCompleted: boolean = false;
let hasPermission: boolean = true;

// 空值(void)通常用于函数无返回值

// null 和 undefined
let u: undefined = undefined;
let n: null = null;

联合类型

// 一个变量可以是多种类型之一
let value: string | number;
value = "Hello";
value = 123;
// value = true;  // 错误:不能赋值为 boolean

类型断言

类型断言用于手动告诉编译器变量的实际类型,绕过自动类型推断,不会改变运行时类型,仅作用于编译阶段。

// 当你知道变量确切的类型时,可以使用类型断言
let someValue: any = "这是一个字符串";
let strLength: number = (someValue as string).length;

2.3 运算符

算术运算符

  • 加法, - 减法, 乘法,/ 除法, %取模(余数),* 幂运算,++|-- 自增自减
let a = 10, b = 3;

console.log(a + b);   // 13 - 加法
console.log(a - b);   // 7  - 减法
console.log(a * b);   // 30 - 乘法
console.log(a / b);   // 3.333... - 除法
console.log(a % b);   // 1  - 取模(余数)
console.log(a ** b);  // 1000 - 幂运算

// 自增自减
let count1 = 5;
console.log(++count1);  // 6 - 先自增再使用
console.log(count1++);  // 6 - 先使用再自增
console.log(count1);    // 7

赋值运算符

+=,-=,=,/=,%=,*=

let x = 10;
x += 5;   // x = x + 5 = 15
x -= 3;   // x = x - 3 = 12
x *= 2;   // x = x * 2 = 24
x /= 4;   // x = x / 4 = 6
x %= 5;   // x = x % 5 = 1
x **= 2;  // x = x ** 2 = 1

比较运算符

==,!=,===严格相等(值和类型),!==严格不等,>,<,>=,<=

let p = 5, q = 10;

console.log(p == q);   // false - 相等
console.log(p != q);   // true  - 不等
console.log(p === q);  // false - 严格相等(值和类型)
console.log(p !== q);  // true  - 严格不等
console.log(p > q);    // false - 大于
console.log(p < q);    // true  - 小于
console.log(p >= 5);   // true  - 大于等于
console.log(q <= 10);  // true  - 小于等于

逻辑运算符

&&,||,!,?.(可选链操作符-安全访问嵌套属性)

let isLogin = true;
let isAdmin = false;

// 逻辑与 (&&) - 所有条件为真才为真
console.log(isLogin && isAdmin);  // false

// 逻辑或 (||) - 任一条件为真即为真
console.log(isLogin || isAdmin);  // true

// 逻辑非 (!) - 取反
console.log(!isLogin);   // false
console.log(!isAdmin);   // true

// 短路求值
let user = null;
let userName = user || "游客";  // 如果user为null/undefined,使用默认值
console.log(userName);  // "游客"

// 可选链操作符 (?.) - 安全访问嵌套属性
let obj: any = { data: { value: 42 } };
console.log(obj?.data?.value);    // 42
console.log(obj?.nonexistent?.value);  // undefined(不会报错)

2.4 流程控制

条件语句

if ,switch

// if-else 语句
let score1 = 85;

if (score1 >= 90) {
    console.log("优秀");
} else if (score1 >= 75) {
    console.log("良好");
} else if (score1 >= 60) {
    console.log("及格");
} else {
    console.log("不及格");
}

// switch 语句
let day = 3;
let dayName: string;

switch (day) {
    case 1:
        dayName = "星期一";
        break;
    case 2:
        dayName = "星期二";
        break;
    case 3:
        dayName = "星期三";
        break;
    case 4:
        dayName = "星期四";
        break;
    case 5:
        dayName = "星期五";
        break;
    case 6:
        dayName = "星期六";
        break;
    case 7:
        dayName = "星期日";
        break;
    default:
        dayName = "无效的日期";
}
console.log(dayName);  // "星期三"

循环语句

for,while,do-while

// for 循环
console.log("for 循环:");
for (let i = 1; i <= 5; i++) {
    console.log(`计数: ${i}`);
}

// while 循环
console.log("while 循环:");
let countDown = 5;
while (countDown > 0) {
    console.log(`倒计时: ${countDown}`);
    countDown--;
}

// do-while 循环(至少执行一次)
console.log("do-while 循环:");
let num = 0;
do {
    console.log(`数字: ${num}`);
    num++;
} while (num < 3);

// for-of 循环(遍历数组/可迭代对象)
let colors = ["红", "绿", "蓝"];
console.log("for-of 循环:");
for (let color of colors) {
    console.log(color);
}

// for-in 循环(遍历对象属性)
let person = { name: "张三", age: 25, city: "上海" };
console.log("for-in 循环:");
for (let key in person) {
    console.log(`${key}: ${person[key]}`);
}

跳转语句

break,continue

// break - 跳出循环
for (let i = 1; i <= 10; i++) {
    if (i === 5) {
        break;  // i=5时跳出循环
    }
    console.log(i);  // 输出: 1,2,3,4
}

// continue - 跳过当前迭代
for (let i = 1; i <= 5; i++) {
    if (i === 3) {
        continue;  // 跳过3
    }
    console.log(i);  // 输出: 1,2,4,5
}

2.5 数组与枚举

数组

// 数组声明方式
let numbers1: number[] = [1, 2, 3, 4, 5];
let numbers2: Array<number> = [6, 7, 8, 9, 10];
let mixed: (string | number)[] = ["hello", 42, "world", 100];

// 数组操作
let fruits: string[] = ["苹果", "香蕉", "橙子"];

// 添加元素
fruits.push("葡萄");      // 末尾添加
fruits.unshift("草莓");   // 开头添加
console.log(fruits);  // ["草莓", "苹果", "香蕉", "橙子", "葡萄"]

// 删除元素
let last = fruits.pop();      // 删除最后一个元素,返回 "葡萄"
let first = fruits.shift();   // 删除第一个元素,返回 "草莓"
console.log(fruits);  // ["苹果", "香蕉", "橙子"]

// 访问和修改
console.log(fruits[0]);     // "苹果"
fruits[1] = "西瓜";
console.log(fruits);        // ["苹果", "西瓜", "橙子"]

// 数组常用方法
let nums:Array<number> = [1, 2, 3, 4, 5];

// map - 转换每个元素
 let doubled:Array<number> = nums.map(n => n * 2);
 console.log('结果:',doubled);  // [2, 4, 6, 8, 10]

// filter - 过滤元素
 let evens:Array<number> = nums.filter(n => n % 2 === 0);
 console.log('结果:',evens);  // [2, 4]

// reduce - 累积计算
let sum:number = nums.reduce((acc, curr) => acc + curr, 0);
console.log('结果:',sum);  // 15

// forEach - 遍历
nums.forEach(n => console.log(`数字: ${n}`));

// find - 查找元素
let found:number = nums.find(n => n > 3);
console.log('结果:',found);  // 4

// some/every - 条件判断
console.log('结果:',nums.some(n => n > 4));   // true (是否有大于4的)
console.log('结果:',nums.every(n => n > 0));  // true (是否都大于0)


// 数组展开运算符
let arr1:Array<number> = [1, 2, 3];
let arr2:Array<number> = [4, 5, 6];
let combined:Array<number> = [...arr1, ...arr2];
console.log('结果:',combined);  // [1, 2, 3, 4, 5, 6]

枚举(Enum)

枚举 = 给一组固定不变的常量起有意义的名字
让代码不用写魔法数字、魔法字符串,一眼看懂含义,还能避免写错值。

// 数字枚举
enum Direction {
    Up,      // 0
    Down,    // 1
    Left,    // 2
    Right    // 3
}

console.log('UP:',Direction.Up);     // 0

// 自定义初始值
enum Status {
    Pending = 1,
    Approved = 2,
    Rejected = 3
}

// 字符串枚举
enum Color {
    Red = "红色",
    Green = "绿色",
    Blue = "蓝色"
}

console.log(Color.Red);  // "红色"

// 枚举的使用场景
enum UserRole {
  Guest = 0,
  User = 1,
  Admin = 2
}

function checkPermission(role: UserRole):void {
  if (role === UserRole.Admin) {
    console.log("拥有所有权限");
  } else if (role === UserRole.User) {
    console.log("拥有普通权限");
  } else {
    console.log("只有浏览权限");
  }
}

checkPermission(2);  // "拥有所有权限"

// 常量枚举(编译后会被内联,性能更好)
const enum Weekday {
    Monday = "周一",
    Tuesday = "周二",
    Wednesday = "周三"
}
console.log(Weekday.Monday);  // 编译后直接替换为 "周一"

2.6 函数

函数 = 可以重复使用的代码块
把一段逻辑封装起来,需要时直接调用,不用重复写代码。

函数声明与表达式

函数声明方式

// 函数声明 
//a,b是函数参数
//:number是函数返回值类型
function add(a: number, b: number): number {
    return a + b;
}

// 箭头函数
let multiply = (a: number, b: number): number => a * b;

console.log('结果1:',add(5, 3));      
console.log('结果2:',multiply(6, 7));  

参数特性

1.必选参数(默认规则)
必须传参,数量、类型严格匹配,ArkTS 强类型约束,不允许隐式缺省。
2.可选参数 ?
参数名后加 ?,表示该参数可传、可不传。
3.默认参数
定义时直接赋值,不传参则使用默认值。
4.剩余参数(不定参数)...
用 ...参数名: 类型[] 接收多个不确定数量的同类型参数,把零散参数收集为数组。

规则:剩余参数只能写在参数列表最后一位

// 可选参数(使用 ?)
function greet1(name: string, title?: string): string {
    if (title) {
        return `你好,${title} ${name}`;
    }
    return `你好,${name}`;
}
console.log(greet1("张三"));          
console.log(greet1("张三", "经理"));   

// 默认参数
function greet2(name: string, greeting: string = "你好"): string {
    return `${greeting},${name}`;
}
console.log(greet2("李四"));           
console.log(greet2("王五", "欢迎"));    

// 剩余参数(不定数量参数)
function sumAll(...numbers: number[]): number {
  return numbers.reduce((total:number, num:number):number => total + num, 0);
}
console.log('结果:'+sumAll(1,2,3));

返回值

// 无返回值(void)
function logMessage(message: string): void {
    console.log(`[LOG] ${message}`);
}

// 函数作为返回值
function multiplyBy(factor: number): (num: number) => number {
    return function(num: number): number {
        return num * factor;
    };
}

let double = multiplyBy(2);
let triple = multiplyBy(3);
console.log('结果:'+double(5));   // 10
console.log('结果:'+triple(5));   // 15

// 递归函数
function factorial(n: number): number {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}
console.log('结果:'+factorial(5));  // 120

函数重载

函数重载 = 同一个函数名,支持不同的参数类型 / 参数个数,调用时自动匹配。
让一个函数名能处理多种参数情况,代码更统一、更优雅。

// 函数重载声明
function process(value: string): string;
function process(value: number): number;
function process(value: boolean): boolean;
// 实现
function process(value: string | number | boolean): string | number | boolean {
    if (typeof value === "string") {
        return value.toUpperCase();
    } else if (typeof value === "number") {
        return value * 2;
    } else {
        return !value;
    }
}

console.log(process("hello"));  // "HELLO"
console.log(process(10));       // 20
console.log(process(true));     // false

2.7 面向对象

类的基本定义

类 = 封装属性 + 方法的模板
用来创建对象,是面向对象(OOP)的核心。

构造函数(constructor)
创建对象时自动执行,用来初始化属性。

// 简单的类
class Animal {
    // 属性
    name: string;
    age: number;
    
    // 构造函数
    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
    
    // 方法 类中的定义的函数 不需要function关键字
    speak(): void {
        console.log(`${this.name} 发出了叫声`);
    }
    getInfo(): string {
        return `${this.name},${this.age}岁`;
    }
}

// 创建实例
let dog = new Animal("旺财", 3);
dog.speak();         
console.log(dog.getInfo());  

访问修饰符

控制属性 / 方法能否被外部访问,ArkTS 强制规范。

修饰符 作用
public 公开(默认),任何地方可访问
private 私有,只能在当前类内部访问
protected 受保护,当前类 + 子类可访问
class Person {
    public name: string;      // 公有(默认),任何地方都可访问
    private age: number;      // 私有,只能在本类内部访问
    protected gender: string; // 受保护,本类和子类可访问
    readonly id: number;      // 只读,只能在初始化时赋值
    
    constructor(name: string, age: number, gender: string, id: number) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.id = id;
    }
    
    // 公共方法访问私有属性
    public getAge(): number {
        return this.age;
    }
    
    // 修改私有属性
    public setAge(age: number): void {
        if (age > 0 && age < 150) {
            this.age = age;
        }
    }
    
    private getSecret(): string {
        return "这是私有方法";
    }
    
    protected getProtectedInfo(): string {
        return `性别: ${this.gender}`;
    }
}

let person1 = new Person("张三", 25, "男", 1001);
console.log(person1.name);        
// console.log(person1.age);       
console.log(person1.getAge());    
// person1.id = 1002;           

继承

1.子类继承父类,复用代码,扩展功能。
使用 extends 关键字
2.子类用 super() 调用父类构造函数
3.子类可以重写父类方法

class Student extends Person {
    studentId: string;
    grade: number;
    
    constructor(name: string, age: number, gender: string, id: number, studentId: string, grade: number) {
        super(name, age, gender, id);  // 调用父类构造函数
        this.studentId = studentId;
        this.grade = grade;
    }
    
    // 重写父类方法
    getInfo(): string {
        // 可以访问 protected 属性
        return `${this.name},${this.getAge()}岁,${this.gender},学号: ${this.studentId}`;
    }
    
    study(): void {
        console.log(`${this.name} 正在学习`);
    }
}

let student = new Student("李华", 18, "男", 1002, "2024001", 85);
console.log(student.getInfo());
student.study();

静态成员

属于类,不属于对象,不用 new 就能调用。

class MathUtils {
    static PI: number = 3.14159;
    
    static add(a: number, b: number): number {
        return a + b;
    }
    
    static multiply(a: number, b: number): number {
        return a * b;
    }
    
    // 静态方法不能访问实例成员
}

// 直接通过类名调用,无需创建实例
console.log('π:'+MathUtils.PI);          // 3.14159
console.log('和:'+MathUtils.add(10, 20)); // 30
console.log('积:'+MathUtils.multiply(5, 6)); // 30

Getter 和 Setter

安全地读取 / 修改私有属性。

class Temperature {
    private _celsius: number = 0;
    
    get celsius(): number {
        return this._celsius;
    }
    
    set celsius(value: number) {
        if (value < -273.15) {
            throw new Error("温度不能低于绝对零度");
        }
        this._celsius = value;
    }
    
    get fahrenheit(): number {
        return this._celsius * 9/5 + 32;
    }
    
    set fahrenheit(value: number) {
        this.celsius = (value - 32) * 5/9;
    }
}

    let temp = new Temperature();
    temp.celsius = 25;
    console.log('摄氏温度:'+temp.celsius);     // 25
    console.log('华氏温度:'+temp.fahrenheit);  // 77
    temp.fahrenheit = 100;
    console.log('华氏温度:'+temp.fahrenheit);  // 100
    console.log('摄氏温度:'+temp.celsius);     // 37.777...

2.8 接口(Interface)

接口定义规则,类必须遵守规则实现。

基础接口

// 定义接口
interface ISpeak {
  speak(): void;
}

// 类实现接口
class Student implements ISpeak {
  name:string;
  constructor(name:string) {
    this.name = name
  }
  speak(): void {
    console.log(`学生${this.name}正在说话`);
  }
}

注意ArkTS接口规则包括属性和方法

实现多个接口

interface ClockInterface {
    currentTime: Date;
    setTime(d: Date): void;
}

interface AlarmInterface {
    alarm(): void;
}

class SmartClock implements ClockInterface, AlarmInterface {
    currentTime: Date;
    
    constructor() {
        this.currentTime = new Date();
    }
    
    setTime(d: Date): void {
        this.currentTime = d;
    }
    
    alarm(): void {
        console.log("闹钟响了!");
    }
}

2.9 泛型

一句话讲清:泛型是什么?
泛型 = 让函数 “不提前写死类型”,调用时再指定类型。
作用:一套代码,支持所有类型,同时保留类型安全。

泛型函数

你想写一个函数:传入什么类型,就返回什么类型。
不用泛型,只能这样写:

// 坏写法:只能处理 number
function identity(arg: number): number {
  return arg;
}

// 更坏写法:用 any,丢失类型检查(不安全)
function identity(arg: any): any {
  return arg;
}

使用泛型解决

// 基本泛型函数
function identity<T>(arg: T): T {
    return arg;
}

// 使用泛型函数
let output1 = identity<string>("hello");
let output2 = identity<number>(100);

// 泛型约束
interface Lengthwise {
    length: number;
}

function logLength<T extends Lengthwise>(arg: T): T {
     console.log('length:'+arg.length);
    return arg;
}

logLength("hello");      
logLength([1, 2, 3]);      
// logLength(123);         // 错误:数字没有 length 属性

泛型接口

泛型接口 = 把 “泛型” 用在接口上,让接口可以适配多种数据类型,保持类型安全。

// 定义泛型接口
interface Result<T> {
  code: number;
  data: T; // data 类型不固定,用 T 表示
  msg: string;
}

// 使用:data 是 string 类型
let res1: Result<string> = {
  code: 200,
  data: "成功",
  msg: "ok"
};

// 使用:data 是 number 类型
let res2: Result<number> = {
  code: 200,
  data: 100,
  msg: "ok"
};

泛型类

泛型类就是给类增加泛型参数,让类的属性、方法可以适配多种类型,兼顾复用性与类型安全,鸿蒙中常用来封装工具类、数据容器、通用缓存等。

// 定义泛型类
class Container<T> {
  // 属性使用泛型 T
  private value: T;

  constructor(val: T) {
    this.value = val;
  }

  // 方法也可使用泛型 T
  getValue(): T {
    return this.value;
  }

  setValue(val: T): void {
    this.value = val;
  }
}

// 使用类时,指定具体类型
// 实例1:T 为 string
let strBox = new Container<string>("ArkTS");
console.log(strBox.getValue()); // ArkTS

// 实例2:T 为 number
let numBox = new Container<number>(666);
console.log(numBox.getValue()); // 666

2.10 常用内置对象

Date 日期对象

// 创建日期对象
let currentDate = new Date();
console.log('现在:'+currentDate);

let specificDate = new Date(2024, 0, 15, 10, 30, 0);  // 2024年1月15日 10:30:00
let dateFromString = new Date("2024-12-25");
let dateFromTimestamp = new Date(1703520000000);
console.log('日期:',specificDate,dateFromString,dateFromTimestamp);

// 获取日期信息
let date = new Date();
console.log('年份',date.getFullYear());    // 年份
console.log('月份',date.getMonth());        // 月份 (0-11)
console.log('日期',date.getDate());         // 日期 (1-31)
console.log('星期几',date.getDay());          // 星期几 (0-6, 0为周日)
console.log('小时',date.getHours());        // 小时 (0-23)
console.log('分钟',date.getMinutes());      // 分钟 (0-59)
console.log('秒',date.getSeconds());      // 秒 (0-59)
console.log('毫秒',date.getMilliseconds()); // 毫秒
console.log('时间戳',date.getTime());         // 时间戳

 //日期格式化
  console.log( date.toString() )
  console.log( date.toDateString() )
  console.log( date.toTimeString() )
  console.log( date.toLocaleString() )
  console.log( date.toLocaleDateString() )
  console.log( date.toLocaleTimeString() )

Math 数学对象

// 常用常量
console.log('π:',Math.PI);    // 3.141592653589793
console.log('E:',Math.E);     // 2.718281828459045

// 基本运算
console.log('结果:',Math.abs(-5));        // 5 - 绝对值
console.log('结果:',Math.ceil(4.2));      // 5 - 向上取整
console.log('结果:',Math.floor(4.9));     // 4 - 向下取整
console.log('结果:',Math.round(4.5));     // 5 - 四舍五入
console.log('结果:',Math.max(1, 5, 3));   // 5 - 最大值
console.log('结果:',Math.min(1, 5, 3));   // 1 - 最小值
console.log('结果:',Math.pow(2, 3));      // 8 - 幂运算
console.log('结果:',Math.sqrt(16));       // 4 - 平方根
console.log('结果:',Math.random());       // 0-1之间的随机数

// 生成指定范围的随机数
function randomRange(min: number, max: number): number {
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
console.log(' 1-100之间的随机整数',randomRange(1, 100));  // 1-100之间的随机整数

Map 和 Set

// Map - 键值对集合
let userMap = new Map<string, string | number>();
userMap.set("name", "张三");
userMap.set("age", 25);
userMap.set("city", "北京");


 // 获取值
console.log('结果',userMap.get("name"));  // "张三"
//包含值
console.log('包含吗',userMap.has("age"));   // true

// 遍历Map
userMap.forEach((value, key) => {
    console.log(`${key}: ${value}`);
});

// 删除和清空
userMap.delete("city");
// userMap.clear();  // 清空所有

console.log('容量:',userMap.size);  // 2

// Set - 唯一值集合
let numberSet = new Set<number>();
numberSet.add(1);
numberSet.add(2);
numberSet.add(3);
numberSet.add(2);  // 重复添加无效

 console.log('包含2吗',numberSet.has(2)); // true
 console.log('容量:',numberSet.size);   // 3

// 遍历Set
numberSet.forEach(value => {
      console.log('当前值:',value);
});

JSON对象

只有两个核心静态方法:
JSON.stringify() → 对象 / 值 → JSON 字符串
JSON.parse() → JSON 字符串 → 对象 / 值

let obj = { name: "张三", age: 20, isOk: true, addr: null };
let str = JSON.stringify(obj);
console.log(str);

let str = '{"name":"张三","age":20}';
let obj = JSON.parse(str);
console.log(obj.name); 

3. ArkUI-基础

3.1.鸿蒙 ArkUI 入门核心章节,覆盖组件核心概念、系统常用布局/基础组件、自定义组件、多态样式、双向数据绑定。

3.1.1. 什么是 ArkUI 组件

ArkUI 是鸿蒙应用的前端UI框架,组件是 ArkUI 页面的最小组成单元。我们看到的按钮、文字、图片、布局容器,全部都是组件,页面本质就是「多个组件嵌套组合」的结果。
ArkUI 采用声明式开发范式:开发者只需要声明UI结构、数据、样式,框架自动完成页面渲染,无需手动操作DOM。

3.1.2. 组件分类

  • 基础组件:最小UI单元,负责展示内容(Text、Image、Button、TextInput等)
  • 布局组件:容器组件,用于控制子组件排列布局(Column、Row、Flex、Grid等)
  • 自定义组件:开发者基于系统组件封装的可复用组件

3.1.3. 组件核心特征

  • 结构:组件标签嵌套组合形成页面结构
  • 属性:设置宽高、颜色、字体等样式
  • 事件:绑定点击、滑动、输入等交互行为
  • 数据:支持数据驱动视图,数据变、视图自动更新

3.1.4. 最简 ArkUI 页面示例

@Entry
@Component
struct Index {
  build() {
    // 布局容器 + 基础组件
    Column() {
      Text("Hello ArkUI")
        .fontSize(30)
        .fontColor(Color.Blue)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

3.2.自定义通用组件

系统内置组件无法满足业务复用需求时,可封装自定义组件,核心优势:代码复用、降低冗余、统一UI样式、便于维护。

3.2.1. 自定义组件核心规则

  • 必须使用 @Component 装饰器声明
  • 必须实现 build() 方法,返回UI结构
  • 无 @Entry 修饰:纯自定义组件,仅供其他页面引用
  • 支持自定义属性、事件,实现父子组件通信

3.2.2. 基础自定义组件

// 自定义按钮组件
@Component
export  struct CustomBtn {
  build() {
    Button("自定义按钮")
      .width(200)
      .height(40)
      .backgroundColor('#007DFF')
      .fontColor(Color.White)
  }
}

// 页面入口

//导入组件
import {CustomBtn} from '../components/CustomBtn'
@Entry
@Component
struct CustomComponentDemo {
  build() {
    Column({ space: 20 }) {
      // 引用自定义组件
      CustomBtn()
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

3.2.3. 可传参自定义组件(@Prop)

通过 @Prop 接收父组件传值,实现组件动态适配,是业务开发高频用法。

@Component
export struct CustomText {
  // 接收父组件传入参数
  @Prop msg: string
  @Prop fontSize: number
  build() {
    Text(this.msg)
      .fontSize(this.fontSize)
      .fontColor(Color.Black)
  }
}

页面使用

import {CustomText} from '../components/CustomText'

  build() {
    Column({ space: 15 }) {
      // 给自定义组件传参
    CustomText({
          msg:'test',
          fontSize:40
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

3.3.ArkUI 常用核心组件详解

3.3.1 布局容器组件

1.Column(垂直布局)
垂直排列子组件,是最常用的基础布局,子组件从上到下依次排布。
核心属性:space(子组件间距)、justifyContent(主轴对齐)、alignItems(交叉轴对齐)

  build() {
    // 垂直布局,子组件间距20
    Column({ space: 20 }) {
      Text("第一行文字").fontSize(20)
      Text("第二行文字").fontSize(20)
      Button("垂直布局按钮").width(150)
    }
    .width('100%')
    .height(300)
    .backgroundColor('#F5F5F5')
    // 主轴居中
    .justifyContent(FlexAlign.Center)
    // 交叉轴居中
    .alignItems(HorizontalAlign.Center)
  }

  1. Row(水平布局)
    水平排列子组件,子组件从左到右依次排布,适合横向排列按钮、标签、输入框等。
  build() {
    Row({ space: 15 }) {
      Button("按钮1").width(100)
      Button("按钮2").width(100)
      Button("按钮3").width(100)
    }
    .width('100%')
    .height(200)
    .backgroundColor('#F5F5F5')
    .justifyContent(FlexAlign.Center)
  }
  1. Flex(弹性布局)
    弹性布局,兼容水平/垂直排列,支持子组件自适应拉伸、均分空间,适配性比Row/Column更强。
build() {
    Flex({ wrap:           FlexWrap.Wrap,justifyContent:FlexAlign.SpaceAround }) {
      ForEach([1,2,3,4,5], (item: number) => {
        Text(`标签${item}`)
          .width(100)
          .height(40)
          .backgroundColor('#E8F4FF')
          .textAlign(TextAlign.Center)
      })
    }
    .width('100%')
    .padding(10)
    .height(200)
  }
  1. Grid(网格布局)
    网格布局,用于多行多列规整排列内容,适合相册、商品列表、功能菜单等场景。
 build() {
    Grid() {
      ForEach([1,2,3,4,5,6], (item: number) => {
        GridItem(){
          Text(`网格${item}`)
            .width('100%')
            .height(80)
            .backgroundColor('#F0F7FF')
            .textAlign(TextAlign.Center)
            .fontSize(18)
        }

      })
    }
    // 每行3列,均分宽度
    .columnsTemplate('1fr 1fr 1fr')
    .rowsGap(10)
    .columnsGap(10)
    .padding(10)
  }
  1. Stack(堆叠布局)
    层叠布局,子组件层层堆叠,后写的组件覆盖先写的,适合实现悬浮按钮、文字叠加、蒙版等效果。
  build() {
    Stack() {
      // 底层背景
      Rect().width(200).height(200).fill('#87CEEB')
      // 上层文字
      Text("堆叠布局")
        .fontSize(22)
        .fontColor(Color.White)
        .fontWeight(FontWeight.Bold)
    }
    .alignContent(Alignment.Center)
  }
  1. Scroll(滚动容器)
    单一滚动容器,当子组件内容超出屏幕高度/宽度时,支持滑动查看,适合长文本、长页面。
 contents:string[] = ['世界杯','欧洲杯','亚洲杯','中超','英超']
  build(){

    Scroll() {
      Column({ space: 10 }) {
        ForEach(this.contents, (item:String,index:number)=>{
          Text(`滚动内容-第${index+1}行:${item}`).fontSize(18)
        })
      }
      .padding(10)
    }
    .width('100%')
    .height(100)
  }
  1. List(高性能列表)
    鸿蒙官方高性能列表组件,按需渲染、复用Item,适合大量数据列表(优于Scroll),是开发列表的首选组件。
 private listData: string[] = ["列表条目1","列表条目2","列表条目3","列表条目4","列表条目5"]
  build() {
    List() {
      ForEach(this.listData, (item: string, index: number) => {
        ListItem() {
          Text(item)
            .width('100%')
            .height(60)
            .fontSize(18)
            .padding({left:15})
        }
      })
    }
    .width('100%')
    .height(200)
    .divider({strokeWidth:1, color:'#EEEEEE'})//分割线
  }

3.3.2. 基础内容组件

  1. Text(文本组件)
    用于展示文字内容,支持字体、颜色、大小、行高、超长省略、换行等全部文本样式。
   Column({ space: 15 }) {
      // 基础文本
      Text("基础文本")
        .fontSize(24)
        .fontColor(Color.Black)
      // 加粗、行高、颜色
      Text("自定义样式文本")
        .fontSize(22)
        .fontColor('#007DFF')
        .fontWeight(FontWeight.Bold)
        .lineHeight(30)
      // 超长文本省略
      Text("超长文本测试超长文本测试超长文本测试")
        .width(200)
        .textOverflow({overflow:TextOverflow.Ellipsis})
        .maxLines(1)
    }
    .padding(20)
  1. Image(图片组件)
    展示本地/网络图片,支持缩放、圆角、裁剪、占位图等,是页面配图核心组件。
 Column({ space: 20 }) {
      // 网络图片
      //Image('https://baikebcs.bdimg.com/baike-react/common/logo-baike.svg')
      // 加载 resources/base/media/ 目录下的图片
      Image($r('app.media.startIcon'))
        .width(50)
        .height(50)
        // 等比例填充
        .objectFit(ImageFit.Cover)
        // 圆角
        .borderRadius(10)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
  1. TextInput(输入框组件)
    接收用户文字输入,支持单行/多行、密码模式、占位符、输入监听,用于登录、搜索、表单场景。
    Column({ space: 20 }) {
      // 普通输入框
      TextInput({placeholder:"请输入用户名"})
        .width(300)
        .height(45)
        .fontSize(18)
        .border({width:1, color:'#EEEEEE'})
        .borderRadius(8)

      // 密码输入框
      TextInput({placeholder:"请输入密码"})
        .width(300)
        .height(45)
        .type(InputType.Password)
        .border({width:1, color:'#EEEEEE'})
        .borderRadius(8)
    }
    .width('100%')
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
  
  1. Button(按钮组件)
    交互按钮,支持填充、描边、文本按钮,绑定点击事件,是核心交互组件。
    Column({ space: 15 }) {
      // 填充按钮
      Button("确认提交")
        .width(200)
        .height(44)
        .backgroundColor('#007DFF')
        .onClick(()=>{
          console.log("点击确认按钮")
        })

      // 描边按钮
      Button("取消")
        .width(200)
        .height(44)
        .backgroundColor(Color.Transparent)
        .border({width:1, color:'#007DFF'})
        .fontColor('#007DFF')
    }
    .width('100%')
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
  1. Slider(滑块组件)
    滑动选择数值组件,用于音量调节、亮度调节、进度选择等场景,支持数值监听。
  @State value: number = 50
  build() {
    Column({ space: 20 }) {
      Text(`当前数值:${this.value}`).fontSize(20)
      Slider({
        value: this.value,
        min: 0,
        max: 100,
        step: 1
      })
      .width(300)
      .onChange((val: number) => {
        this.value = val
      })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
  }

3.4. 组件多态样式

多态样式:组件可根据不同状态(默认、按压、禁用、聚焦)自动切换样式,无需手动判断状态,简化交互样式开发。
核心API:.stateStyles()
支持状态:normal(默认)、pressed(按压)、disabled(禁用)、focused(聚焦)

3.4.1. 多态样式完整示例

@State isDisable: boolean = false

  // 使用装饰器 @Styles ,
  @Styles//默认样式
  normalStyle() {
    .backgroundColor('#005FCB')
  }

  @Styles//挤压样式
  pressedStyle() {
    .backgroundColor('#007DFF')
  }

  @Styles//禁用样式
  disabledStyle(){
    .backgroundColor('#CCCCCC')
  }
  build() {
    Column({ space: 30 }) {
      // 多态按钮
      Button("多态样式按钮")
        .width(220)
        .height(48)
        .stateStyles({
          normal: this.normalStyle,
          pressed: this.pressedStyle,
          disabled: this.disabledStyle
        })
        .enabled(!this.isDisable)

      // 切换禁用状态按钮
      Button("切换禁用状态")
        .width(220)
        .onClick(()=>{
          this.isDisable = !this.isDisable
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

3.5.双向数据绑定

3.5.1. 核心概念

单向绑定:数据变→视图变,视图变→数据不变(普通 @State 绑定)
双向绑定:数据和视图互相联动,视图修改自动同步数据,数据修改自动更新视图
ArkUI 双向绑定核心语法:$$

3.5.2. 基础双向绑定(TextInput)

输入框输入内容,自动同步变量;修改变量,输入框内容自动更新。

  // 定义响应式数据
  @State inputText: string = ""

  build() {
    Column({ space: 20 }) {
      // 双向数据绑定 $$
      TextInput({text: $$this.inputText, placeholder:"请输入内容"})
        .width(300)
        .height(45)
        .border({width:1, color:'#EEE'})
        .borderRadius(8)

      // 实时展示绑定数据
      Text(`实时输入内容:${this.inputText}`).fontSize(20)

      // 测试修改数据,反向更新视图
      Button("清空内容")
        .width(150)
        .onClick(()=>{
          this.inputText = ""
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

3.5.3. 滑块双向绑定实战

//定义响应式数据

@State num: number = 30
  build() {
    Column({ space: 20 }) {
      Text(`当前数值:${this.num}`).fontSize(22)
      // 滑块双向绑定
      Slider({value: $$this.num, min:0, max:100, step:1})
        .width(300)

      Button("重置为50")
        .onClick(()=>{
          this.num = 50
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

3.5.4. 双向绑定核心总结

  • 语法:$$变量名 实现双向绑定
  • 依赖:变量必须被@State 装饰为响应式数据
  • 场景:输入框、滑块、开关等可交互修改视图的组件
  • 优势:无需手动监听事件赋值,代码极简,数据视图自动同步
    六、本章总结
  1. ArkUI 基于声明式开发,组件是页面核心单元,分为布局、基础、自定义组件
  2. 11大常用组件覆盖日常90%页面开发,List、Flex、Scroll为高频核心布局
  3. 自定义组件实现代码复用,支持父子传参,是工程化开发基础
  4. 多态样式实现组件不同状态自动切换样式,提升交互体验
  5. 双向数据绑定 $$ 实现视图与数据双向联动,简化交互逻辑

4. ArkUI-高级

本章将深入探讨 ArkUI 的高级特性,包括渲染控制语句、组件样式复用(@Styles@Extend)以及组件结构复用(@Builder@BuilderParam)。掌握这些特性将帮助你编写更优雅、可维护的鸿蒙应用代码。

4.1. 渲染控制语句

ArkUI 提供了多种渲染控制语句,用于根据条件或循环动态构建 UI。

4.1.1. 条件渲染:if/else

根据条件决定是否渲染某个组件或分支。

  @State isLoggedIn: boolean = false
  @State userRole: string = 'guest' // guest, user, admin

  build() {
    Column({ space: 20 }) {
      // 基础条件渲染
      if (this.isLoggedIn) {
        Text('欢迎回来!')
          .fontSize(18)
          .fontColor('#4ECDC4')
      } else {
        Text('请先登录')
          .fontSize(18)
          .fontColor('#FF6B6B')
      }

      // 多分支条件渲染
      if (this.userRole === 'admin') {
        Row({ space: 10 }) {
          Button('管理用户')
          Button('系统设置')
          Button('数据统计')
        }
      } else if (this.userRole === 'user') {
        Row({ space: 10 }) {
          Button('个人资料')
          Button('我的订单')
        }
      } else {
        Text('请注册成为会员')
          .fontColor('#999')
      }

      // 控制按钮
      Button(this.isLoggedIn ? '退出登录' : '登录')
        .onClick(() => {
          this.isLoggedIn = !this.isLoggedIn
        })
      
      Button('切换角色')
        .onClick(() => {
          const roles = ['guest', 'user', 'admin']
          const currentIndex = roles.indexOf(this.userRole)
          this.userRole = roles[(currentIndex + 1) % roles.length]
        })
    }
    .width('100%')
    .padding(20)
  }

4.1.2 循环渲染:ForEach

基于数组数据重复渲染相同结构的组件。
基本语法结构

ForEach(
  数据源数组,
  (item) => { // 每一项渲染
    // 你的 UI 组件
  },
  (item) => 唯一标识 // 必须写!
)

商品项组件

 @Component
export struct ProductItem {

  @Prop product: Product

  build() {
    Row({ space: 15 }) {
      // 商品图标
      Column() {
        Image($r('app.media.startIcon'))
          .width(50)
          .height(50)
          .borderRadius(8)
      }

      // 商品信息
      Column() {
        Text(this.product.name)
          .fontSize(16)
          .fontWeight(FontWeight.Medium)

        Text(`¥${this.product.price}`)
          .fontSize(14)
          .fontColor('#FF6B6B')

        if (!this.product.inStock) {
          Text('缺货')
            .fontSize(12)
            .fontColor('#999')
        }
      }
      .layoutWeight(1)
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .padding(10)
    .backgroundColor('#F5F5F5')
    .borderRadius(8)
  }


}

export  interface Product{
  id: number,
  name: string,
  price: number,
  inStock: boolean
};

商品列表

import {ProductItem} from '../components/ProductItem'
import {Product} from '../components/ProductItem'
@Entry
@Component
struct Index {
  @State products: Array<Product> = [
    {
      id: 1,
      name: '鸿蒙手机',
      price: 3999,
      inStock: true
    },
    {
      id: 2,
      name: '智能手表',
      price: 1299,
      inStock: true
    },
    {
      id: 3,
      name: '无线耳机',
      price: 899,
      inStock: false
    },
    {
      id: 4,
      name: '平板电脑',
      price: 2999,
      inStock: true
    }
  ]

  build() {
    Column({ space: 20 }) {
      // 标题栏
      Row() {
        Text('商品列表')
          .fontSize(24)
          .fontWeight(FontWeight.Bold)
        Blank()
      }
      .width('100%')
      // 使用 ForEach 渲染商品列表
      ForEach(
        this.products,
        (product:Product) => {
          ProductItem({
            product: product
          })
        } ,(product:Product) => product.id.toString()
      )
    } .width('100%')
    .height('100%')
    .padding(20)
  }
}

4.2 组件样式复用

4.2.1 @Styles 装饰器:通用样式复用

定义:@Styles 后面跟方法名
使用:组件后面直接写 .样式名()

 @Styles
 commonStyle() {
  .backgroundColor(Color.White)
  .borderRadius(12)
  .padding(15)
  .shadow({ radius: 6, color: '#E0E0E0', offsetY: 2 })
 }


 build() {
  Scroll() {
   Column({ space: 16 }) {
    // 应用样式
    Column() {
     Text('卡片标题')

     Text('这是卡片内容,应用了统一的卡片样式和文本样式')
      .fontSize(14)
      .fontColor('#666')
      .margin({ top: 8 })
    }
    .commonStyle()

    // 带参数的样式使用
    Row({ space: 12 }) {
     Button('主要按钮')
      .fontColor(Color.Red)

     Button('危险按钮')
      .fontColor(Color.Brown)
    }
    .width('100%')
    .justifyContent(FlexAlign.Center)
    .commonStyle()

   }
   .padding(20)
  }
 }

4.2.2 @Extend 装饰器

@Extend:给某一种特定组件(Button/Text/Column)扩展专属样式 + 事件
一句话:@Extend = 专属定制皮肤 + 点击事件
@Extend定义 到组件外面、文件最上方(必须全局)
核心语法(必须记住)

// 给 Text 扩展样式
@Extend(Text) 
function 样式名() {
  .fontSize(20)
  .fontColor(Color.Red)
  .onClick(() => {
    console.log('Text 被点击了')
  }) 
}
@Extend(Text)
function title() {
  .fontSize(18)
  .fontWeight(FontWeight.Bold)
  .fontColor('#333')
  .margin({ bottom: 4 })
}

Text('测试').title()

4.2.3 @Styles vs @Extend 对比

核心区别一句话总结
@Styles:通用样式函数,可以给任意组件使用,只能定义基础样式属性。
@Extend:专属组件扩展,只能给指定单个组件使用,能定义专属属性 + 事件 + 样式。

4.3 组件结构复用

4.3.1 @Builder 装饰器:UI 结构复用

@Builder 是「轻量级可复用 UI 片段」,能封装一整段结构 + 样式 + 事件,还能传参,比 @Extend/@Styles 强很多。

  • 把重复的一段 UI(不管几个组件)抽成函数
  • 可传参、可包含布局 / 组件 / 样式 / 事件
  • 分两种:全局 Builder、组件内私有 Builder

1.全局Builder
定义全局Builder

@Builder
export function GlobalCard(title: string, content: string) {
  Column({ space: 8 }) {
    // 标题
    Text(title)
      .fontSize(18)
      .fontWeight(FontWeight.Bold)
      .fontColor('#000')

    // 内容
    Text(content)
      .fontSize(14)
      .fontColor('#666')

    // 按钮
    Button('查看详情')
      .width('100%')
      .height(40)
      .backgroundColor('#317aff')
      .onClick(() => {
        console.log('全局卡片点击:', title)
      })
  }
  .width('100%')
  .padding(16)
  .backgroundColor('#f7f8fa')
  .borderRadius(12)
  .shadow({ radius: 4, color: '#00000010' })
}

使用全局Builder

import {GlobalCard} from '../common/builder/GlobalCard'
GlobalCard('my','好好学习')

2.组件内私有 Builder

// 私有Builder
  @Builder
  private MyCard(title: string, msg: string) {
    Column() {
      Text(title).fontSize(16).fontWeight(FontWeight.Bold)
      Text(msg).fontSize(14).fontColor('#666')
    }
    .padding(10)
    .backgroundColor('#f5f5f5')
    .borderRadius(8)
  }

  build() {
    Column() {
      Text('Hello World')
      // 调用私有Builder
      this.MyCard('我是卡片', '这是卡片内容')
    }

  }

4.3.2 @BuilderParam:组件内容定制(插槽)

@BuilderParam 就是 ArkTS 里的 “插槽 slot”:
在自定义组件里留一个 “位置”,让父组件传入任意 UI 结构来填充这个位置。
一句话:组件壳子不变,内容由外部定制。

1.最简单案例(单插槽)
子组件(定义插槽)

// 自定义卡片组件
@Component
export struct Card {
  // 默认内容
  @Builder
  defaultContent() {
    Text('默认内容').fontColor('#999')
  }

  // 1. 默认插槽:没有传就用默认内容
  @BuilderParam content: () => void = this.defaultContent

  build() {
    Column() {
      Text('我是卡片标题').fontSize(18).fontWeight(FontWeight.Bold)
      // 2. 插槽位置:调用 BuilderParam
      this.content()
    }
    .padding(16)
    .backgroundColor('#f5f5f5')
    .borderRadius(12)
  }
}

父组件(使用组件,传内容)

import {Card} from '../common/components/Card'
  Column({ space: 20 }) {
      // 方式1:不传内容 → 显示默认内容
      Card()

      // 方式2:尾随闭包 → 自定义插槽内容(最常用)
      Card() {
        Text('我是外部传入的内容').fontColor('#333')
        Button('点我')
          .margin({ top: 8 })
          .onClick(() => console.log('点击'))
      }
    }
    .padding(20)

重点:只有一个 @BuilderParam 时,才能用尾随闭包 { ... } 直接传 UI。

2.多插槽案例(头部 + 内容 + 底部)
子组件(多个 BuilderParam)

@Component
export struct Layout {
  //默认内容
  @Builder defHeader() { Text('默认头部') }
  @Builder defMain() { Text('默认主体') }
  @Builder defFooter() { Text('默认底部') }
  
  // 多个插槽
  @BuilderParam header: () => void = this.defHeader
  @BuilderParam main: () => void = this.defMain
  @BuilderParam footer: () => void = this.defFooter



  build() {
    Column() {
      this.header()//header插槽位置
      Divider()
      this.main()//main插槽位置
      Divider()
      this.footer()//footer插槽位置
    }
    .width('100%')
    .height(300)
    .border({ width: 1, color: '#eee' })
  }
}

父组件(给每个插槽传 Builder)

import {Layout} from '../common/components/Layout'
// 父组件定义多个 Builder 指定插槽内容
  @Builder
  myHeader() {
    Text('自定义头部').fontSize(20).fontWeight(FontWeight.Bold)
  }

  @Builder
  myMain() {
    Text('自定义主体内容')
    Image($r('app.media.startIcon'))
  }

  @Builder
  myFooter() {
    Button('提交')
  }

Column({ space: 20 }) {
      // 多插槽必须用参数方式传,不能尾随闭包
      Layout({
        header: this.myHeader,
        main: this.myMain,
        footer: this.myFooter
      })
    }
    .padding(20)

4.4 综合实战:图书卡片

综合运用本章所学知识,构建一个功能完善的商品卡片组件。
1.图书组件

//图书数据结构
export interface Book {
  id: string
  name: string
  image: Resource
  tags: string[]
  isHot?: boolean
  isNew?: boolean
}
@Component
export struct BookCard {
  @Prop book: Book;
  @BuilderParam extraContent?: () => void  // 额外内容插槽
  @BuilderParam actionButton?: () => void   // 操作按钮插槽

  // 默认操作按钮
  @Builder
  defaultActionButton() {
    Button('收藏')
      .fontSize(12)
      .backgroundColor('#FF6B6B')
      .borderRadius(20)
      .height(32)
      .padding({ left: 16, right: 16 })
  }

  // 样式复用
  @Styles
  tagStyle() {
    .padding({ left: 6, right: 6, top: 2, bottom: 2 })
    .borderRadius(4)
  }


  build() {
    Column() {
      // 图片区域
      Stack({ alignContent: Alignment.TopStart }) {
        Image(this.book.image)
          .width('100%')
          .height(180)
          .objectFit(ImageFit.Fill)

        // 标签徽章
        if (this.book.isHot) {
          Text('热门')
            .tagStyle()
            .backgroundColor('#FF6B6B')
            .fontColor(Color.White)
            .margin({ top: 8, left: 8 })
        } else if (this.book.isNew) {
          Text('新书')
            .tagStyle()
            .backgroundColor('#4ECDC4')
            .fontColor(Color.White)
            .margin({ top: 8, left: 8 })
        }
      }

      // 信息
      Column() {
        Text(this.book.name)
          .fontSize(14)
          .fontWeight(FontWeight.Medium)
          .maxLines(2)
          .textOverflow({ overflow: TextOverflow.Ellipsis })


        // 标签行
        Row({ space: 6 }) {
          ForEach(this.book.tags, (tag: string,index:number) => {
            Text(tag)
              .tagStyle()
              .backgroundColor('#F0F0F0')
              .fontColor('#666')
          },(index:number)=> index.toString())
        }
        .margin({ top: 8 })

        // 额外内容插槽
        if (this.extraContent) {
          this.extraContent()
        }
      }
      .padding(12)
      .alignItems(HorizontalAlign.Center)
      Blank()
      // 操作按钮
      Row() {
        if( this.actionButton) {
          this.actionButton()
        } else{
          this.defaultActionButton()
        }

      }
      .padding(12)
      .borderRadius({ bottomLeft: 12, bottomRight: 12 })
      .backgroundColor('#FAFAFA')
    }
    .width('100%')
    .backgroundColor(Color.White)
    .borderRadius(12)
    .shadow({ radius: 4, color: '#E0E0E0', offsetY: 2 })
  }
}

2.图书列表页面

import { Book,BookCard } from '../components/BookCard'

@Entry
@Component
struct BookListPage {
  @State books: Book[] = [
    {
      id: '1',
      name: '万物有光',
      image: $r('app.media.book1'),
      tags: ['医疗', '人文', '教育'],
      isHot: true
    },
    {
      id: '2',
      name: '盗墓笔记·南部档案',
      image: $r('app.media.book2'),
      tags: ['小说', '玄幻'],
      isNew: true
    }
  ]

  @Builder
  likeExtra() {
    Row() {
      Text('❤️')
        .fontSize(10)
        .fontColor('#4ECDC4')
    }
    .margin({ top: 8 })
  }

  @Builder
  addWishListButton() {
    Button('加入心愿清单')
      .fontSize(12)
      .backgroundColor('#4ECDC4')
      .borderRadius(20)
      .height(32)
  }


  build() {
    Column() {
      // 页面标题
      Row() {
        Text('图书推荐')
          .fontSize(24)
          .fontWeight(FontWeight.Bold)

        Blank()

        Text(`共${this.books.length}本图书`)
          .fontSize(14)
          .fontColor('#999')
      }
      .width('100%')
      .padding(16)

      // 商品列表
      Scroll() {
        Column({ space: 16 }) {
          ForEach(this.books, (book: Book) => {
            if (book.id === '1') {
              BookCard({
                book: book,
                extraContent: this.likeExtra,
                actionButton: this.addWishListButton
              })
            } else if (book.id === '2') {
              BookCard({
                book: book,
              })
            }
          })
        }
        .padding(16)
      }
      .scrollBar(BarState.Auto)
    }
    .backgroundColor('#F5F5F5')
  }
}

4.5 本章小结

本章学习了 ArkUI 的高级特性:

核心知识点回顾

  1. 渲染语句

    • if/else:条件渲染,根据状态动态显示不同内容
    • ForEach:循环渲染,基于数组创建列表
  2. 样式复用

    • @Styles:通用样式复用,适用于所有组件
    • @Extend:为特定组件扩展样式,可以使用组件专属属性
  3. 结构复用

    • @Builder:复用 UI 结构,支持参数传递
    • @BuilderParam:组件内容定制,实现灵活的插槽机制

最佳实践建议

  • 选择合适的复用方式:纯样式用 @Styles,组件专属样式用 @Extend,UI 结构用 @Builder
  • 合理使用插槽@BuilderParam 让组件更灵活,但不要过度使用
  • 保持组件纯净:复用组件的逻辑应该清晰单一

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容