本节目标
- 理解 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 应用架构与元服务的学习,涵盖元服务的创建、分包策略、卡片开发、以及元服务与应用之间的协同设计。