Analysis of HarmonyOS ArkUI Reactive Framework and Engine Implementation
The article dissects HarmonyOS ArkUI’s reactive architecture, showing how the open‑source AceEngine wraps state in ObservedProperty objects, tracks dependencies, updates dirty elements, extends the TypeScript compiler with custom UI syntax, bundles via Rollup, and provides a CDP‑based debugger, achieving fine‑grained updates comparable to Vue but with lower memory overhead and faster response on HarmonyOS devices.
HarmonyOS ArkUI is a declarative open framework that enables cross‑device UI development using the ArkTS language and a minimal DSL. This article examines how ArkUI implements its reactive system by analyzing the open‑source AceEngine rendering engine and related tooling.
Responsive Example – A simple HelloWorld component created with DevEcoStudio demonstrates the use of the message state and the onClick event to trigger UI updates.
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
.width('100%')
.onClick(() => { this.message = "Test Reactive" });
}
.height('100%')
}
}The generated JavaScript artifact reveals that the message property is wrapped by an ObservedPropertySimplePU object, whose getter and setter are intercepted to notify dependent elements, similar to Vue’s reactivity model.
class Index extends ViewPU {
constructor(parent, params, __localStorage, elmtId = -1) {
super(parent, __localStorage, elmtId);
this.__message = new ObservedPropertySimplePU('Hello World', this, "message");
this.setInitiallyProvidedValue(params);
}
get message() { return this.__message.get(); }
set message(newValue) { this.__message.set(newValue); }
// ... component creation omitted for brevity ...
}Dependency Tracking – During a property get , the current rendering element ID is added to dependentElmtIdsByProperty_ . When the property is set, viewPropertyHasChanged marks the component as dirty and records the dependent element IDs in dirtDescendantElementIds_ .
protected recordPropertyDependentUpdate() {
const elmtId = this.getRenderingElmtId();
this.dependentElmtIdsByProperty_.addPropertyDependency(elmtId);
}
protected notifyPropertyHasChangedPU() {
this.owningView_.viewPropertyHasChanged(this.info_, this.dependentElmtIdsByProperty_.getAllPropertyDependencies());
}The dirty‑component list is processed by updateDirtyElements , which invokes the stored updateFunc for each element and clears the dirty set.
public updateDirtyElements() {
do {
dirtElmtIdsFromRootNode.forEach(elmtId => {
if (this.hasRecycleManager()) {
this.UpdateElement(this.recycleManager_.proxyNodeId(elmtId));
} else {
this.UpdateElement(elmtId);
}
this.dirtDescendantElementIds_.delete(elmtId);
});
} while (this.dirtDescendantElementIds_.size);
}Engineering – TypeScript Superset – ArkUI extends the TypeScript compiler (ohos‑typescript) with custom syntax such as struct , @Builder , and Component . The compiler transforms a struct Index declaration into a class that extends ViewPU , and flattens nested UI syntax into a series of statements.
if (ts.isStructDeclaration(node)) {
return ts.factory.createClassDeclaration(
ts.getModifiers(node),
node.name,
undefined,
updateHeritageClauses(node, log),
memberNode
);
}During bundling, Rollup invokes the TypeScript transpiler with a custom transformer ( processUISyntax ) that injects the reactive wrappers before code generation.
const result: ts.TranspileOutput = ts.transpileModule(newContent, {
compilerOptions,
fileName: id,
transformers: { before: [processUISyntax(null)] }
});Debugging Support – The ArkTS runtime ( arkcompiler_ets_runtime ) provides a lightweight debugger that swaps the normal opcode dispatch table with a debug‑specific table when a breakpoint is hit. Breakpoint commands are sent via the Chrome Debug Protocol (CDP) and translated to the internal DAP format.
void EcmaInterpreter::RunInternal(JSThread *thread, const uint8_t *pc, JSTaggedType *sp) {
uint8_t opcode = READ_INST_OP();
auto *dispatchTable = instDispatchTable.data();
CHECK_SWITCH_TO_DEBUGGER_TABLE();
goto *dispatchTable[opcode];
}
DEBUG_HANDLE_OPCODE(LDNAN) {
NOTIFY_DEBUGGER_EVENT();
REAL_GOTO_DISPATCH_OPCODE(EcmaOpcode::LDNAN);
}Overall, the analysis shows that ArkUI’s reactive architecture mirrors mature frameworks like Vue and SolidJS, but it opts for fine‑grained property updates instead of a virtual‑DOM diff, aiming for lower memory overhead and faster response times on HarmonyOS devices.
DaTaobao Tech
Official account of DaTaobao Technology
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.