惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Engineering at Meta
Engineering at Meta
G
Google Developers Blog
WordPress大学
WordPress大学
M
MIT News - Artificial intelligence
D
DataBreaches.Net
云风的 BLOG
云风的 BLOG
爱范儿
爱范儿
Microsoft Security Blog
Microsoft Security Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Blog — PlanetScale
Blog — PlanetScale
T
Tailwind CSS Blog
S
SegmentFault 最新的问题
阮一峰的网络日志
阮一峰的网络日志
博客园 - 三生石上(FineUI控件)
酷 壳 – CoolShell
酷 壳 – CoolShell
Recent Announcements
Recent Announcements
T
The Blog of Author Tim Ferriss
I
InfoQ
MyScale Blog
MyScale Blog
V
V2EX
B
Blog
罗磊的独立博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - GoGrid

企业级详述:ArkTS 尾随闭包(Trailing Closure) 企业级视角下的 ArkTS 深度解析 企业级面向切面编程(AOP)详解 ArkTS 与 ArkUI 详述 ArkUI框架px2vp等单位转换API修正示例与速查手册 HarmonyOS单位转换API迁移指南与最佳实践 鸿蒙ohos前缀命名规范与资源体系详解 HarmonyOS Toast弹窗企业级开发规范与最佳实践 ArkUI Stage模型企业级实用教程 企业级鸿蒙HAP开发指南 HarmonyOS LazyForEach企业级开发规范与实战指南 ArkTS LazyForEach 企业级技术详解与最佳实践 ArkTS ForEach 企业级技术规范与最佳实践 ArkTS $与this关键字企业级技术详解 DevEco Studio 中文支持与路径配置指南 ArkTS V1 与 V2 装饰器映射关系企业级参考文档 ArkTS @ComponentV2 与 @Component 企业级对比技术文档 ArkTS 声明式开发企业级技术指南 ArkTS struct 企业级技术规范文档 ArkTS 对象字面量企业级技术规范文档 ArkTS @Prop 装饰器技术说明文档 DevEco Studio 预览功能使用指南 ArkTS中.ets后缀含义说明
ArkUI 企业级开发实用教程
GoGrid · 2026-04-08 · via 博客园 - GoGrid

ArkUI 企业级开发实用教程

本教程基于 HarmonyOS NEXT API 12+ 企业级开发标准编写,从实战角度出发,系统讲解 ArkUI 开发的核心技术、工程实践、性能优化与项目架构,帮助开发者快速掌握企业级应用开发能力,可直接作为团队培训教材或开发参考手册。

教程说明

本教程以实战为核心,每个知识点均配套可运行的代码示例和企业级最佳实践,学习过程中建议配合DevEco Studio实际操作,效果更佳。

1.1 环境配置标准流程

1. 安装DevEco Studio:下载最新正式版,安装到纯英文路径,建议路径:D:\DevEco\DevEco Studio\

2. 配置SDK:下载HarmonyOS NEXT SDK,存储到纯英文路径:D:\DevEco\Sdk\NEXT\

3. 配置模拟器:创建API 12+的模拟器,建议选择Phone设备,分辨率1080*2340

4. 环境验证:创建Hello World项目,编译运行到模拟器,确认环境正常

环境检查要点

所有路径必须是纯英文、无空格、无特殊字符;系统用户名如果是中文,必须手动修改SDK路径到非用户目录。

1.2 第一个ArkUI应用开发

// 页面入口组件
@Entry
@ComponentV2
struct HelloWorld {
  @Local count: number = 0;

  build() {
    Column() {
      Text(`Hello ArkUI!`)
        .fontSize(30)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 50, bottom: 30 })

      Text(`点击次数: ${this.count}`)
        .fontSize(20)
        .margin({ bottom: 30 })

