@pyreon/vue-compat
v0.12.10
Published
Vue 3-compatible Composition API shim for Pyreon — write Vue-style code that runs on Pyreon's reactive engine
Downloads
5,657
Readme
@pyreon/vue-compat
Vue 3 Composition API shim that runs on Pyreon's signal-based reactive engine. Migrate Vue code by swapping the import path.
Install
bun add @pyreon/vue-compatQuick Start
// Replace:
// import { ref, computed, watch } from "vue"
// With:
import { ref, computed, watch } from '@pyreon/vue-compat'
function Counter() {
const count = ref(0)
const doubled = computed(() => count.value * 2)
watch(count, (newVal, oldVal) => {
console.log(`count: ${oldVal} -> ${newVal}`)
})
return (
<div>
<span>{doubled.value}</span>
<button onClick={() => count.value++}>Count: {count.value}</button>
</div>
)
}Reactive Objects
import { reactive, watchEffect } from '@pyreon/vue-compat'
function UserForm() {
const form = reactive({ name: '', email: '' })
watchEffect(() => {
console.log('form changed:', form.name, form.email)
})
return (
<div>
<input
value={form.name}
onInput={(e) => (form.name = e.currentTarget.value)}
placeholder="Name"
/>
<input
value={form.email}
onInput={(e) => (form.email = e.currentTarget.value)}
placeholder="Email"
/>
<p>
Hello, {form.name} ({form.email})
</p>
</div>
)
}Provide / Inject
import { ref, provide, inject, defineComponent } from '@pyreon/vue-compat'
const ThemeKey = Symbol('theme')
function ThemeProvider(props: { children: any }) {
const theme = ref('light')
provide(ThemeKey, theme)
return (
<div>
<button onClick={() => (theme.value = theme.value === 'light' ? 'dark' : 'light')}>
Toggle theme
</button>
{props.children}
</div>
)
}
function ThemedBox() {
const theme = inject(ThemeKey, ref('light'))
return <div class={`box-${theme.value}`}>Theme: {theme.value}</div>
}createApp
import { createApp, ref } from '@pyreon/vue-compat'
function App() {
const message = ref('Hello from Pyreon')
return <h1>{message.value}</h1>
}
const app = createApp(App)
app.mount('#app')Key Differences from Vue
- No virtual DOM. Pyreon uses fine-grained reactivity -- no diffing, no re-renders.
- Components run once (setup phase only).
reactive()uses Pyreon's store proxy with deep signal wrapping.readonly()is strict -- setting any property (including symbols) throws an error.provide/injectuses Pyreon's context system -- fully isolated per component tree.
API
Reactivity: Refs
ref(value)-- returns{ value }with reactive.value.shallowRef(value)-- shallow reactive ref (no deep tracking).triggerRef(ref)-- force subscribers to re-run.isRef(val)-- type guard.unref(val)-- unwrap a ref or return value as-is.toRef(obj, key)-- create a ref bound to an object property.toRefs(obj)-- convert all properties to refs.
Reactivity: Computed
computed(fn)-- read-only computed ref.computed({ get, set })-- writable computed ref.
Reactivity: Objects
reactive(obj)-- deep reactive proxy (Pyreon store).shallowReactive(obj)-- shallow reactive proxy.readonly(obj)-- read-only proxy that throws on writes.toRaw(proxy)-- unwrap to the original object.
Watchers
watch(source, callback, options?)-- watch a ref, getter, or reactive object. Supportsimmediateanddeep.watchEffect(fn)-- auto-tracking effect, returns stop handle.
Lifecycle Hooks
onMounted(fn)/onBeforeMount(fn)onUnmounted(fn)/onBeforeUnmount(fn)onUpdated(fn)
Dependency Injection
provide(key, value)-- provide a value to descendants.inject(key, defaultValue?)-- inject a value from an ancestor.
Application
createApp(component, props?)-- create an app instance with.mount(el)and.unmount().defineComponent(setup)-- wrapper for type inference (returns setup as-is).nextTick()-- wait for the next microtask.
Utilities
h/Fragment-- JSX runtime.batch(fn)-- coalesce multiple signal writes.
