Counter
The smallest complete ivue class: one piece of mutable state, one derived value, two actions. count is a ref-getter, double is a plain getter — no computed() — and the methods write through .value.
What to notice
doubleallocates nothing. It is a plain getter, re-derived on render — zero bytes per instance, fully reactive through leaf tracking.- Methods are engine-bound.
counter.incrementis safe to pass as a handler directly; its identity is stable.
The source
The demo above runs these exact files from the playground:
ts
// Counter.ts — the first class: one ref, one plain getter, two methods.
import { ref } from 'vue';
import { Reactive } from '../../ivue';
class $Counter {
get count() {
return ref(0);
}
// Derived value: a plain getter. No computed() needed for simple math.
get double() {
return this.count.value * 2;
}
increment() {
this.count.value++;
}
reset() {
this.count.value = 0;
}
}
export namespace Counter {
export const $Class = $Counter; // raw — children `extends` this
export let Class = Reactive($Class); // reactive — you `new` this
export type Instance = typeof Class.Instance; // defineExpose type & reactive() interop
}vue
<script setup lang="ts">
import { Counter } from './Counter';
const counter = new Counter.Class();
// the state destructure
const {
// state refs
count,
} = counter;
</script>
<template>
<div class="pane">
<p class="note">
double is a plain getter, not a computed(). It re-derives whenever the
component renders.
</p>
<div class="vals">
<div>
<div class="k">count</div>
<div class="n">{{ count }}</div>
</div>
<div>
<div class="k">double · plain getter</div>
<div class="n grad">{{ counter.double }}</div>
</div>
</div>
<div class="row">
<button class="btn primary" type="button" @click="counter.increment()">
+1
</button>
<button class="btn" type="button" @click="counter.reset()">Reset</button>
</div>
</div>
</template>
<style scoped src="../example-pane.css"></style>Open in StackBlitz ⚡ — the playground boots with this example's route and file active.