      Button("点击我")
        .width(200)
        .height(50)
        .onClick(() => {
          this.count += 1;
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

实战练习 运行上述代码,点击按钮观察计数变化,理解响应式状态的基本工作原理。

2.1 声明式UI基本概念

ArkUI采用声明式开发范式,核心思想是 UI = f(State),即界面是状态的函数,状态变化时UI自动更新。开发者只需描述UI的目标状态,框架自动计算差异并更新。

2.2 常用基础组件

组件

用途

常用属性

Text

显示文本内容

fontSize、fontColor、fontWeight、maxLines

Button

按钮组件

type、onClick、width、height

Image

显示图片

src、objectFit、width、height

TextInput

输入框组件

placeholder、onChange、type

Column/Row

线性布局容器

justifyContent、alignItems、space

List/ListItem

列表容器

space、listDirection、onScroll

2.3 实战:用户登录页面开发

@Entry
@ComponentV2
struct LoginPage {
  @Local username: string = "";
  @Local password: string = "";
  @Local isLoading: boolean = false;

  // 登录逻辑
  handleLogin() {
    if (!this.username || !this.password) {
      prompt.showToast({ message: "请输入用户名和密码" });
      return;
    }

    this.isLoading = true;
    // 模拟登录请求
    setTimeout(() => {
      this.isLoading = false;
      prompt.showToast({ message: "登录成功" });
      // 跳转到首页
      router.pushUrl({ url: "pages/home" });
    }, 1500);
  }

  build() {
    Column() {
      // 标题
      Text("用户登录")
        .fontSize(28)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 80, bottom: 50 })

      // 用户名输入
      TextInput({ placeholder: "请输入用户名" })
        .width('80%')
        .height(50)
        .margin({ bottom: 20 })
        .onChange((value) => {
          this.username = value;
        })

      // 密码输入
      TextInput({ placeholder: "请输入密码", type: InputType.Password })
        .width('80%')
        .height(50)
        .margin({ bottom: 40 })
        .onChange((value) => {
          this.password = value;
        })

      // 登录按钮
      Button(this.isLoading ? "登录中..." : "登录")
        .width('80%')
        .height(50)
        .enabled(!this.isLoading)
        .onClick(() => {
          this.handleLogin();
        })
    }
    .width('100%')
    .height('100%')
    .alignItems(HorizontalAlign.Center)
    .backgroundColor('#f5f5f5')
  }
}

实战练习 实现登录页面,添加"忘记密码"和"注册账号"链接,实现表单验证逻辑。

3.1 布局体系概述

ArkUI提供多层次的布局方案,满足不同场景需求:

• 线性布局Column/Row,最简单常用的布局方式

• 层叠布局Stack,子组件堆叠排列

• 相对布局RelativeContainer,复杂对齐场景

• 弹性布局Flex,弹性分配空间

• 网格布局Grid,网格排列场景

3.2 最佳布局实践

• 优先使用Column/Row,实现简单布局

• 复杂对齐场景使用RelativeContainer,减少布局嵌套

• 布局嵌套层级不超过5层,避免性能问题

• 使用自适应单位vp/fp,适配不同屏幕尺寸

• 避免在Scroll组件中嵌套List,会导致滚动冲突

3.3 实战:商品卡片布局

@ComponentV2
struct ProductCard {
  @Param product: Product;

