【ArkUI进阶练中学】第12课:应用架构演进与遗留系统迁移

本节目标

  • 理解 FA 模型与 Stage 模型的核心差异,掌握从 FA 迁移到 Stage 的完整流程与自动化工具的使用
  • 掌握 API 废弃迁移的三类处理策略,能够系统性地扫描、排序和替换废弃 API
  • 掌握 JS 到 ArkTS 的语法升级路径,能够将无类型、弱类型的遗留代码重构为强类型代码
  • 掌握渐进式重构与绞杀者模式的核心思想,能够在不中断业务交付的前提下有序迁移遗留系统
  • 掌握增量迁移的节奏控制与风险管控四件套,能够制定可回滚、可灰度、可监控的迁移方案
  • 能够为已有的 FA 模型应用或 JS 遗留项目制定合理的架构演进路线图

一、FA 模型与 Stage 模型的核心差异

1.1 两代开发模型的历史背景

FA 模型(Feature Ability)是 HarmonyOS 早期(API 7 之前)的开发模型,以 FeatureAbility 和 PageAbility 为核心。Stage 模型自 API 8 开始推出,API 9 正式推荐使用,是 HarmonyOS 5 及以后版本的主推模型。官方已明确 FA 模型不再主推,建议迁移至 Stage 模型。

1.2 核心差异对比

维度 FA 模型 Stage 模型
应用单元 FeatureAbility + PageAbility UIAbility + ExtensionAbility
上下文获取 featureAbility.getContext() 组件内 this.context 或 @ohos.app.ability.Context
生命周期 onStart / onActive / onInactive / onStop onCreate / onForeground / onBackground / onDestroy
页面路由 AbilitySlice 栈管理 Navigation 组件(推荐)或 router
包结构 config.json module.json5 + app.json5
性能 冷启动约 350ms 冷启动约 150ms(提升约 57%)

迁移工作量分布:配置迁移约占 5%-10%,Ability 迁移约占 30%-40%,AbilitySlice 迁移约占 40%-50%(工作量最大),资源迁移约占 5%-10%。

1.3 迁移的性能收益

以同一个邮件应用为例:FA 模型冷启动耗时约 350ms,Stage 模型约 150ms,启动性能提升约 57%;页面切换从 70ms 降至 40ms,性能提升约 43%。

二、FA 到 Stage 的迁移步骤

2.1 自动化迁移工具

官方提供迁移工具(FA→Stage Migration Tool)可以完成约 60%-70% 的自动化迁移工作,包括配置迁移、Ability 名称转换和 Slice 结构识别。剩余 30%-40% 的工作需要手动完成,主要是 Navigation 替换 AbilitySlice、状态管理重构和生命周期回调重写。

2.2 迁移步骤详解

第一步:创建新工程

新建一个 Stage 模型的 Empty Ability 工程,Project name 和 Bundle name 必须与原项目保持一致。Compile API 选择 9,Model 选择 Stage。

第二步:拷贝代码目录

将原项目 /entry/src/main/ets/ 下的 pages 目录和其他分包目录统一拷贝到新项目的对应目录下。

第三步:合并生命周期方法

  • 将原项目 app.ets 中的 onCreate 方法合并到新项目的 MainAbility.ts 的 onCreate 中。
  • 将原项目 MainAbility.ts 中的 onDestroy、onWindowStageCreate、onWindowStageDestroy、onForeground、onBackground 方法分别拷贝到新项目对应方法中。

第四步:拷贝资源文件

将原项目 /entry/src/main/resources/ 目录下的所有内容覆盖到新项目对应目录下。

第五步:修改代码引用

  • 所有引用 ts 或 ets 文件的地方删除文件后缀。
  • 修改框架 API 关键字,如将 featureAbility 相关调用替换为 Stage 模型的对应接口。

第六步:修改配置文件

  • 修改 main_pages.json 中的首页文件路径。
  • 修改 module.json5,更新 srcEntrance、mainElement、pages 等字段。如果有 requestPermissions 字段则直接复制。

第七步:签名配置

如果原项目配置了签名,需要在 build-profile.json5 中配置相同的签名。注意:直接拷贝 storePassword 和 keyPassword 可能不生效,需要在 IDE 的签名配置页中重新填写明文密码。

2.3 迁移成本评估

项目复杂度 工作量 人员配置
简单应用(单个 FA + 1-2 个 AbilitySlice) 2-4 周 1 人
中等应用(3-5 个 FA + 少量 PA + 多个 AbilitySlice) 1-2 个月 1-2 人
复杂应用(10+ 个 FA + 多个 PA + 跨应用调用) 3-6 个月 2-4 人

