
VanillaCreamJS è una libreria JavaScript che permette letteralmente di aggiungere “super poteri” al JavaScript nativo, rendendo possibili operazioni potenti e moderne, come la reattività, senza la necessità di implementare build-tool e build-step.
È una tecnologia unica nel suo genere, perché:
- Consente di “donare” la reattività al Vanilla JavaScript
- Lavora con performance straordinarie, essendo privo di algoritmi di diffing e ricalcolo
- Fornisce strumenti potenti, ma senza API complesse
- Permette di incrementare le proprie skills relative al JavaScript puro
- Permette di affiancare qualsiasi tecnologia back-end, e quindi di essere integrato in ogni stack di sviluppo
- Incrementa la produttività degli sviluppatori rispetto al Vanilla JavaScript e ai framework tradizionali nei progetti di piccole e medie dimensioni
- Combina le migliori potenzialità di strumenti come HTMX, Vue e Alpine
Il suo nome indica proprio il suo obiettivo: raffinare il Vanilla JavaScript e portarlo ad un livello èlitario, ma senza allontanarsi da esso.
VanillaCream è realizzato e mantenuto da Riccardo Degni, senior web developer italiano.
$uperpoteri e operazioni DOM
Il nucleo di VanillaCream passa dalla funzione $, che a prima vista potrebbe sembrare la vecchia funzionalità di jQuery, ma in realtà offre un universo estremamente differente e moderno di potenzialità.
Questa funzione restituisce un elemento o un set di elementi dotati di super poteri, e permette di agganciare uno stato reattivo:
<div id="app">
<div id="el"></div>
</div>
<script>
const [el, s] = $('#el', {counter: 10})
el.text = () => s.counter
el.onClick(() => s.counter++)
</script>
Code language: HTML, XML (xml)
Qui vediamo come $, restituisca un elemento, identificato dalla variabile el (il nome è a libera scelta) e un oggetto reattivo, collegato all’elemento.
Ogni elemento prodotto con $ ha a disposizione le seguenti proprietà:
- text: permette di impostare un valore testuale per il contenuto
- html: permette di impostare un valore HTML per il contenuto
- class: permette di impostare classi CSS
- on: permette di impostare eventi (anche con la forma on[Event])
- attr: permette di impostare attributi HTML
- data: permette di impostare attributi data-
Quando a una di queste proprietà viene assegnata una funzione, la caratteristica dell’elemento o del set di elementi a cui puntano diventa reattiva.
Infatti, nell’esempio precedente, quando clicchiamo sul div con id “el” incrementiamo il valore di counter nell’oggetto s, modificando automaticamente il textContent dell’elemento stesso.
Ad esempio, per effettuare un “toggle” di una classe, basta scrivere:
<div id="app">
<div id="el">box</div>
</div>
<script>
const [el, s] = $('#el', {hl: true})
el.class.highlighted = () => s.hl
el.on.click(() => s.hl = !s.hl)
</script>
Code language: HTML, XML (xml)
In questo caso, all’elemento con id “el” viene impostata la classe CSS highlighted, dato che il valore di s.hl è true.
Al click sull’elemento variamo il valore booleano del valore di stato assegnato a class, attivando o disattivando di conseguenza la classe CSS sull’elemento in modo reattivo.
Reattività di massa
Possiamo anche usare il metodo set per impostare infinite caratteristiche reattive in un solo colpo:
el.css.set({
color: 'blue',
backgroundColor = () => state.bg,
width = () => state.x + 'px'
})
el.class.set({
active: () => state.active,
box: () => state.counter >= 10,
otherClass: () => state.active && state.counter >= 10
})
el.attr.set({
title: () => `Title: ${state.title}`
})
el.data.set({
x: () => state.x * 10
})
Code language: CSS (css)
Reattività profonda
Il sistema di reattività di VanillaCream è tra i più potenti e flessibili in assoluto: permette di monitorare valori primitivi ma anche valori reference come array, oggetti e matrici. Possiamo infatti azionare la reattività semplicemente variando il valore di una proprietà di un oggetto o di un elemento di un array, oppure usando metodi di mutazione come push, pop, slice e cosi via!
<div id="app">
<div ref="box"></div>
Insert fruit: <input ref="fruit">
<button ref="btn">Add Fruit</button>
</div>
<script>
const {box, fruit, btn, state} = $.refs('#app', {fruits: ['apple', 'pear', 'kiwi']})
box.text = () => 'Fruits: ' + state.fruits.join(', ')
btn.onClick(() => {
state.fruits.push( fruit.attr.value )
fruit.attr.value = ''
})
</script>
Code language: HTML, XML (xml)
In questo esempio, il valore del div con ref box riflette gli elementi dell’array di stato concatenati da virgole. Cliccando sul pulsante btn possiamo aggiungere all’array di stato, semplicemente con push, il valore del campo di input fruit.
Nessun hook da utilizzare, nessuna API complessa, solo puro Vanilla JavaScript!
Riferimenti
Il grande potere di VanillaCream deriva dalla funzione $.refs, che prende in considerazione un elemento radice e produce variabili dotate di super-poteri (sono istanze prodotte internamente con $) che hanno lo stesso nome dei valori degli attributi ref nel template HTML:
<div id="app">
<div ref="el"></div>
<button ref="btn">Update state</button>
</div>
<script>
const {el, btn, state} = $.refs('app', {counter: 0, bg: 'lightblue', boxClass: false})
el.html = () => `Counter: ${state.counter}`
el.css.backgroundColor = () => state.bg
el.class.box = () => state.boxClass
btn.onClick(() => {
state.counter++
if(state.bg != 'lightgreen') state.bg = 'lightgreen'
state.boxClass = !state.boxClass
})
</script>
Code language: HTML, XML (xml)
In questo caso:
- la variabile el si riferisce all’elemento con ref “el”
- la variabile btn si riferisce all’elemento con ref “btn”
- la variabile state è l’oggetto reattivo di questa istanza di lavoro
- el avrà un testo che riflette reattivamente il contenuto di state.counter
- el avrà un background-color che riflette nativamente il valore di state.bg
- el avrà la classe CSS chiamata box impostata se state.boxClass è true
- al click sul pulsante btn, counter incrementa, bg assume il valore lightgreen e boxClass cambia il suo valore. Questi settings alterano reattivamente le caratterisitche di el
Possiamo anche usare namespace nei riferimenti, per ridurre il numero di variabili destrutturate da $.refs e raggrupparle logicamente, ad esempio:
<div id="app">
<button ref="btn.AZ">Sort A-Z</button>
<button ref="btn.ZA">Sort Z-A</button>
<button ref="btn.Random">Randomize</button>
<ul ref="el"></ul>
</div>
<script>
const { el, btn, state } = $.refs('#app', {...});
// ...
</script>
Code language: HTML, XML (xml)
Reattivià dal template HTML
VanillaCream permette di aggiornare lo stato direttamente dal template HTML, attraverso gli attributi x- che permettono di targetizzare le relative proprietà che abbiamo analizzato:
<div id="app">
<div x-text="`Counter: ${state.counter}`"></div>
<button x-event.click="() => state.counter++">Increase counter</button>
<div x-if="state.counter === 10">Counter is 10!</div>
</div>
<script>
const {state} = $.refs('#app', {counter: 0})
</script>
Code language: HTML, XML (xml)
In questo esempio, all’interno di #app:
- il contenuto del primo div riflette reattivamente il valore di counter
- cliccando sul pulsante si incrementa il valore di counter
- il terzo div compare nel DOM se il valore di counter è uguale a 10
Richieste AJAX: controllo totale
Possiamo realizzare richieste ajax in molteplici modi, ottenendo un controllo totale su tutte le loro caratteristiche.
Ad esempio, per creare una richiesta POST da JavaScript:
const result = await $.post('https://jsonplaceholder.typicode.com/posts', {
data: {
title: 'VanillaCream.js',
body: 'Best Vanilla JS framework',
userId: 1
}
});
el.html = result
Code language: JavaScript (javascript)
Per caricare direttamente in un elemento HTML il risultato di una richiesta:
<div id="app">
<div ref="el"></div>
</div>
<script>
const {el} = $.refs('#app')
el.post('https://jsonplaceholder.typicode.com/posts/', {
start: () => el.text = 'Loading...',
data: {title: 'VanillaCream.js', body: 'Best Vanilla JS framework', userId: 1}
})
</script>
Code language: HTML, XML (xml)
Inoltre, abbiamo la possibilità di creare richieste ajax direttamente dal template HTML, senza utilizzare codice JavaScript:
<div x-get="{
url: 'https://httpbin.org/html',
headers: {'Content-type': 'application/json', 'X-Custom': 'Ciao!'}
on: 'click'
}">Click here</div>
Code language: JavaScript (javascript)
Con questo codice, cliccando sull’elemento creiamo una richiesta ajax di tipo GET grazie all’attributo x-get settando determinate intestazioni HTTP e caricando automaticamente il contenuto al termine della richiesta.
Componenti e back-end
Anche se VanillaCream offre la possibilità di realizzare componenti front-end, possiamo affiancarlo a qualsiasi tecnologia back-end e delegare a questa la componentizzazione, in modo da modularizzare le applicazioni.
Ad esempio, in Laravel potremmo avere un componente che recupera stringhe dal database e le collega al front-end, dove VanillaCream attua la reattività:
<!—add-fruit.blade.php -->
<div id="app">
<div ref="box"></div>
Insert fruit: <input ref="fruit">
<button ref="btn">Add Fruit</button>
</div>
<script>
const {box, fruit, btn, state} = $.refs('#app', {
fruits: @json($fruits)
});
box.text = () => 'Fruits: ' + state.fruits.join(', ')
btn.onClick(async () => {
const v = fruit.attr.value
await $.get('/my-backend/add-fruit', {
data: {fruit: v}
});
state.fruits.push( v )
fruit.attr.value = ''
})
</script>
Code language: HTML, XML (xml)
… molto altro!
Come puoi ricavare, VanillaCream è uno strumento rivoluzionario e molto potente, ma le sue potenzialità non finiscono qui. Abbiamo a disposizione:
- computed properties
- side effects
- componenti
- subset di elementi
- stati globali
- … e molto altro!
Si tratta di una tecnologia moderna e flessibile che consente di creare UI/UX reattive in modo semplice e raffinato.
Sul sito ufficiale è possibile trovare:
- La documentazione completa
- Una serie di demo illustrative
- Un playground per testare VanillaCream sul campo