  build() {
    Column() {
      // 商品图片
      Stack({ alignContent: Alignment.TopEnd }) {
        Image(this.product.imageUrl)
          .width('100%')
          .height(180)
          .objectFit(ImageFit.Cover)

        // 热销标签
        if (this.product.isHot) {
          Text("热销")
            .fontSize(12)
            .fontColor(Color.White)
            .backgroundColor(Color.Red)
            .padding({ left: 8, right: 8, top: 2, bottom: 2 })
            .margin(8)
            .borderRadius(4)
        }
      }
      .borderRadius({ topLeft: 8, topRight: 8 })

      // 商品信息
      Column({ space: 8 }) {
        Text(this.product.name)
          .fontSize(14)
          .maxLines(2)
          .textOverflow({ overflow: TextOverflow.Ellipsis })

        Row({ space: 4, alignItems: VerticalAlign.Center }) {
          Text(`¥${this.product.price.toFixed(2)}`)
            .fontSize(16)
            .fontColor(Color.Red)
            .fontWeight(FontWeight.Bold)

          if (this.product.originalPrice) {
            Text(`¥${this.product.originalPrice.toFixed(2)}`)
              .fontSize(12)
              .fontColor('#999')
              .decoration({ type: TextDecorationType.LineThrough })
          }
        }

        Row({ justifyContent: FlexAlign.SpaceBetween, alignItems: VerticalAlign.Center }) {
          Text(`${this.product.sales}人已买`)
            .fontSize(12)
            .fontColor('#999')

          Button("加入购物车")
            .type(ButtonType.Capsule)
            .fontSize(10)
            .height(24)
            .padding({ left: 8, right: 8 })
            .onClick(() => {
              // 加入购物车逻辑
            })
        }
      }
      .padding(10)
    }
    .width('48%')
    .borderRadius(8)
    .backgroundColor(Color.White)
    .shadow({ radius: 4, color: '#1A000000', offsetX: 0, offsetY: 2 })
  }
}

4.1 状态管理最佳实践

• 状态最小化:仅将需要触发UI更新的数据标记为响应式状态

• 状态下推:状态尽量保存在最底层的使用组件,减少影响范围

• 单一数据源:相同状态只保存在一处,避免多数据源不一致

• 状态不可变性:修改状态时生成新对象,避免直接修改原对象

4.2 组件通信方式

通信场景

推荐方式

示例

→子传参

@Param

父组件传递属性,子组件@Param接收

→父通知

@Event

子组件触发事件,父组件监听回调

跨层级传递

@Provider/@Consumer

祖先组件提供,后代组件消费

全局状态

AppStorage/全局状态管理库

应用级状态共享

4.3 实战:Todo List应用开发

// 待办项数据结构
interface TodoItem {
  id: number;
  content: string;
  completed: boolean;
}

// 待办项组件
@ComponentV2
struct TodoItemCard {
  @Param item: TodoItem;
  @Event onToggle: (id: number) => void;
  @Event onDelete: (id: number) => void;

  build() {
    Row({ space: 10, justifyContent: FlexAlign.SpaceBetween }) {
      Row({ space: 10, alignItems: VerticalAlign.Center }) {
        Toggle({ type: ToggleType.Checkbox, isOn: this.item.completed })
          .selectedColor(Color.Green)
          .onChange((isOn) => {
            this.onToggle(this.item.id);
          })

        Text(this.item.content)
          .fontSize(16)
          .decoration({ type: this.item.completed ? TextDecorationType.LineThrough : TextDecorationType.None })
          .fontColor(this.item.completed ? '#999' : '#333')
      }

      Button("删除")
        .type(ButtonType.Capsule)
        .fontSize(12)
        .height(28)
        .backgroundColor(Color.Red)
        .onClick(() => {
          this.onDelete(this.item.id);
        })
    }
    .width('100%')
    .padding(15)
    .borderRadius(8)
    .backgroundColor(Color.White)
    .margin({ bottom: 10 })
  }
}

// 主页面
@Entry
@ComponentV2
struct TodoListPage {
  @Local todoList: TodoItem[] = [];
  @Local inputText: string = "";

  // 添加待办
  addTodo() {
    if (!this.inputText.trim()) {
      prompt.showToast({ message: "请输入待办内容" });
      return;
    }

    const newTodo: TodoItem = {
      id: Date.now(),
      content: this.inputText.trim(),
      completed: false
    };

    this.todoList = [...this.todoList, newTodo];
    this.inputText = "";
  }

  // 切换完成状态
  toggleTodo(id: number) {
    this.todoList = this.todoList.map(item => {
      if (item.id === id) {
        return { ...item, completed: !item.completed };
      }
      return item;
    });
  }

  // 删除待办
  deleteTodo(id: number) {
    this.todoList = this.todoList.filter(item => item.id !== id);
  }

