How Formily Achieves High Performance Form Rendering: A Deep Dive into Fine-Grained Reactivity
Formily achieves high performance form rendering through fine-grained reactivity and observer-based component wrappers that isolate updates to only the UI elements that depend on changed data.
Formily, Alibaba's open-source form solution, solves the performance bottlenecks common in large-scale form applications by implementing a sophisticated reactive architecture. Unlike traditional form libraries that trigger top-down re-renders, Formily's high performance form rendering strategy ensures that individual fields update independently without affecting sibling or parent components.
Fine-Grained Reactivity with @formily/reactive
At the heart of Formily's performance lies the @formily/reactive package, which implements a proxy-based observable system that tracks dependencies at the property level.
Observable State Tracking
Every field value, state flag, and schema node in Formily is wrapped with observable() to create a tracking proxy. In packages/reactive/src/observable.ts, the core function initializes this proxy:
// packages/reactive/src/observable.ts
export function observable<T extends object>(target: T): T {
return createObservable(null, null, target)
}
This proxy intercepts reads and writes, allowing Formily to know exactly which components access which properties. When a component renders and reads field.value, it subscribes only to that specific property's changes.
Annotation-Based Granularity Control
Formily provides annotations in packages/reactive/src/annotations/*.ts to control tracking depth:
observable.shallow– Tracks only direct property assignments, ignoring nested object changesobservable.deep– Deeply observes all nested propertiesobservable.computed– Creates memoized derived values that only recompute when dependencies change
These annotations prevent unnecessary deep traversals. For example, marking a large array as shallow ensures that mutating individual items doesn't trigger observers unless the array reference itself changes.
Batched Updates with Tracker
The Tracker class batches reactions to prevent render cascades. In packages/reactive-vue/src/observer/observerInVue2.ts, the integration schedules reactive renders:
// packages/reactive-vue/src/observer/observerInVue2.ts
const tracker = new Tracker(() => {
// scheduler support
observerOptions?.scheduler?.(reactiveRender) ?? reactiveRender()
})
This ensures that a single synchronous operation modifying multiple fields results in exactly one render pass per observer, not one per field change.
Observer-Wrapped Components for Isolated Rendering
Formily wraps UI components with observer higher-order components (HOCs) that connect the reactive core to the view layer.
Vue Integration with observer()
In @formily/reactive-vue, the observer() function wraps Vue components to create reactive boundaries. The wrapper registers a Tracker that re-executes the component's render function only when observed fields change:
// packages/reactive-vue/src/observer/observerInVue2.ts
const tracker = new Tracker(() => {
observerOptions?.scheduler?.(reactiveRender) ?? reactiveRender()
})
Because the render function runs inside a tracked closure, only the fields accessed during that specific render are subscribed. Updating an unrelated field elsewhere in the form does not trigger this component's re-render.
React Integration with observer()
The React implementation in packages/reactive-react/src/observer.ts provides identical functionality for React components, ensuring cross-framework consistency in performance characteristics.
Field-Level Isolation
Each field in Formily is an independent observable entity. The Field, ObjectField, and ArrayField components in packages/vue/src/components/ are individually wrapped with observer:
This architecture ensures that the form's top-level component never re-renders when a child field updates. Only the specific field component (and its descendants that access the changed data) undergoes the render cycle.
Efficient Prop Mapping with connect and mapProps
Formily optimizes the boundary between field state and UI components through pure functional wrappers.
Functional Component Wrappers
The connect() function in packages/vue/src/shared/connect.ts creates a pure component that maps field state to component props without instantiating a new Vue component on every render:
// packages/vue/src/shared/connect.ts
export function connect<T extends VueComponent>(target: T, ...args: IComponentMapper[]): T {
const Component = args.reduce((t, mapper) => mapper(t), target)
// functional component created once
…
}
This pattern ensures that the component definition is created once and reused, reducing memory pressure and initialization overhead.
Selective Property Binding
mapProps() allows developers to declaratively select only the necessary parts of a field (such as value and error) rather than passing the entire field object:
// Example usage pattern from connect.ts
mapProps({
value: 'value',
disabled: 'disabled',
error: 'error'
})
This selective binding reduces the surface area for prop-change detection, preventing components from re-rendering when unrelated field properties (like loading or active) change.
Advanced Optimization Techniques
Beyond the core reactive system, Formily implements several architectural optimizations to minimize DOM operations and render cycles.
Virtual Fragment Rendering
The Fragment component in packages/vue/src/shared/fragment.ts renders children without creating an extra wrapper DOM element. This keeps the virtual DOM tree shallow and reduces the number of actual DOM nodes that the browser must manage and reconcile.
Scheduler and Batch API for Heavy Operations
Formily exposes a custom scheduler option in the observer configuration that allows developers to defer rendering or batch multiple field updates. In packages/reactive-vue/src/observer/observerInVue2.ts, the scheduler handling appears at lines 84-92:
// React example using scheduler
import { observer } from '@formily/reactive-react'
const MyComponent = observer(
(props) => {
const form = useForm()
return <div>{form.values.title}</div>
},
{
scheduler: (render) => requestAnimationFrame(render), // defer to next frame
}
)
This capability is essential for heavy operations like bulk data imports or cascading field calculations, allowing the UI to remain responsive by grouping renders.
Schema Render Map Memoization
The RecursionField component in packages/vue/src/components/RecursionField.ts builds a render map that groups render functions by field key. This memoization ensures that the same field type is rendered with the same function instance without recreating it on each render cycle, reducing memory churn and improving reconciliation performance.
Summary
Formily's high performance form rendering architecture rests on these key pillars:
- Fine-grained reactivity via proxy-based observables that track dependencies at the property level, ensuring components subscribe only to data they actually use
- Observer-wrapped components that isolate renders to individual fields, preventing parent components from re-rendering when child data changes
- Efficient prop mapping through
connect()andmapProps()that minimize component initialization overhead and reduce prop-change detection surface area - Field-level isolation where each field operates as an independent observable entity, enabling massive forms to update individual inputs without global re-renders
- Render optimizations including virtual fragments, scheduler APIs for batching, and schema render map memoization to minimize DOM operations and memory churn
Frequently Asked Questions
What makes Formily faster than traditional form libraries?
Traditional form libraries typically rely on top-down state propagation where changing any field value triggers a re-render of the entire form component tree. Formily replaces this pattern with fine-grained reactivity using proxy-based observables in packages/reactive/src/observable.ts. When a component wrapped with observer renders, it subscribes only to the specific observable properties it accesses. Updating an unrelated field does not trigger that component's render function, allowing forms with hundreds of fields to maintain 60fps interactions.
How does Formily prevent unnecessary re-renders in large forms?
Formily implements field-level isolation through its observer pattern. Each Field, ArrayField, and ObjectField component in packages/vue/src/components/ is individually wrapped with the observer HOC from @formily/reactive-vue (or @formily/reactive-react). This creates a reactive boundary where only the specific field component re-renders when its observable state changes. The parent form component and sibling fields remain unaffected. Additionally, the connect() utility in packages/vue/src/shared/connect.ts creates pure functional wrappers that prevent unnecessary component initialization overhead.
Can I use Formily's reactive system without the form components?
Yes, the @formily/reactive package is designed as a standalone reactivity system independent of the form logic. You can import observable, observer, and Tracker directly from @formily/reactive or its framework-specific bindings (@formily/reactive-react, @formily/reactive-vue) to build high-performance reactive applications. The observable() function in packages/reactive/src/observable.ts works with any plain JavaScript object, and the observer wrapper can be applied to any React or Vue component. This modular architecture allows developers to leverage Formily's fine-grained reactivity for state management beyond forms.
Does Formily's performance optimization work across Vue 2, Vue 3, and React?
Yes, Formily provides framework-specific implementations that adapt the core reactivity patterns to each platform's rendering mechanism. For Vue 2, the observerInVue2.ts file in packages/reactive-vue/src/observer/ implements a custom tracking layer compatible with Vue 2's reactivity system. Vue 3 leverages the native Proxy-based reactivity with similar observer patterns. For React, packages/reactive-react/src/observer.ts provides a observer HOC that uses the Tracker class to batch updates and trigger React re-renders only when observed data changes. The core packages/reactive package ensures consistent behavior across all three frameworks, making the high performance form rendering capabilities available regardless of your frontend stack.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →