Options API Guide
The Options API is a classic API style provided by Lyt.js that allows you to define component behavior and state through object options. This style is very intuitive for developers familiar with traditional frontend frameworks.
What is the Options API?
The Options API is an object-options-based API style where you organize component logic by defining options such as data, methods, computed, watch, etc.
The main advantages of this approach are:
- Intuitive and easy to understand: Easier to grasp for beginners
- Clear structure: Code is organized by functional type with a clear structure
- Backward compatible: Similar style to traditional frontend frameworks, low migration cost
- Easy to debug: Fixed code structure makes troubleshooting easier
Basic Usage
Component Definition
Define a component using the Options API:
import { createApp } from '@lytjs/core'
const app = createApp({
// Component name
name: 'Counter',
// Reactive data
data() {
return {
count: 0,
message: 'Hello Lyt.js'
}
},
// Computed properties
computed: {
doubleCount() {
return this.count * 2
}
},
// Methods
methods: {
increment() {
this.count++
},
updateMessage(newMessage) {
this.message = newMessage
}
},
// Lifecycle hooks
mounted() {
console.log('Component mounted')
},
// Template
template: `
<div>
<h1>{{ message }}</h1>
<p>Count: {{ count }}</p>
<p>Double count: {{ doubleCount }}</p>
<button @click="increment">Increment</button>
<input model="message" placeholder="Enter message" />
</div>
`
})
app.mount('#app')Core Options
data
The data option defines the component's reactive data. It must be a function that returns an object.
data() {
return {
count: 0,
user: {
name: 'John',
age: 30
},
items: [1, 2, 3]
}
}Note: data must be a function so that each component instance gets its own independent copy of the data.
methods
The methods option defines component methods that can be called in templates or accessed via this in other methods.
methods: {
increment() {
this.count++
},
greet(name) {
return `Hello, ${name}!`
}
}computed
The computed option defines computed properties that automatically calculate based on their reactive dependencies and cache the results.
computed: {
doubleCount() {
return this.count * 2
},
fullName() {
return `${this.firstName} ${this.lastName}`
}
}watch
The watch option monitors reactive data changes and executes callback functions when data changes.
watch: {
count(newValue, oldValue) {
console.log(`Count changed from ${oldValue} to ${newValue}`)
},
// Deep watch
user: {
handler(newValue, oldValue) {
console.log('User changed:', newValue)
},
deep: true
}
}props
The props option defines component properties, allowing parent components to pass data to child components.
props: {
// Basic types
title: String,
count: Number,
// With default value
initialCount: {
type: Number,
default: 0
},
// Required property
message: {
type: String,
required: true
},
// Custom validation
age: {
type: Number,
validator: (value) => {
return value >= 0 && value <= 150
}
}
}emits
The emits option defines events that a component can trigger.
emits: ['update:count', 'submit'],
methods: {
increment() {
this.count++
this.$emit('update:count', this.count)
},
submit() {
this.$emit('submit', this.formData)
}
}template
The template option defines the component's template, supporting interpolation expressions, directives, etc.
template: `
<div>
<h1>{{ title }}</h1>
<p v-if="showMessage">{{ message }}</p>
<ul>
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
</ul>
<button @click="increment">Increment</button>
</div>
`Lifecycle Hooks
The Options API provides the following lifecycle hooks:
Creation Phase
- beforeCreate: Called before the component instance is created. Data observation and event mechanisms are not yet initialized.
- created: Component instance creation is complete. Data observation and event mechanisms are initialized, but the DOM is not yet mounted.
Mounting Phase
- beforeMount: Called before DOM mounting. The template has been compiled.
- mounted: DOM mounting is complete. DOM operations can be performed.
Updating Phase
- beforeUpdate: Called before data update. The DOM has not yet been updated.
- updated: Data update is complete. The DOM has been updated.
Unmounting Phase
- beforeUnmount: Called before the component is unmounted.
- unmounted: Component unmounting is complete.
Error Handling
- errorCaptured: Captures errors from child components.
Component Communication
Parent to Child: Passing Data
Use props to pass data to child components:
// Parent component
const Parent = {
template: `
<Child :title="title" :count="count" />
`,
data() {
return {
title: 'Parent Component',
count: 10
}
}
}
// Child component
const Child = {
props: ['title', 'count'],
template: `
<div>
<h2>{{ title }}</h2>
<p>Count: {{ count }}</p>
</div>
`
}Child to Parent: Passing Data
Use $emit to trigger events and pass data to parent components:
// Child component
const Child = {
data() {
return {
localCount: 0
}
},
methods: {
increment() {
this.localCount++
this.$emit('update:count', this.localCount)
}
},
template: `
<button @click="increment">Increment</button>
`
}
// Parent component
const Parent = {
data() {
return {
count: 0
}
},
methods: {
handleUpdateCount(newCount) {
this.count = newCount
}
},
template: `
<div>
<p>Count: {{ count }}</p>
<Child @update:count="handleUpdateCount" />
</div>
`
}Sibling Component Communication
Use an event bus or state management:
// eventBus.js
import { createApp } from '@lytjs/core'
const eventBus = createApp({})
export default eventBus
// Component A
import eventBus from './eventBus'
const ComponentA = {
methods: {
sendMessage() {
eventBus.emit('message', 'Hello from Component A')
}
}
}
// Component B
import eventBus from './eventBus'
const ComponentB = {
data() {
return {
message: ''
}
},
mounted() {
eventBus.on('message', (msg) => {
this.message = msg
})
}
}Advanced Features
Mixins
Mixins allow you to reuse component logic:
// mixins/logger.js
export const loggerMixin = {
methods: {
log(message) {
console.log(`[${this.name || 'Component'}] ${message}`)
}
},
mounted() {
this.log('Component mounted')
}
}
// Usage in a component
import { loggerMixin } from './mixins/logger'
const MyComponent = {
mixins: [loggerMixin],
name: 'MyComponent',
mounted() {
this.log('Custom mounted logic')
}
}Custom Directives
Custom directives allow you to manipulate DOM elements:
// Register a global directive
app.directive('focus', {
mounted(el) {
el.focus()
}
})
// Usage in template
// <input v-focus />
// Local directive
const MyComponent = {
directives: {
focus: {
mounted(el) {
el.focus()
}
}
}
}Filters
Filters are used for text formatting:
// Register a global filter
app.config.globalProperties.$filters = {
capitalize(value) {
if (!value) return ''
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
// Usage in template
// {{ message | capitalize }}Comparison with Composition API
Options API
const app = createApp({
data() {
return {
count: 0,
message: 'Hello'
}
},
computed: {
doubleCount() {
return this.count * 2
}
},
methods: {
increment() {
this.count++
}
},
mounted() {
console.log('Component mounted')
}
})Composition API
import { ref, computed, onMounted } from '@lytjs/core'
const app = createApp({
setup() {
const count = ref(0)
const message = ref('Hello')
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
onMounted(() => {
console.log('Component mounted')
})
return {
count,
message,
doubleCount,
increment
}
}
})Best Practices
1. Data Management
- Only define reactive data in
data - Avoid complex computations in templates; use
computedproperties - Use
watchorwatchEffectfor data changes that need monitoring
2. Method Organization
- Organize methods by functionality, keeping each method focused on a single responsibility
- Avoid complex business logic in methods; consider splitting or using composables
- Use descriptive method names for better code readability
3. Lifecycle Hooks
- Only use lifecycle hooks when necessary
- Perform DOM operations in
mounted, clean up resources inbeforeUnmount - Avoid DOM operations in
createdsince the DOM is not yet mounted
4. Component Communication
- For parent-child communication, use
propsand$emit - For sibling or cross-level component communication, use an event bus or state management
- Avoid using
$parentor$childrenfor component communication, as it increases coupling
5. Performance Optimization
- Use
v-ifandv-showappropriately to control element visibility - Always add a
keyattribute when usingv-for - Use
computedproperties to cache results for complex computations - Consider virtual scrolling for large lists
Example: Complete Options API Application
import { createApp } from '@lytjs/core'
const app = createApp({
name: 'TodoApp',
data() {
return {
newTodo: '',
todos: [
{ id: 1, text: 'Learn Lyt.js', done: false },
{ id: 2, text: 'Build an app', done: false }
]
}
},
computed: {
remaining() {
return this.todos.filter(todo => !todo.done).length
},
completed() {
return this.todos.filter(todo => todo.done).length
}
},
methods: {
addTodo() {
if (!this.newTodo.trim()) return
this.todos.push({
id: Date.now(),
text: this.newTodo,
done: false
})
this.newTodo = ''
},
toggleTodo(id) {
const todo = this.todos.find(todo => todo.id === id)
if (todo) {
todo.done = !todo.done
}
},
removeTodo(id) {
this.todos = this.todos.filter(todo => todo.id !== id)
},
clearCompleted() {
this.todos = this.todos.filter(todo => !todo.done)
}
},
mounted() {
console.log('Todo app mounted')
},
template: `
<div class="todo-app">
<h1>Todo App</h1>
<div class="stats">
<p>{{ remaining }} remaining, {{ completed }} completed</p>
</div>
<form @submit.prevent="addTodo">
<input
v-model="newTodo"
placeholder="Add a new task..."
class="todo-input"
/>
<button type="submit" class="add-button">Add</button>
</form>
<ul class="todo-list">
<li
v-for="todo in todos"
:key="todo.id"
:class="{ completed: todo.done }"
>
<input
type="checkbox"
:checked="todo.done"
@change="toggleTodo(todo.id)"
/>
<span>{{ todo.text }}</span>
<button @click="removeTodo(todo.id)" class="remove-button">Remove</button>
</li>
</ul>
<button
v-if="completed > 0"
@click="clearCompleted"
class="clear-button"
>
Clear Completed
</button>
</div>
`
})
app.mount('#app')Summary
The Options API is a classic API style provided by Lyt.js that organizes component logic through object options, making it intuitive and easy to understand for beginners. By using options such as data, methods, computed, and watch, you can create fully functional components.
The Options API is particularly suitable for:
- Small components with simple logic
- Beginners who need an intuitive and easy-to-understand API style
- Projects migrating from traditional frontend frameworks
- Rapid prototyping
Whether you use the Options API or the Composition API, Lyt.js provides a great development experience. You can choose the appropriate API style based on your project's complexity and your team's familiarity, and you can even mix both styles in the same project.