分类工作量:配置迁移(config.json→module.json5)约占 5%-10%,自动化率约 90%;Ability 迁移(FA→UIAbility,PA→ExtensionAbility)约占 30%-40%,自动化率约 70%;AbilitySlice 迁移(Slice→Navigation 页面)约占 40%-50%,自动化率最低,需要手动重构。

三、API 废弃迁移与兼容性适配

3.1 废弃 API 的三类处理策略

有替代 → 迁移到新 API

如旧存储 API 迁移到新 KV Store API。迁移流程:编译告警扫描 → 列出废弃清单 → 按影响排序 → 逐个替换 → 回归验证 → 清告警。

无替代 → 保留 + 封装兼容层

如果废弃 API 没有直接替代,可以在兼容层中封装,对外提供统一接口,内部根据 API 版本调用不同实现。

已移除 → 必须重写

查阅官方迁移指南,按照新 API 的行为语义重写逻辑。

风险提示:新 API 的行为可能与旧 API 存在差异,包括错误码、返回值、时序等,替换后必须跑完整的回归测试。

3.2 运行时 API 版本兼容判断

当组件属性仅在较高 API 版本中支持,但应用需要兼容较低版本设备时,可以使用 deviceInfo.sdkApiVersion 进行运行时判断。

使用 AttributeModifier 进行兼容性适配:

typescript 复制代码
import { deviceInfo } from '@kit.BasicServicesKit';

class MyListModifier implements AttributeModifier<ListAttribute> {
  applyNormalAttribute(instance: ListAttribute): void {
    // 根据设备 API 版本判断是否使用新属性
    if (deviceInfo.sdkApiVersion > 14) {
      instance.backToTop(true);  // API 15+ 才支持的属性
    }
  }
}

@Entry
@Component
struct CompatibilityDemo {
  modifier: MyListModifier = new MyListModifier();

  build() {
    List() {
      // 列表内容
    }
    .height('100%')
    .width('100%')
    .attributeModifier(this.modifier)
  }
}

对于自定义组件的兼容性,例如 @ReusableV2 组件复用新特性在 SDK 版本 5.1.0(18) 提供,为了让应用兼容 API 12 的老设备,需要在 LazyForEach 中根据 deviceInfo.sdkApiVersion 判断使用哪个自定义组件:

typescript 复制代码
import { deviceInfo } from '@kit.BasicServicesKit';

// 在 LazyForEach 中根据 API 版本选择不同组件
LazyForEach(this.data, (item: number, index: number) => {
  ListItem() {
    if (deviceInfo.sdkApiVersion >= 18) {
      ReuseComponentV2({ num: item })  // API 18+ 使用 V2 复用组件
    } else {
      V1Component({ num: item })  // API 18 以下使用 V1 普通组件
    }
  }
}, (item: number) => index.toString())

四、JS 到 ArkTS 的语法升级

4.1 核心约束

从 JS 迁移到 ArkTS 需要满足以下核心约束:

  • 类型标注:所有变量、参数、返回值都必须有类型标注。
  • 禁止 any :不允许使用 any 类型,必须用具体类型或泛型替代。
  • 对象字面量:对象字面量需要有显式的类型上下文。
  • 解构赋值:部分解构赋值场景不被支持。
  • 对象方法:未标注类型的对象方法不被支持。

4.2 升级路径

第一步:开启严格编译模式

在 build-profile.json5 中开启严格模式,收集全部编译报错。

第二步:按错误类型批量修复

将错误分为类型缺失、any 替换、对象字面量等类别,批量处理。引入 interface 和 type 覆盖数据模型。

第三步:编译清零

确保所有编译错误被消除,然后运行完整的回归测试。

第四步:静态检查

使用 Code Linter 逐项清零剩余的代码质量问题。

4.3 典型语法转换示例

typescript 复制代码
// JS 旧代码
function calculateTotal(items) {
  let total = 0;
  for (let i = 0; i < items.length; i++) {
    total += items[i].price * items[i].quantity;
  }
  return total;
}

// ArkTS 新代码
interface CartItem {
  price: number;
  quantity: number;
}

function calculateTotal(items: CartItem[]): number {
  let total: number = 0;
  for (let i = 0; i < items.length; i++) {
    total += items[i].price * items[i].quantity;
  }
  return total;
}

五、渐进式重构与风险管控

5.1 绞杀者模式

老旧项目的架构重构不应采用"推倒重来"的方式,而应采用绞杀者模式(Strangler Pattern) :新功能和新模块使用新架构开发,老模块逐步迁移到新架构,老架构慢慢"被绞杀"直至消失。全程业务不停,系统平滑过渡。