  build() {
    Column() {
      // 顶部输入区域
      Row({ space: 10 }) {
        TextInput({ placeholder: "输入待办事项", text: this.inputText })
          .layoutWeight(1)
          .height(44)
          .onChange((value) => {
            this.inputText = value;
          })
          .onSubmit(() => {
            this.addTodo();
          })

        Button("添加")
          .width(80)
          .height(44)
          .onClick(() => {
            this.addTodo();
          })
      }
      .width('100%')
      .padding(15)
      .backgroundColor(Color.White)

      // 统计信息
      Row({ justifyContent: FlexAlign.SpaceBetween }) {
        Text(`总共: ${this.todoList.length} `)
          .fontSize(14)
        Text(`已完成: ${this.todoList.filter(item => item.completed).length} `)
          .fontSize(14)
      }
      .width('100%')
      .padding(15)

      // 待办列表
      List({ space: 10 }) {
        ForEach(this.todoList, (item: TodoItem) => {
          ListItem() {
            TodoItemCard({
              item: item,
              onToggle: (id) => this.toggleTodo(id),
              onDelete: (id) => this.deleteTodo(id)
            })
          }
        }, (item: TodoItem) => item.id.toString())
      }
      .layoutWeight(1)
      .padding(15)
      .backgroundColor('#f5f5f5')
    }
    .width('100%')
    .height('100%')
  }
}

实战练习 实现Todo List的全选、批量删除、本地持久化存储功能。

5.1 网络请求封装

// 网络请求工具类
class HttpUtil {
  private static baseUrl: string = "https://api.example.com";
  private static timeout: number = 30000;

  // GET请求
  static async get(url: string, params?: Record): Promise { // 拼接查询参数 if (params) { const queryString = new URLSearchParams(params).toString(); url += `?${queryString}`; } const response = await fetch(`${this.baseUrl}${url}`, { method: 'GET', headers: this.getHeaders(), timeout: this.timeout }); return this.handleResponse(response); } // POST请求 static async post(url: string, data?: any): Promise { const response = await fetch(`${this.baseUrl}${url}`, { method: 'POST', headers: this.getHeaders(), body: JSON.stringify(data), timeout: this.timeout }); return this.handleResponse(response); } // 获取请求头 private static getHeaders(): Record { const headers: Record = { 'Content-Type': 'application/json' }; // 添加token const token = AppStorage.get('token'); if (token) { headers['Authorization'] = `Bearer ${token}`; } return headers; } // 处理响应 private static async handleResponse(response: Response): Promise { const result = await response.json() as ApiResponse; if (result.code === 200) { return result.data; } else if (result.code === 401) { // token过期,跳转到登录页 router.clear(); router.pushUrl({ url: "pages/login" }); throw new Error("登录已过期,请重新登录"); } else { prompt.showToast({ message: result.message || "请求失败" }); throw new Error(result.message || "请求失败"); } } } // API响应类型 interface ApiResponse { code: number; message: string; data: T; }

5.2 实战:商品列表页开发

// 商品API
class ProductApi {
  // 获取商品列表
  static getProductList(params: { page: number; pageSize: number; keyword?: string }): Promise { return HttpUtil.get("/product/list", params); } } @Entry @ComponentV2 struct ProductListPage { @Local productList: Product[] = []; @Local page: number = 1; @Local pageSize: number = 20; @Local isLoading: boolean = false; @Local hasMore: boolean = true; @Local keyword: string = ""; aboutToAppear() { this.loadData(); } // 加载数据 async loadData(isRefresh: boolean = false) { if (this.isLoading) return; this.isLoading = true; try { const params = { page: isRefresh ? 1 : this.page, pageSize: this.pageSize, keyword: this.keyword }; const result = await ProductApi.getProductList(params); if (isRefresh) { this.productList = result; this.page = 2; } else { this.productList = [...this.productList, ...result]; this.page += 1; } this.hasMore = result.length === this.pageSize; } catch (e) { console.error("加载商品列表失败", e); } finally { this.isLoading = false; } } build() { Column() { // 搜索框 TextInput({ placeholder: "搜索商品", text: this.keyword }) .width('90%') .height(40) .margin(15) .borderRadius(20) .backgroundColor('#f0f0f0') .onChange((value) => { this.keyword = value; }) .onSubmit(() => { this.loadData(true); }) // 商品列表 List({ space: 15 }) { // 下拉刷新 Refresh({ refreshing: this.isLoading && this.page === 1 }) .onRefresh(() => { this.loadData(true); }) // 商品网格 GridRow({ columns: 2, gutter: 15 }) { ForEach(this.productList, (item: Product) => { GridCol() { ProductCard({ product: item }) } }, (item: Product) => item.id.toString()) } .padding(15) // 加载更多 if (this.hasMore && this.productList.length > 0) { ListItem() { Row({ justifyContent: FlexAlign.Center }) { if (this.isLoading) { LoadingProgress() .width(20) .height(20) .margin({ right: 8 }) Text("加载中...") .fontSize(14) .fontColor('#999') } else { Text("上拉加载更多") .fontSize(14) .fontColor('#999') } } .height(50) .onAppear(() => { if (!this.isLoading && this.hasMore) { this.loadData(); } }) } } // 空状态 if (!this.isLoading && this.productList.length === 0) { ListItem() { Column({ space: 10, justifyContent: FlexAlign.Center }) { Image($r('app.media.empty')) .width(100) .height(100) .objectFit(ImageFit.Contain) Text("暂无商品") .fontSize(14) .fontColor('#999') } .height(300) } } } .layoutWeight(1) } .width('100%') .height('100%') .backgroundColor('#f5f5f5') } }

6.1 常用性能优化手段

• 长列表优化:使用LazyForEach配合reuseId实现组件复用,避免使用ForEach渲染大量数据

• 减少重渲染:合理拆分组件,状态最小化,避免不必要的组件重建

• build()方法优化:禁止在build()中执行复杂计算、网络请求、日志输出等操作

• 图片优化:使用合适分辨率的图片,开启内存缓存,及时释放不可见图片

• 资源释放:在aboutToDisappear中清理定时器、事件监听、订阅等资源

6.2 性能优化检查清单

1. ✅ 长列表使用LazyForEach,每个ListItem设置唯一reuseId

2. ✅ build()方法中无复杂计算和副作用操作

3. ✅ 组件嵌套层级不超过5层

4. ✅ 频繁切换的组件使用Visibility控制,而非if/else

5. ✅ 所有定时器、事件监听在aboutToDisappear中正确释放

6. ✅ 图片使用.webp格式,尺寸不超过显示尺寸的2倍

7. ✅ 避免在循环中创建函数和对象

8. ✅ 复杂计算结果使用@Memo或局部变量缓存

7.1 企业级项目目录结构

src/main/ets/
├── common/                  # 公共资源
│   ├── components/          # 基础公共组件
│   ├── utils/               # 工具函数
│   ├── constants/           # 常量定义
│   ├── types/               # 通用类型定义
│   ├── api/                 # 公共API封装
│   └── styles/              # 全局样式
├── features/                # 业务特性模块
│   ├── home/                # 首页模块
│   │   ├── components/      # 首页私有组件
│   │   ├── pages/           # 首页页面
│   │   ├── viewmodels/      # 首页业务逻辑
│   │   ├── api/             # 首页API
│   │   └── types/           # 首页类型定义
│   ├── product/             # 商品模块
│   └── user/                # 用户模块
├── router/                  # 路由配置
├── store/                   # 全局状态管理
└── entryability/            # 应用入口配置

7.2 代码规范

• 组件名、类名使用大驼峰命名,如ProductCard、HttpUtil

• 变量名、方法名使用小驼峰命名,如productList、loadData

• 常量使用全大写下划线分隔,如BASE_URL、PAGE_SIZE

• 所有变量、方法参数、返回值明确类型,禁止使用any类型

• 组件代码行数不超过300行,超过则拆分组件

• 每个组件、类、方法添加JSDoc注释,说明用途、参数、返回值

问题现象

解决方案

状态变化后UI不更新

检查是否使用了响应式装饰器,修改对象/数组时是否生成了新实例

列表滑动卡顿

替换为LazyForEach,为ListItem添加reuseId,优化item布局

页面跳转卡顿

将复杂初始化逻辑移到aboutToAppear,使用异步执行

图片显示异常

检查图片路径是否正确,是否有网络权限,图片格式是否支持

网络请求失败

检查是否配置了网络权限,域名是否在白名单中,参数是否正确

打包失败

检查路径是否包含中文,依赖是否正确安装,配置文件是否有误

学习建议

完成本教程所有实战练习后,建议选择一个实际业务场景(如新闻客户端、电商App、社交应用)进行完整项目开发,在实践中积累经验。遇到问题优先查阅官方文档,或在HarmonyOS开发者社区搜索解决方案。

文档版本:V1.0 | 适配版本:HarmonyOS NEXT API 12+ | 更新日期:2024年4月