Components and Lifecycle Events
In Lune, components are simplified compared to standard Vue. They are primarily functions that return a scope object.
Function Components
Reusable scope logic can be created with functions:
function Counter(props) {
return {
count: props.initialCount,
inc() {
this.count++;
},
onMounted() {
console.log(`I'm mounted!`);
}
};
}
createApp({
Counter
}).mount();Usage
Use the function in lu-scope to instantiate the component:
<div lu-scope="Counter({ initialCount: 1 })" @lune:mounted="onMounted">
<p>{{ count }}</p>
<button @click="inc">increment</button>
</div>
<div lu-scope="Counter({ initialCount: 2 })">
<p>{{ count }}</p>
<button @click="inc">increment</button>
</div>NOTE
Lune does not have automatic lifecycle hooks like mounted() or setup(). To run code when a component is mounted, define a method in your scope and wire it up manually using @lune:mounted="methodName". See Lifecycle Events for details.
Components with Template
If you want to reuse a piece of template, you can provide a special $template key on the scope object. The value can be an ID selector to a <template> element:
function Counter(props) {
return {
$template: "#counter-template",
count: props.initialCount,
inc() {
this.count++;
}
};
}<template id="counter-template">
My count is {{ count }}
<button @click="inc">++</button>
</template>
<!-- reuse it -->
<div lu-scope="Counter({ initialCount: 1 })"></div>
<div lu-scope="Counter({ initialCount: 2 })"></div>The <template> approach is recommended over inline strings because it is more efficient to clone from a native template element.
Organizing Code
For larger projects, it's best to keep your component logic in separate JavaScript files.
components/Counter.js:
export default function Counter(props) {
return {
count: props.initialCount || 0,
inc() {
this.count++;
},
dec() {
this.count--;
}
};
}index.html:
<script type="module">
import { createApp } from "https://esm.run/lune-js";
import Counter from "./components/Counter.js";
createApp({
Counter
}).mount();
</script>
<div lu-scope="Counter({ initialCount: 10 })">...</div>Lifecycle Events
Lune provides special lifecycle events that you can listen to on any element:
@lune:mounted: Fired when the element is mounted and directives are initialized.@lune:unmounted: Fired when the element is unmounted (e.g., vialu-if).
Usage
<div lu-scope="{ show:false }">
<button type="button" @click="show = !show">show</button>
<div lu-if="show" @lune:mounted="console.log('mounted on: ', $el)" @lune:unmounted="console.log('unmounted: ', $el)">
I am visible
</div>
</div>Practical Use Cases
Auto-focus Input
<input @lune:mounted="$el.focus()" />Integrating 3rd Party Libraries
You can use @lune:mounted to initialize jQuery plugins or other libraries on the element. A library is not reachable from a template just because it is a global — put it in the app data, or register it with allowGlobals:
import Pikaday from "pikaday";
createApp({ Pikaday }).mount();<div @lune:mounted="new Pikaday({ field: $el })">
<input type="text" />
</div>A component function is the other way around it, since the function body is ordinary JavaScript:
function DatePicker() {
return {
init($el) {
new Pikaday({ field: $el });
}
};
}
createApp({ DatePicker }).mount();<div lu-scope="DatePicker()" @lune:mounted="init($el)">
<input type="text" />
</div>Cleanup
Use @lune:unmounted to clean up event listeners or timers to prevent memory leaks.
Lifecycle Diagram
Here is a visualization of the component lifecycle:
NOTE
lu-effect watchers are automatically disposed of when the component is unmounted.