| name | ohos-dev-arkui-v1-v2-migration |
| description | Use when migrating OpenHarmony/HarmonyOS ArkUI state management from V1 (@Component, @State/@Prop/@Link/@Provide/@Consume/@Watch/@Observed) to V2 (@ComponentV2, @Local/@Param/@Event/@Provider/@Consumer/@Monitor/@ObservedV2/@Trace), or assessing migration feasibility. Trigger phrases include "迁移V1到V2", "V1V2迁移", "状态管理迁移", "将@Component改为@ComponentV2", "迁移@State/@Prop/@Link到V2", "migrate V1 to V2". Provides automated analysis (component structure, dependency tracing, API version detection, V1/V2 mixing validation), step-by-step migration guidance, and post-migration validation. |
| metadata | {"author":"openharmony","scope":"domain","stage":"development","domain":"arkui","capability":"v1-v2-migration","version":"0.1.0","status":"draft","tags":["arkui","componentv2","state-management","migration"],"related-skills":[]} |
V1 → V2 State Management Migration Skill
Migrate OpenHarmony ArkUI @Component (V1) components to @ComponentV2 (V2).
When to Use
Activate this skill when the user expresses any of the following intents:
- 迁移 V1 组件到 V2 / V1V2 迁移 / 状态管理迁移 (migrate V1 components to V2 / state management migration)
- Change
@Component to @ComponentV2
- Migrate decorators such as
@State/@Prop/@Link to their V2 equivalents
- Analyze whether a component can be migrated to V2 / assess migration risk
Overall Workflow
0. Confirm target → 1. Analyze → 2. Plan → 3. Execute → 4. Verify
Each phase is detailed below. Always proceed in this order; never skip analysis and jump straight to execution.
Step 0: Confirm the Migration Target
Before any analysis, confirm the migration target in the following order.
Case 1: The user only says "V1V2 migration" without a project path
You must first ask the user for the project path, e.g.:
Please provide the HarmonyOS/OpenHarmony project path to migrate.
After receiving the path, proceed to Case 2. Do not assume or guess the project path.
Case 2: The user provides a project path but no specific component
Run the scan script:
python3 {{SKILL_DIR}}/scripts/component_analyzer.py <project-path> --scan-v1
The script outputs the list of all V1 components in the project, plus an instruction field.
You must present the V1 component list to the user and ask which component to migrate. Do not skip this step.
If the list is empty, inform the user that the project contains no V1 components and no migration is needed.
Case 3: The user provides both a project path and a component name
Proceed directly to Step 1 (Analysis).
Step 1: Analyze
1.1 Detect the API version
python3 {{SKILL_DIR}}/scripts/api_version_checker.py <project-dir> --json
Key output fields:
mixingRules: "strict" (API < 19) or "relaxed" (API >= 19)
compatibleApiVersion: the minimum compatible API version
availableApis: list of available compatibility APIs
Decision points:
strict → complex types cannot cross the V1/V2 boundary during migration; a bridge pattern may be required
relaxed → UIUtils.enableV2Compatibility() and UIUtils.makeV1Observed() can relax the constraints
1.2 Analyze the target component
python3 {{SKILL_DIR}}/scripts/component_analyzer.py <target-file-or-dir> --json
Output includes:
- Component version (V1/V2), decorator list, state variable types
- Child component references, rendering mode (ForEach/LazyForEach/Repeat)
- App-level state usage (LocalStorage/AppStorage/PersistentStorage/Environment/animateTo)
1.3 Trace the dependency chain
python3 {{SKILL_DIR}}/scripts/dependency_tracer.py <component-name> <project-dir> --json
Output:
mustMigrate: list of components that must be migrated together due to data interaction
- State passing types between components (state_variable_ref / two_way_binding / callback / literal)
- Dependency graph (parent → child data flow)
Decision points:
- If
mustMigrate contains only one component → it can be migrated independently
- If there are multiple components → evaluate whether to migrate them all or use a bridge pattern
1.4 Mixing validation (run once before and once after migration)
python3 {{SKILL_DIR}}/scripts/mixing_validator.py <project-dir> --json [--target <component-name>]
Output:
violations: mixing issues that will cause compile/runtime errors
warnings: mixing scenarios that may be risky
suggestions: available compatibility API suggestions
summary.isCompliant: whether all checks pass
Step 2: Plan
Based on the analysis, report the migration scope and strategy to the user:
Independent migration (preferred)
The target component has no external data interaction (hasInput: false, hasOutput: false), or all interactions use simple types.
Joint migration
The target component exchanges complex types with its parent/child components. All components in mustMigrate must be migrated together.
Bridge pattern (use when API < 19)
When V1 must pass an @Observed-decorated class to a V2 component:
V1Comp → V1BridgeComponent(@Component) → V2Comp(@ComponentV2)
The bridge is a pure V1 component: it destructures the @Observed class into simple-type fields and passes them to the V2 child's @Param (complex types cannot cross the V1→V2 boundary when API < 19). It must NOT hold an @ObservedV2 object via a V1 decorator. For multi-component sharing, use a standalone @ObservedV2/@Trace singleton written by V1 and read by V2. See the bridge section in references/mixing-rules.md.
Confirm the strategy with the user before proceeding to execution.
Step 3: Execute the Migration
Rewrite the code item by item in the following order. Each item maps to a reference document.
3.1 Component decorator
@Component → @ComponentV2
If the component has @Entry, leave it unchanged (@Entry works in both V1 and V2).
If the component has @Reusable, change it to @ReusableV2.
3.2 State variable decorator mapping
For the full mapping table, see references/decorator-mapping.md. Quick reference:
| V1 | V2 | Key points |
|---|
@State simple type | @Local | Direct replacement |
@State complex type | @Local + @ObservedV2/@Trace on the class | V2 @Local observes only itself, not its properties |
@State needing external init | @Param @Once | @Local forbids external initialization |
@Prop | @Param | @Param is passed by reference (not a deep copy); @Param is read-only |
@Link | @Param + @Event | Replace two-way binding with a callback pattern |
@Provide/@Consume | @Provider/@Consumer | V2 requires the () syntax; the alias is the unique match key |
@Watch | @Monitor | V2 is asynchronous; supports multiple variables; provides before/after |
@Observed/@ObjectLink | @ObservedV2/@Trace | Deep observation; no longer needs child-component decomposition |
$$ binding | !! binding | Direct replacement |
3.3 Data object migration
Change @Observed classes to @ObservedV2 and add @Trace to the properties.
@Observed
class Model {
@Track public name: string = '';
@Track public count: number = 0;
}
@ObservedV2
class Model {
@Trace public name: string = '';
@Trace public count: number = 0;
}
Note: @Observed and @ObservedV2 cannot coexist on the same class. If the class is referenced by other V1 components, resolve those dependencies first.
For detailed rules, see references/class-migration.md.
3.4 Rendering control migration
| V1 | V2 |
|---|
ForEach | Repeat(...).each(...).key(...) |
LazyForEach + IDataSource | Repeat(...).each(...).key(...).virtualScroll() + @Local array |
In V2 the data source is a plain @Local array; modifying the array triggers updates — no need to call notifyDataAdd and friends manually.
Use .templateId() + .template() for template rendering instead of manual if checks.
For detailed rules and code examples, see references/rendering-migration.md.
3.5 App-level state migration
| V1 | V2 |
|---|
LocalStorage | @ObservedV2/@Trace singleton |
AppStorage | AppStorageV2.connect() |
@StorageProp/@StorageLink | AppStorageV2.connect() + @Local + @Monitor |
PersistentStorage | PersistenceV2.globalConnect() |
Environment | Read directly from UIAbilityContext.config |
Important: keep V1 API calls; only add V2 API calls. During incremental migration, a single .ts file may contain V1 API calls for multiple keys. When migrating a component, add the corresponding V2 API (e.g. AppStorageV2.connect()) only for the keys that component uses, and do not remove the original V1 API calls (e.g. AppStorage.setOrCreate()), because other not-yet-migrated V1 components may still be using other keys in the same file. Only when stateApiByKey shows that all decoratorUsage for a key have been migrated to V2 may the V1 API calls for that key be removed.
For detailed rules, see references/app-state-migration.md.
3.6 Built-in objects and animateTo
- Wrap framework built-in objects (
ChildrenMainSize/WaterFlowSections/attributeModifier) with UIUtils.makeObserved().
animateTo is incompatible with V2's asynchronous update mechanism; force a synchronous flush first with animateToImmediately (API < 22) or UIUtils.applySync() (API >= 22).
For detailed rules, see references/advanced-topics.md.
3.7 New V2 capabilities
After migration, consider adopting new V2 capabilities:
@Computed: derived state with automatic result caching
Repeat template rendering + virtualScroll: replaces ForEach/LazyForEach
Step 4: Verify
4.1 Mixing validation
python3 {{SKILL_DIR}}/scripts/mixing_validator.py <project-dir> --json --target <component-name>
Ensure summary.isCompliant is true and all violations are empty.
4.2 Item-by-item checklist
Reference document index
| File | Contents | When to consult |
|---|
references/decorator-mapping.md | Full decorator mapping table, migration rules for @State/@Prop/@Link/@Provide/@Watch | When rewriting state variables |
references/class-migration.md | @Observed/@ObjectLink/@Track → @ObservedV2/@Trace, nested object observation, precise updates | When migrating data object classes |
references/mixing-rules.md | V1/V2 mixing rules, API < 19 vs >= 19 differences, bridge pattern, enableV2Compatibility | When components coexist during migration |
references/rendering-migration.md | ForEach/LazyForEach → Repeat, virtualScroll, template rendering, @Reusable → @ReusableV2 | When rewriting rendering logic |
references/app-state-migration.md | LocalStorage/AppStorage/PersistentStorage/Environment → V2 alternatives | When migrating app-level state |
references/advanced-topics.md | Built-in objects (makeObserved), animateTo migration, V1/V2 update mechanism differences | When handling special cases |
references/architecture.md | Overall skill architecture, five-phase workflow, script responsibilities and import relationships | Maintainer reference: understanding script collaboration and data flow |
references/migration-overview.md | Design overview, detailed JSON output fields of each script, Storage key tracing mechanism, end-to-end example walkthrough | Maintainer reference: interpreting script output and migration decisions |
Example index
| Directory | Scenario | Migration points covered |
|---|
examples/simple-component/ | Simple component | @State→@Local, @Prop→@Param |
examples/component-with-props/ | Complex parent-child interaction | @Link→@Param+@Event, @Provide→@Provider, @Watch→@Monitor, $$→!! |
examples/observed-class/ | Data object | @Observed→@ObservedV2, @ObjectLink→@Trace, nested observation |
examples/localstorage/ | App-level state | LocalStorage→singleton, AppStorage→AppStorageV2, PersistentStorage→PersistenceV2 |
examples/partial-migration/ | Partial migration and coexistence | V1/V2 coexistence, bridge pattern, API version checks |
Each example directory contains before.ets (V1 code) and after.ets (V2 code) for side-by-side reference.
Notes / Caveats
- Never mix V1/V2 decorators within the same component — compile error.
- Never decorate the same class with both @Observed and @ObservedV2 — compile error.
- @Local forbids external initialization — when a value must come from outside, use
@Param @Once.
- @Param is read-only — when the child needs to modify it, keep a local copy and sync via
@Monitor.
- @Monitor is asynchronous — different from the synchronous behavior of V1's @Watch.
- V2 deep observation —
@ObservedV2/@Trace can directly observe nested properties; you no longer need to decompose child components layer by layer.
- animateTo compatibility — V2's async mechanism is incompatible with animateTo; a synchronous-flush prefix is mandatory.
- Migration is incremental — partial migration is allowed, but the mixing rules must be followed (see
references/mixing-rules.md).