Reactivity APIs
Lune's reactivity comes from @lune-js/core, a focused port of @vue/reactivity built on Alien Signals. lune-js re-exports it, so you never need to install it separately: reactive(), readonly(), shallowReactive(), shallowReadonly() and effect() are the primitives you build with.
Usage
When using the CDN build, these APIs are available on the global Lune object.
<script src="https://cdn.jsdelivr.net/npm/lune-js"></script>
<script>
const { effect, nextTick, reactive, readonly, shallowReactive, shallowReadonly } = Lune;
</script>When using the ES module build, you can import them from lune-js.
import { effect, nextTick, reactive, readonly, shallowReactive, shallowReadonly } from "lune-js";API Reference
reactive(object)
Returns a reactive proxy of the given object. This is typically used for defining the initial state of your application.
const state = reactive({ count: 0 });Nested objects and arrays are proxied on access, so mutations at any depth are tracked.
readonly(object)
Returns a deeply read-only proxy of the given object. Writes are rejected with a development warning, which makes it a good wrapper for state you want to share but not let templates mutate.
const state = reactive({ count: 0 });
const frozen = readonly(state);shallowReactive(object) / shallowReadonly(object)
The same as reactive() and readonly(), except only top-level properties are tracked. Nested objects are handed back untouched, which is useful for holding large or foreign objects (a third-party instance, a big payload) without proxying their internals.
effect(fn)
Runs a function immediately while reactively tracking its dependencies and re-runs it whenever the dependencies change. It returns the effect runner, whose .effect handle exposes stop() when you need to tear the effect down manually.
const state = reactive({ count: 0 });
effect(() => console.log(state.count)); // prints 0
state.count++; // prints 1Effects created by directives are stopped for you when the element is removed or app.unmount() runs. Effects you create yourself are not tied to any element, so stop them yourself if they should not outlive the page.
nextTick(fn?)
Waits for the pending DOM updates to flush. See $nextTick for the template-facing form.
What Lune does not include
@lune-js/core deliberately leaves out parts of @vue/reactivity: there is no ref() family, no computed(), no watch(), and no effectScope(), and collections such as Map and Set are not reactive sources. Use JavaScript getters for derived values, and effect() where you would reach for watch(). If you need the full API, install @vue/reactivity alongside Lune.
Best Practices
- Use
reactivefor state objects: This is the most common pattern for defining your application state. - Use getters for derived values: Use JavaScript getters in your reactive objects for computed-like behavior:js
const state = reactive({ count: 0, get double() { return this.count * 2; } }); - Use
effectfor side effects: Use it when you need to react to state changes with side effects like logging, network requests, or DOM manipulation.