Support for both Vue 2 Options API and Vue 3 Composition API
- Overview
- API Styles
- Quick Start
- Component Basics
- Props
- Setup Function (Composition API)
- Options API
- Templates
- Reactive System
- Slots
- Component Lifecycle
- Comparison: Vue 2 vs Vue 3
- API Reference
- Examples
ETAF's component system now supports both Vue 2's Options API and Vue 3's Composition API, giving you flexibility in how you write components. Choose the style that best fits your needs or use both styles in the same project!
- Declarative Components - Define reusable UI components with props and state
- Reactive State Management - Automatic dependency tracking and updates
- Two API Styles - Options API (Vue 2) or Composition API (Vue 3)
- Type Safety - Structured component definitions with clear contracts
The component system is built on three main pillars:
- Component Definition - Using
etaf-define-componentmacro - Reactive System - ref, computed, watch for state management
- Template Rendering - Integration with ETAF's TML templating
The Composition API uses a setup function to define reactive state and logic:
(etaf-define-component my-counter
:props '(:initial)
:setup (lambda (props)
(let* ((count (etaf-ref 0))
(increment (lambda () (etaf-ref-update count #'1+))))
(list :count count :increment increment)))
:template ...)Pros:
- More flexible for complex logic
- Better code reusability through composables
- Explicit reactive references
- Better TypeScript support (in JavaScript)
The Options API uses data, methods, computed, and watch options:
(etaf-define-component my-counter
:props '(:initial)
:data (lambda () (list :count 0))
:methods (list :increment (lambda () ...))
:computed (list :doubled (lambda () ...))
:watch (list :count (lambda (new old) ...))
:template ...)Pros:
- Familiar to Vue 2 developers
- More organized structure for simple components
- Clear separation of concerns
- Less boilerplate for simple cases
Here's a simple counter using the Composition API:
(require 'etaf-component)
;; Define a counter component using Composition API
(etaf-define-component my-counter
:props '(:initial-count)
:setup (lambda (props)
(let* ((count (etaf-ref
(or (plist-get props :initial-count) 0)))
(increment (lambda ()
(etaf-ref-set count
(1+ (etaf-ref-get count))))))
(list :count count
:increment increment)))
:template (lambda (data)
`(div :class "counter"
(button :on-click ,(plist-get data :increment)
"Count: "
,(format "%d" (etaf-ref-get
(plist-get data :count)))))))
;; Use the component
(etaf-paint-to-buffer "*demo*"
'(my-counter :initial-count 5))The same counter using the Options API:
(require 'etaf-component)
;; Define a counter component using Options API
(etaf-define-component my-counter
:props '(:initial-count)
:data (lambda ()
(list :count 0))
:methods (list
:increment (lambda ()
(let ((count-ref (plist-get this :count)))
(etaf-ref-update count-ref #'1+))))
:computed (list
:doubled (lambda ()
(let ((count-ref (plist-get this :count)))
(* 2 (etaf-ref-get count-ref)))))
:mounted (lambda ()
(message "Counter mounted!"))
:template (lambda (data)
`(div :class "counter"
(button :on-click ,(plist-get data :increment)
"Count: "
,(format "%d" (etaf-ref-get
(plist-get data :count)))))))
;; Use the component
(etaf-paint-to-buffer "*demo*"
'(my-counter :initial-count 5))Components are defined using the etaf-define-component macro with either API style:
Composition API:
(etaf-define-component component-name
:props '(:prop1 :prop2)
:setup setup-function
:template template-function)Options API:
(etaf-define-component component-name
:props '(:prop1 :prop2)
:data data-function
:methods methods-plist
:computed computed-plist
:watch watch-plist
:mounted mounted-hook
:template template-function)A component definition can include:
Common to both APIs:
- name - Symbol identifying the component
- :props - List of properties the component accepts
- :template - Template for rendering (function or s-expression)
- :emits (optional) - List of events the component can emit
Composition API specific:
- :setup - Function that sets up reactive state and methods
Options API specific:
- :data - Function returning initial reactive data
- :methods - Plist of method functions
- :computed - Plist of computed property getters
- :watch - Plist of watcher functions
- :mounted/:updated/:unmounted - Lifecycle hooks
Once defined, components can be used like regular HTML elements:
;; Simple usage
(my-component)
;; With props
(my-component :title "Hello" :count 42)
;; With children (slots)
(my-component :title "Container"
(p "Child 1")
(p "Child 2"))Props are the way to pass data from parent to child components.
(etaf-define-component user-card
:props '(:name :email :avatar)
:template (lambda (data)
`(div :class "user-card"
(img :src ,(plist-get data :avatar))
(h3 ,(plist-get data :name))
(p ,(plist-get data :email)))))Props are automatically extracted and passed to the setup function and template:
;; Parent component uses it:
(user-card :name "Alice"
:email "alice@example.com"
:avatar "/images/alice.png")Future versions may support prop validation:
:props '((:name :type string :required t)
(:count :type number :default 0)
(:disabled :type boolean))The setup function is where you define the component's reactive state and methods. It runs once when the component is created.
:setup (lambda (props)
(let* ((count (etaf-ref 0))
(increment (lambda ()
(etaf-ref-update count #'1+))))
;; Return data and methods for the template
(list :count count
:increment increment)))The setup function returns a plist of:
- Reactive References - Created with
etaf-ref - Computed Values - Created with
etaf-computed - Methods - Lambda functions
- Static Data - Regular values
Props are passed as the first argument:
:setup (lambda (props)
(let ((initial (plist-get props :initial-value)))
(list :value (etaf-ref initial))))The Options API provides a structured way to define components using distinct options for data, methods, computed properties, and watchers.
The :data option is a function that returns the initial state as a plist. All values are automatically converted to reactive refs.
(etaf-define-component my-form
:data (lambda ()
(list :username ""
:email ""
:age 0
:terms-accepted nil)))Key Points:
- Data function is called once when component is created
- All values become reactive refs automatically
- Access with
(plist-get this :property-name) - Values are wrapped in refs, so use
etaf-ref-getandetaf-ref-set
The :methods option is a plist of functions that can be called from the template or other methods.
(etaf-define-component todo-item
:data (lambda ()
(list :done nil
:text ""))
:methods (list
:toggle (lambda ()
(let ((done-ref (plist-get this :done)))
(etaf-ref-set done-ref
(not (etaf-ref-get done-ref)))))
:update-text (lambda (new-text)
(let ((text-ref (plist-get this :text)))
(etaf-ref-set text-ref new-text)))
:reset (lambda ()
(let ((done-ref (plist-get this :done))
(text-ref (plist-get this :text)))
(etaf-ref-set done-ref nil)
(etaf-ref-set text-ref "")))))Key Points:
- Methods have access to
this- the component's data - Methods can call other methods via
(plist-get this :method-name) - Methods are automatically bound to the component context
The :computed option defines derived state that automatically updates when dependencies change.
(etaf-define-component shopping-cart
:data (lambda ()
(list :items '()
:tax-rate 0.08))
:computed (list
:subtotal (lambda ()
(let ((items-ref (plist-get this :items)))
(apply #'+ (mapcar (lambda (item)
(plist-get item :price))
(etaf-ref-get items-ref)))))
:tax (lambda ()
(let ((subtotal-computed (plist-get this :subtotal))
(tax-rate-ref (plist-get this :tax-rate)))
(* (etaf-computed-get subtotal-computed)
(etaf-ref-get tax-rate-ref))))
:total (lambda ()
(let ((subtotal-computed (plist-get this :subtotal))
(tax-computed (plist-get this :tax)))
(+ (etaf-computed-get subtotal-computed)
(etaf-computed-get tax-computed))))))Key Points:
- Computed properties are cached and only recompute when dependencies change
- Access computed values with
etaf-computed-get - Computed properties can depend on other computed properties
- Use
thisto access component data and other computed properties
The :watch option allows you to react to data changes.
(etaf-define-component search-input
:data (lambda ()
(list :query ""
:results '()))
:watch (list
:query (lambda (new-val old-val)
(message "Search query changed: %s -> %s" old-val new-val)
;; Perform search with new query
(when (> (length new-val) 2)
;; Trigger search...
)))
:methods (list
:search (lambda ()
;; Search logic here
)))Key Points:
- Watchers receive
(new-value old-value)as arguments - Watchers are called whenever the watched property changes
- Watch works with both data properties and computed properties
- Use watchers for side effects like API calls, logging, etc.
The Options API supports lifecycle hooks for component initialization and cleanup.
(etaf-define-component data-fetcher
:props '(:user-id)
:data (lambda ()
(list :user-data nil
:loading t
:error nil))
:mounted (lambda ()
(message "Component mounted, fetching data...")
;; Fetch data when component mounts
(let ((user-id (plist-get props :user-id)))
;; Async data fetch would go here
))
:updated (lambda ()
(message "Component updated"))
:unmounted (lambda ()
(message "Component unmounted, cleaning up...")
;; Cleanup subscriptions, timers, etc.
))Available Hooks:
- :mounted - Called when component is first rendered and added to the DOM
- :updated - Called when component re-renders due to data changes
- :unmounted - Called when component is removed from the DOM
Note: Currently, :mounted is fully supported. :updated and :unmounted hooks require deeper integration with the virtual DOM lifecycle system.
Templates define how components render. They can be static or dynamic.
Most common - a function that receives data and returns ETML:
:template (lambda (data)
`(div :class "widget"
(h2 "Count: " ,(etaf-ref-get (plist-get data :count)))
(button :on-click ,(plist-get data :increment)
"Increment")))For simple components without dynamic data:
:template '(div :class "static-component"
(p "This never changes"))Templates have access to:
- All data returned from setup
- Props (merged into data)
- Special
$slotsprop for children
ETAF's reactive system automatically tracks dependencies and triggers updates when data changes.
Create a reactive reference to a value:
(let ((count (etaf-ref 0)))
;; Read value
(etaf-ref-get count) ; => 0
;; Set value (triggers updates)
(etaf-ref-set count 5)
;; Update with function
(etaf-ref-update count #'1+))Key Points:
- Use
etaf-refto create reactive references - Use
etaf-ref-getto read (tracks dependencies) - Use
etaf-ref-setto write (triggers updates) - Use
etaf-ref-updatefor functional updates
Create derived reactive values:
(let* ((price (etaf-ref 100))
(quantity (etaf-ref 2))
(total (etaf-computed
(lambda ()
(* (etaf-ref-get price)
(etaf-ref-get quantity))))))
(etaf-computed-get total) ; => 200
;; Change dependency
(etaf-ref-set quantity 3)
;; Automatically recomputes
(etaf-computed-get total) ; => 300
)Features:
- Lazy evaluation - only computes when accessed
- Automatic caching - only recomputes when dependencies change
- Automatic dependency tracking
- Can depend on other computed values
Watch a specific reactive source and run a callback when it changes:
(let* ((count (etaf-ref 0))
(stop (etaf-watch
count
(lambda (new-val old-val)
(message "Count changed: %s -> %s" old-val new-val)))))
(etaf-ref-set count 1) ; Logs: "Count changed: 0 -> 1"
(etaf-ref-set count 2) ; Logs: "Count changed: 1 -> 2"
;; Stop watching
(funcall stop)
(etaf-ref-set count 3) ; No log
)Options:
(etaf-watch source callback
'(:immediate t)) ; Run callback immediately with current valueAutomatically track dependencies and run effect when they change:
(let* ((firstName (etaf-ref "John"))
(lastName (etaf-ref "Doe"))
(stop (etaf-watch-effect
(lambda ()
(message "Name: %s %s"
(etaf-ref-get firstName)
(etaf-ref-get lastName))))))
;; Immediately logs: "Name: John Doe"
(etaf-ref-set firstName "Jane") ; Logs: "Name: Jane Doe"
(etaf-ref-set lastName "Smith") ; Logs: "Name: Jane Smith"
;; Stop effect
(funcall stop)
)Key Differences from watch:
- Runs immediately
- Automatically tracks all accessed refs
- No need to specify sources explicitly
- Most commonly used for side effects
Create a reactive object from a plist:
(let ((user (etaf-reactive '(:name "Alice" :age 30))))
;; Read values
(etaf-reactive-get user :name) ; => "Alice"
;; Update values
(etaf-reactive-set user :name "Bob")
(etaf-reactive-set user :age 31)
;; Watch changes
(etaf-watch-effect
(lambda ()
(message "User: %s, Age: %d"
(etaf-reactive-get user :name)
(etaf-reactive-get user :age))))
;; Convert back to plist
(etaf-reactive-to-plist user) ; => (:name "Bob" :age 31)
)When to Use:
- Multiple related properties
- Object-like data structures
- Need to add properties dynamically
Slots allow components to accept children elements.
Children are passed via the special :$slots prop:
(etaf-define-component card
:props '(:title)
:template (lambda (data)
(let ((title (plist-get data :title))
(slots (plist-get data :$slots)))
`(div :class "card"
(h2 :class "card-title" ,title)
(div :class "card-body"
,@slots)))))
;; Usage with slots
(card :title "My Card"
(p "This is the card content")
(button "Action")):template (lambda (data)
(let ((slots (plist-get data :$slots)))
(if slots
`(div ,@slots)
`(div (p "No content provided")))))Components integrate with ETAF's Virtual DOM lifecycle hooks:
Called when component is first rendered:
:setup (lambda (props)
(let ((data (etaf-ref nil)))
;; Fetch data when component mounts
(etaf-watch-effect
(lambda ()
;; This runs on mount and whenever dependencies change
(setq data (fetch-data))))
(list :data data)))Use watch or watchEffect to respond to changes:
:setup (lambda (props)
(let ((count (etaf-ref 0)))
;; Watch for changes
(etaf-watch count
(lambda (new-val old-val)
(message "Count updated: %s" new-val)))
(list :count count)))Watch and watchEffect return stop functions for cleanup:
:setup (lambda (props)
(let* ((count (etaf-ref 0))
(stop (etaf-watch-effect
(lambda ()
;; Effect logic
))))
;; Save stop function if needed
(list :count count
:cleanup stop)))ETAF supports both Vue 2's Options API and Vue 3's Composition API:
| Feature | Vue 2 | Vue 3 | ETAF |
|---|---|---|---|
| Component Definition | Vue.component() / export default |
defineComponent() |
etaf-define-component |
| Options API | |||
| Data | data() |
data() |
:data function |
| Methods | methods: {} |
methods: {} |
:methods plist |
| Computed | computed: {} |
computed: {} |
:computed plist |
| Watch | watch: {} |
watch: {} |
:watch plist |
| Lifecycle - Mounted | mounted() |
mounted() |
:mounted |
| Lifecycle - Updated | updated() |
updated() |
:updated |
| Lifecycle - Unmounted | beforeDestroy() |
unmounted() |
:unmounted |
| Composition API | |||
| Setup | Not available | setup() |
:setup |
| Reactive Refs | Not available | ref() |
etaf-ref |
| Computed Values | Not available | computed() |
etaf-computed |
| Watch | Not available | watch() |
etaf-watch |
| Watch Effect | Not available | watchEffect() |
etaf-watch-effect |
| Reactive Objects | Not available | reactive() |
etaf-reactive |
| Common | |||
| Props | props: [] |
props: [] |
:props list |
| Template | template: "" |
template: "" |
:template |
| Emits | $emit() |
emits: [] |
:emits list |
| Slots | <slot> |
<slot> |
:$slots prop |
- Language - Vue uses JavaScript, ETAF uses Emacs Lisp
- Templates - Vue uses HTML-like syntax, ETAF uses S-expressions (ETML)
- Data Structures - ETAF uses plists instead of JavaScript objects
- Rendering - ETAF renders to text buffers, Vue renders to DOM
- Context Binding - ETAF uses
thisvariable in Options API (similar to Vue 2)
All three (Vue 2, Vue 3, and ETAF) share these principles:
- Declarative Rendering
- Component-Based Architecture
- Reactive Data Binding
- Automatic Dependency Tracking
ETAF's dual API support means:
- Options API - Great for beginners and simple components (like Vue 2)
- Composition API - Better for complex logic and code reuse (like Vue 3)
- Your Choice - Use either or both styles in the same project!
If you're familiar with Vue, here's how to think about ETAF:
From Vue 2:
// Vue 2
export default {
data() {
return { count: 0 }
},
methods: {
increment() { this.count++ }
}
};; ETAF Options API
(etaf-define-component my-component
:data (lambda () (list :count 0))
:methods (list
:increment (lambda ()
(etaf-ref-update (plist-get this :count) #'1+))))From Vue 3:
// Vue 3
import { ref } from 'vue'
export default {
setup() {
const count = ref(0)
const increment = () => count.value++
return { count, increment }
}
};; ETAF Composition API
(etaf-define-component my-component
:setup (lambda (props)
(let* ((count (etaf-ref 0))
(increment (lambda () (etaf-ref-update count #'1+))))
(list :count count :increment increment))))(etaf-define-component name &rest options)Define a component. Returns the component name.
Options:
:props- List of prop names:setup- Setup function (props) → data plist:template- Template function or s-expression:emits- List of emitted events
(etaf-component-get name)Get component definition by name.
(etaf-component-defined-p name)Check if component is registered.
(etaf-component-list-all)List all registered component names.
- :setup - Setup function
(lambda (props) -> plist)- Receives props, returns reactive data and methods
- Has access to all Composition API functions (ref, computed, watch, etc.)
-
:data - Data function
(lambda () -> plist)- Returns initial component data
- All values automatically become reactive refs
-
:methods - Methods plist
(list :method1 fn1 :method2 fn2 ...)- Functions that modify component state
- Have access to
this(component data)
-
:computed - Computed properties plist
(list :prop1 getter1 :prop2 getter2 ...)- Derived state that updates automatically
- Getters have access to
this
-
:watch - Watchers plist
(list :prop1 watcher1 :prop2 watcher2 ...)- Watch functions receive
(new-value old-value) - Triggered when watched property changes
- Watch functions receive
-
:mounted - Lifecycle hook
(lambda () ...)- Called when component is first rendered
-
:updated - Lifecycle hook
(lambda () ...)- Called when component re-renders (requires vdom integration)
-
:unmounted - Lifecycle hook
(lambda () ...)- Called when component is removed (requires vdom integration)
- :props - List of prop names
'(:prop1 :prop2 ...) - :template - Template function or s-expression
- :render - Custom render function (advanced)
- :emits - List of emittable events
'(:event1 :event2 ...)
(etaf-ref initial-value)Create a reactive reference.
(etaf-ref-get ref)Get the current value of a ref.
(etaf-ref-set ref value)Set a new value and trigger updates.
(etaf-ref-update ref update-fn)Update ref by applying function to current value.
(etaf-computed getter-fn)Create a computed value.
(etaf-computed-get computed)Get the computed value (recomputes if dirty).
(etaf-watch source callback &optional options)Watch a ref or computed value. Returns stop function.
Options:
:immediate- Run callback immediately
(etaf-watch-effect effect-fn)Run effect and re-run when dependencies change. Returns stop function.
(etaf-reactive plist)Create a reactive object.
(etaf-reactive-get reactive key)Get value from reactive object.
(etaf-reactive-set reactive key value)Set value in reactive object.
(etaf-reactive-to-plist reactive)Convert reactive object to plain plist.
(etaf-define-component simple-button
:props '(:label :variant)
:template (lambda (data)
(let ((label (plist-get data :label))
(variant (or (plist-get data :variant) "primary")))
`(button :class ,(format "btn btn-%s" variant)
,label))))
;; Usage
(simple-button :label "Click Me" :variant "success")(etaf-define-component counter
:props '(:initial)
:setup (lambda (props)
(let* ((count (etaf-ref (or (plist-get props :initial) 0)))
(increment (lambda ()
(etaf-ref-update count #'1+)))
(decrement (lambda ()
(etaf-ref-update count #'1-)))
(reset (lambda ()
(etaf-ref-set count 0))))
(list :count count
:increment increment
:decrement decrement
:reset reset)))
:template (lambda (data)
`(div :class "counter"
(button :on-click ,(plist-get data :decrement) "-")
(span :class "count"
,(format "%d" (etaf-ref-get (plist-get data :count))))
(button :on-click ,(plist-get data :increment) "+")
(button :on-click ,(plist-get data :reset) "Reset"))))
;; Usage
(counter :initial 10)(etaf-define-component todo-list
:props '(:initial-items)
:setup (lambda (props)
(let* ((items (etaf-ref (or (plist-get props :initial-items) '())))
(new-text (etaf-ref ""))
(add-item (lambda ()
(let ((text (etaf-ref-get new-text)))
(when (not (string-empty-p text))
(etaf-ref-set items
(append (etaf-ref-get items)
(list (list :text text :done nil))))
(etaf-ref-set new-text "")))))
(toggle-item (lambda (index)
(let* ((current (etaf-ref-get items))
(item (nth index current))
(updated (plist-put (copy-sequence item)
:done
(not (plist-get item :done)))))
(setf (nth index current) updated)
(etaf-ref-set items current))))
(remaining (etaf-computed
(lambda ()
(length (seq-filter
(lambda (item)
(not (plist-get item :done)))
(etaf-ref-get items)))))))
(list :items items
:new-text new-text
:add-item add-item
:toggle-item toggle-item
:remaining remaining)))
:template (lambda (data)
(let ((items (etaf-ref-get (plist-get data :items))))
`(div :class "todo-list"
(div :class "todo-input"
(input :type "text"
:placeholder "What needs to be done?")
(button :on-click ,(plist-get data :add-item)
"Add"))
(ul :class "todo-items"
,@(cl-loop for item in items
for index from 0
collect
`(li :class ,(if (plist-get item :done)
"done" "")
(input :type "checkbox"
:checked ,(plist-get item :done)
:on-change (lambda ()
(funcall ,(plist-get data :toggle-item)
,index)))
(span ,(plist-get item :text)))))
(div :class "todo-footer"
,(format "%d items left"
(etaf-computed-get (plist-get data :remaining))))))))
;; Usage
(todo-list :initial-items '((:text "Learn ETAF" :done nil)
(:text "Build app" :done nil)))(etaf-define-component user-form
:setup (lambda (props)
(let* ((form (etaf-reactive '(:name "" :email "" :age nil)))
(errors (etaf-ref '()))
(validate (lambda ()
(let ((errs '()))
(when (string-empty-p (etaf-reactive-get form :name))
(push '(:name . "Name is required") errs))
(when (string-empty-p (etaf-reactive-get form :email))
(push '(:email . "Email is required") errs))
(etaf-ref-set errors errs)
(null errs))))
(submit (lambda ()
(when (funcall validate)
(message "Form submitted: %S"
(etaf-reactive-to-plist form))))))
(list :form form
:errors errors
:validate validate
:submit submit)))
:template (lambda (data)
(let ((errors (etaf-ref-get (plist-get data :errors))))
`(form :class "user-form"
(div :class "form-field"
(label "Name:")
(input :type "text"
:name "name")
,@(when-let ((err (cdr (assq :name errors))))
`((span :class "error" ,err))))
(div :class "form-field"
(label "Email:")
(input :type "email"
:name "email")
,@(when-let ((err (cdr (assq :email errors))))
`((span :class "error" ,err))))
(button :type "submit"
:on-click ,(plist-get data :submit)
"Submit")))))
;; Usage
(user-form)Break down complex UIs into smaller, reusable components:
;; Good
(etaf-define-component user-card ...)
(etaf-define-component user-avatar ...)
(etaf-define-component user-bio ...)
;; Instead of one large componentDon't duplicate state, compute it:
;; Good
(let ((items (etaf-ref '(...)))
(active-items (etaf-computed
(lambda ()
(seq-filter #'is-active (etaf-ref-get items))))))
...)
;; Avoid
(let ((items (etaf-ref '(...)))
(active-items (etaf-ref '(...)))) ; Must keep in sync manually
...)Prefer watchEffect over watch when you don't need old values:
;; Good - automatic dependency tracking
(etaf-watch-effect
(lambda ()
(message "User: %s" (etaf-ref-get name))))
;; Verbose - explicit dependencies
(etaf-watch name
(lambda (new old)
(message "User: %s" new)))Use descriptive prop names:
;; Good
:props '(:user-name :user-email :is-admin)
;; Avoid
:props '(:name :email :flag)Add docstrings to setup functions:
:setup (lambda (props)
"Setup for TodoList component.
Manages a list of todo items with add/remove/toggle operations."
...)Problem: Error: "Component not registered"
Solution: Make sure to define the component before using it:
;; Define first
(etaf-define-component my-component ...)
;; Then use
(my-component)Problem: UI doesn't update when ref changes
Solution: Make sure you're using etaf-ref-set, not direct mutation:
;; Good
(etaf-ref-set count (1+ (etaf-ref-get count)))
;; Bad - won't trigger updates
(plist-put count :value (1+ (plist-get count :value)))Problem: Computed value shows stale data
Solution: Ensure you're reading dependencies with etaf-ref-get:
;; Good - tracks dependency
(etaf-computed
(lambda ()
(etaf-ref-get my-ref)))
;; Bad - doesn't track
(etaf-computed
(lambda ()
my-ref)) ; Returns ref object, not valueThe component system was extracted from etaf-etml.el to etaf-component.el.
Note: Backward compatibility aliases have been removed. You must update your code to use the new function names.
To update existing code:
-
Change requires:
;; Add this (require 'etaf-component)
-
Update function names (required):
;; Find/replace - old names no longer work etaf-etml-define-component → etaf-define-component etaf-etml-ref → etaf-ref etaf-etml-computed → etaf-computed etaf-etml-watch-source → etaf-watch etaf-etml-watch-effect → etaf-watch-effect etaf-etml-reactive → etaf-reactive etaf-etml-ref-get → etaf-ref-get etaf-etml-ref-set → etaf-ref-set etaf-etml-ref-update → etaf-ref-update etaf-etml-computed-get → etaf-computed-get etaf-etml-reactive-get → etaf-reactive-get etaf-etml-reactive-set → etaf-reactive-set etaf-etml-reactive-to-plist → etaf-reactive-to-plist etaf-component-* → etaf-component-* -
Test your code after updating all function names
- Vue 3 Composition API
- Vue 3 Reactivity in Depth
- ETAF Virtual DOM
- ETAF Event Model
- Component Examples
For questions or issues, please visit the ETAF GitHub repository.