9.9. Vue 3 Integration¶
Wire a ComponentVMOf<M> to a Vue 3 component via the Composition API
reactive() / ref() primitives.
9.9.1. Reactivity primitive¶
Vue 3's reactivity tracks reads and writes to reactive(obj) and
ref() values; templates re-render automatically when tracked
values change. Bridge VMx by syncing a local ref from VMx hub events.
9.9.2. Mapping¶
| Vue 3 | VMx |
|---|---|
ref(value).value = newValue |
PropertyChangedMessage<T> handler |
@click="fn" |
command.execute() in fn |
computed(() => ...) |
DerivedProperty<T> / fromSources |
onUnmounted(() => cleanup) |
dispose the subscription |
9.9.3. Adapter skeleton¶
// composables/useVm.ts
import { ref, onUnmounted, type Ref } from "vue";
import { filter } from "rxjs/operators";
import { ComponentVMOf, type IMessageHub, PropertyChangedMessage } from "@thekaveh/vmx";
export function useVm<M, K extends keyof ComponentVMOf<M>>(
vm: ComponentVMOf<M>,
hub: IMessageHub,
property: K
): Ref<ComponentVMOf<M>[K]> {
const value = ref(vm[property]) as Ref<ComponentVMOf<M>[K]>;
const sub = hub.messages
.pipe(filter((m): m is PropertyChangedMessage<unknown> =>
m instanceof PropertyChangedMessage &&
m.sender === vm &&
m.propertyName === property))
.subscribe(() => { value.value = vm[property]; });
onUnmounted(() => sub.unsubscribe());
return value;
}
<script setup lang="ts">
import { useVm } from "./composables/useVm";
import type { ComponentVMOf, ICommand, IMessageHub } from "@thekaveh/vmx";
const props = defineProps<{
vm: ComponentVMOf<Note>;
hub: IMessageHub;
saveCommand: ICommand;
}>();
const model = useVm(props.vm, props.hub, "model");
</script>
<template>
<h1>{{ model.title }}</h1>
<button @click="props.saveCommand.execute()">Save</button>
</template>
9.9.4. Fuller example¶
No worked Vue Notes-Showcase ships yet. The React recipe
(react.md) uses the same hub-subscription shape (just with
useSyncExternalStore instead of ref) and is a good reference.