Reactivity
v-ibe uses a fine-grained reactive system. Instead of re-rendering entire component trees, it tracks exactly which DOM nodes depend on which values and updates them directly.
State
@State creates a reactive value. The framework auto-detects the type and wraps it accordingly:
@State count = 0; // primitive → Signal
@State user = { name: '' }; // object → CompositeSignal (deep proxy)
@State items: string[] = [];// array → ReactiveArray
When you assign a new value or mutate a property, any dependent computations and DOM bindings update automatically.
CompositeSignal
When @State wraps an object, it creates a deep reactive proxy. Nested properties are tracked individually:
@State user = { name: 'John', address: { city: 'NYC' } };
// In view — only this <span> updates when name changes
<span>{this.user.name}</span>
// Mutations trigger granular updates
this.user.name = 'Jane'; // updates only subscribers of .name
this.user.address.city = 'LA'; // updates only subscribers of .address.city
Nested objects and arrays are automatically wrapped — no extra setup needed.
ReactiveArray
When @State wraps an array, it creates a ReactiveArray with granular index tracking:
@State todos: Todo[] = [];
Standard array methods work as expected:
this.todos.push({ text: 'New todo' });
this.todos.splice(1, 1);
this.todos[0] = { text: 'Updated' };
Use .map() and .filter() to derive new reactive arrays:
// Derived array — updates only when source changes
@Computed
get completed() {
return this.todos.filter(t => t.done);
}
Render lists with <For> for fine-grained updates — only the changed index re-renders:
<For each={this.todos}>
{(todo) => <TodoItem todo={todo} />}
</For>
Computed
@Computed creates a memoized derived value. It only recalculates when its dependencies change:
@State firstName = 'John';
@State lastName = 'Doe';
@Computed
get fullName() {
return `${this.firstName} ${this.lastName}`;
}
Accessing this.fullName multiple times without changing firstName or lastName returns the cached value.
Effect
@Effect runs a side effect whenever its tracked dependencies change:
@State query = '';
@Effect
searchOnChange() {
console.log('Searching for:', this.query);
// This runs every time this.query changes
}
Effects are automatically disposed when the component is disconnected.
Resource
@Resource handles async data fetching with built-in loading and error states. It executes when the component mounts:
@Resource(async (signal) => {
const res = await fetch('/api/users', { signal });
return res.json();
})
users!: IResource<User[]>;
In the template, use <Show> for conditional rendering based on the resource state:
view() {
return (
<div>
<Show when={this.users.loading}>
{() => <p>Loading...</p>}
</Show>
<Show when={this.users.error}>
{() => <p>Error: {this.users.error.message}</p>}
</Show>
<Show when={this.users.data}>
{() => <UserList users={this.users()} />}
</Show>
</div>
);
}