老旧架构的典型特征与目标架构的对应关系:

老旧架构特征 目标架构
全部逻辑在页面 三层分层
全局状态散落 状态管理收敛
直接依赖框架 依赖倒置 + 接口化
模块不分家 按业务域拆分

5.2 增量迁移节奏

每次迭代带一部分迁移,迁移量不超过整体代码量的 20%。改造与需求并行,小步合入,确保每一步都可编译、可测试、可上线。

5.3 风险管控四件套

行为测试先行:迁移前编写契约测试,确保迁移前后行为一致。

灰度发布:新版本小流量验证,观察崩溃率和性能指标。

回滚预案:一键回滚到迁移前版本。迁移后的应用支持回退到 FA 版本,保留 FA 版本的 HAP 包,在应用市场中同时提供 FA 版和 Stage 版两个安装包。

监控兜底:对比迁移前后的崩溃率和性能指标,确保无退化。

5.4 风险矩阵

每项改造评估"影响面 × 概率":高影响高概率的改造需分批拆解、重点回归;低影响的改造可快速批量处理。

六、多元化习题

习题 1(判断题)

题目:FA 模型的代码可以通过官方工具一键转换为 Stage 模型,无需任何手动修改。

答案:错误

解读:官方迁移工具可以完成约 60%-70% 的自动化迁移工作,但剩余 30%-40% 需要手动完成,主要是 Navigation 替换 AbilitySlice、状态管理重构和生命周期回调重写。

习题 2(单选题)

题目:在 FA 模型迁移到 Stage 模型的过程中,哪部分工作量占比最大?

A. 配置迁移(config.json→module.json5)

B. Ability 迁移(FA→UIAbility)

C. AbilitySlice 迁移(Slice→Navigation 页面)

D. 资源文件迁移

答案:C

解读:AbilitySlice 迁移是迁移成本最高的部分,占迁移总工作量的约 40%-50%,因为 AbilitySlice 的架构(共享 FA 生命周期、Slice 栈管理、Slice 间数据传递)需要完全重构为 Navigation 页面架构。

习题 3(多选题)

题目:关于渐进式重构的风险管控,以下说法正确的有(多选):

A. 迁移前应编写契约测试,确保迁移前后行为一致

B. 新版本应直接全量发布,无需灰度验证

C. 需要准备一键回滚到迁移前版本的预案

D. 应对比迁移前后的崩溃率和性能指标

答案:A、C、D

解读:行为测试先行是风险管控的第一道防线,选项 A 正确。灰度发布是小流量验证,不是全量发布,选项 B 错误。回滚预案确保迁移失败时可快速恢复,选项 C 正确。监控兜底对比迁移前后的崩溃率和性能指标,选项 D 正确。

习题 4(代码填空题)

题目:请补全以下代码,在 LazyForEach 中根据 API 版本选择不同的自定义组件。

typescript 复制代码
import { deviceInfo } from '@kit.BasicServicesKit';

LazyForEach(this.data, (item: number) => {
  ListItem() {
    if (deviceInfo.______________ >= 18) {
      ReuseComponentV2({ num: item })
    } else {
      V1Component({ num: item })
    }
  }
}, (item: number) => item.toString())

答案 :sdkApiVersion

解读 :deviceInfo.sdkApiVersion 返回当前设备的 API 版本号,通过版本判断可以决定使用哪个自定义组件,实现运行时兼容性适配。

习题 5(代码改错题)

题目:以下废弃 API 迁移代码存在风险,请指出问题并给出修正方案。

typescript 复制代码
// 旧代码(已废弃)
preferences.get('user_name', (err, val) => {
  console.log('用户名:', val);
});

// 迁移后
import { preferences } from '@kit.ArkData';
const pref = preferences.getPreferencesSync(context, { name: 'user' });
const userName = pref.getSync('user_name', '');
console.log('用户名:', userName);

答案:代码本身没有错误,但存在迁移风险。新 API 的行为可能与旧 API 存在差异,包括错误码、返回值、时序等。修正方案是在替换后补充完整的回归测试,确保新 API 在各种边界条件下(key 不存在、value 为空、存储异常)的行为与旧 API 一致。

习题 6(简答题)

题目:简述 FA 模型迁移到 Stage 模型的完整步骤。

答案:迁移步骤包括:第一步,创建新的 Stage 模型工程,Bundle name 与原项目保持一致。第二步,将原项目 ets 目录下的 pages 和其他分包目录拷贝到新项目。第三步,合并生命周期方法,将原项目 app.ets 的 onCreate 合并到新项目的 MainAbility 中,将 onDestroy、onWindowStageCreate 等方法分别拷贝到对应方法中。第四步,拷贝 resources 目录下的所有资源文件。第五步,修改代码引用,删除文件后缀,替换框架 API 关键字。第六步,修改 main_pages.json 和 module.json5 配置文件。第七步,配置签名,注意 storePassword 和 keyPassword 需要在 IDE 中重新填写。

习题 7(简答题)

题目:简述绞杀者模式的核心思想,以及如何在 HarmonyOS 遗留系统迁移中应用该模式。

答案:绞杀者模式的核心思想是不推倒重来,而是新功能和新模块使用新架构开发,老模块逐步迁移到新架构,老架构慢慢被绞杀直至消失。全程业务不停,系统平滑过渡。在 HarmonyOS 遗留系统迁移中应用该模式:首先识别老旧架构的典型特征(逻辑全在页面、状态散落、直接依赖框架、模块不分家),对应目标架构(三层分层、状态收敛、依赖倒置、按业务域拆分)制定迁移路线。然后按增量迁移节奏,每次迭代带不超过 20% 的代码进行迁移,保持业务持续交付。迁移过程中使用风险管控四件套(行为测试先行、灰度发布、回滚预案、监控兜底)确保风险可控。

习题 8(简答题)

题目:简述 API 废弃迁移的三类处理策略,以及各自的适用场景。

答案:API 废弃迁移的三类处理策略包括:有替代 → 迁移到新 API,适用于官方提供了直接替代 API 的场景,迁移流程为编译告警扫描 → 列出废弃清单 → 按影响排序 → 逐个替换 → 回归验证 → 清告警。无替代 → 保留 + 封装兼容层,适用于废弃 API 没有直接替代的场景,可以在兼容层中封装,对外提供统一接口,内部根据 API 版本调用不同实现。已移除 → 必须重写,适用于 API 已被完全移除的场景,需要查阅官方迁移指南,按照新 API 的行为语义重写逻辑。风险提示:新 API 的行为可能与旧 API 存在差异,包括错误码、返回值、时序等,替换后必须跑完整的回归测试。

七、本节知识点总结

FA 与 Stage 模型差异

FA 模型以 FeatureAbility 和 PageAbility 为主,Stage 模型以 UIAbility 和 ExtensionAbility 为主。Stage 模型通过组件 context 属性获取上下文,采用 Navigation 管理页面路由,性能提升约 57%。

迁移步骤

创建 Stage 工程 → 拷贝代码目录 → 合并生命周期方法 → 拷贝资源文件 → 修改代码引用 → 修改配置文件 → 配置签名。官方迁移工具自动化率约 60%-70%,AbilitySlice 迁移是最大工作量(40%-50%)。

API 废弃迁移

三类处理策略:有替代迁移新 API、无替代封装兼容层、已移除必须重写。使用 deviceInfo.sdkApiVersion 进行运行时版本判断,通过 AttributeModifier 或条件渲染实现兼容性适配。

JS 到 ArkTS 语法升级

核心约束包括类型标注、禁止 any、对象字面量需要类型上下文。升级路径:开启严格编译 → 按错误类型批量修复 → 引入类型定义 → 编译清零 → 运行回归。

渐进式重构

采用绞杀者模式,新功能用新架构,老模块逐步迁移。每次迭代迁移不超过 20% 代码,确保可编译、可测试、可上线。风险管控四件套:行为测试先行、灰度发布、回滚预案、监控兜底。

下节预告

第13课将进入 ArkUI 应用架构与元服务的学习,涵盖元服务的创建、分包策略、卡片开发、以及元服务与应用之间的协同设计。

相关推荐
我想我不够好。1 小时前
装一个纯净的系统
学习
小黄蚁2 小时前
使用LVGL模拟示波器创建正弦函数波形
单片机·学习
海盗12342 小时前
微软技术日报 2026-10-04:26H2 三个已知问题确认,Blazor 补上智能体 UI
人工智能·microsoft·ui·机器人·aigc
辣知2 小时前
辣知·化智71 四川广汉三星堆的金铜贝
学习
天天进步20152 小时前
90天AI接单学习路线:从LLM应用到AI Agent
人工智能·学习
vx_Biye_Design2 小时前
springboot小学生英语学习APP62773-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·python·学习·课程设计
@Mike@2 小时前
13-数据库学习笔记(查询执行处理模型)
数据库·笔记·学习
java1234_小锋3 小时前
shadcn/ui 开源项目,专业打造专业UI
ui·开源
传奇开心果编程3 小时前
【ArkUI进阶练中学】第13课:元服务与卡片开发
学习·ui·华为·harmonyos