Cosa risolvono i Web Framework e come farne a meno (Parte 1)

Pubblicato: 2022-03-10
Riassunto rapido ↬ In questo articolo, Noam Rosenthal approfondisce alcune caratteristiche tecniche comuni a tutti i framework e spiega come alcuni dei diversi framework li implementano e quanto costano.

Recentemente sono diventato molto interessato a confrontare i framework con JavaScript vanilla. È iniziato dopo una certa frustrazione nell'usare React in alcuni dei miei progetti freelance e con la mia recente e più intima conoscenza degli standard web come editor di specifiche.

Mi interessava vedere quali sono i punti in comune e le differenze tra i framework , cosa offre la piattaforma web come alternativa più snella e se è sufficiente. Il mio obiettivo non è colpire i framework, ma piuttosto capire i costi ei benefici, determinare se esiste un'alternativa e vedere se possiamo imparare da essa, anche se decidiamo di utilizzare un framework.

In questa prima parte, approfondirò alcune caratteristiche tecniche comuni tra i framework e come alcuni dei diversi framework le implementano. Esaminerò anche il costo dell'utilizzo di tali framework.

I Quadri

Ho scelto quattro framework da considerare: React, che è oggi quello dominante, e tre nuovi contendenti che affermano di fare le cose in modo diverso da React.

  • Reagire
    “React rende indolore la creazione di interfacce utente interattive. Le visualizzazioni dichiarative rendono il codice più prevedibile e più facile da eseguire il debug".
  • SolidJS
    "Solid segue la stessa filosofia di React... Tuttavia ha un'implementazione completamente diversa che rinuncia all'utilizzo di un DOM virtuale."
  • Svelto
    “Svelte è un approccio radicalmente nuovo alla creazione di interfacce utente... un passaggio di compilazione che si verifica quando crei la tua app. Invece di utilizzare tecniche come il DOM diffing virtuale, Svelte scrive un codice che aggiorna chirurgicamente il DOM quando cambia lo stato della tua app".
  • Illuminato
    "Costruendo sulla base degli standard dei componenti Web, Lit aggiunge solo... reattività, modelli dichiarativi e una manciata di funzioni ponderate".

Per riassumere cosa dicono i framework sui loro differenziatori:

  • React semplifica la creazione di interfacce utente con viste dichiarative.
  • SolidJS segue la filosofia di React ma utilizza una tecnica diversa.
  • Svelte utilizza un approccio in fase di compilazione alle interfacce utente.
  • Lit utilizza gli standard esistenti, con alcune funzionalità leggere aggiunte.

Quali framework risolvono

I framework stessi menzionano le parole dichiarativo, reattività e DOM virtuale. Immergiamoci in cosa significano.

Programmazione dichiarativa

La programmazione dichiarativa è un paradigma in cui la logica è definita senza specificare il flusso di controllo. Descriviamo quale deve essere il risultato, piuttosto che quali passi ci porterebbero lì.

Agli albori dei framework dichiarativi, intorno al 2010, le API DOM erano molto più semplici e dettagliate e la scrittura di applicazioni Web con JavaScript imperativo richiedeva molto codice standard. Fu allora che il concetto di "model-view-viewmodel" (MVVM) divenne prevalente, con gli allora innovativi framework Knockout e AngularJS, che fornivano un livello dichiarativo JavaScript che gestiva quella complessità all'interno della libreria.

MVVM non è un termine ampiamente utilizzato oggi ed è in qualche modo una variazione del vecchio termine "data-binding".

Associazione dati

Il data binding è un modo dichiarativo per esprimere il modo in cui i dati vengono sincronizzati tra un modello e un'interfaccia utente.

Tutti i framework dell'interfaccia utente più diffusi forniscono una qualche forma di associazione dati e le esercitazioni iniziano con un esempio di associazione dati.

Ecco l'associazione dei dati in JSX (SolidJS e React):

 function HelloWorld() { const name = "Solid or React"; return ( <div>Hello {name}!</div> ) }

Associazione dati in acceso:

 class HelloWorld extends LitElement { @property() name = 'lit'; render() { return html`<p>Hello ${this.name}!</p>`; } }

Rilegatura dati in Svelte:

 <script> let name = 'world'; </script> <h1>Hello {name}!</h1>

Reattività

La reattività è un modo dichiarativo per esprimere la propagazione del cambiamento.

Quando abbiamo un modo per esprimere in modo dichiarativo l'associazione dei dati, abbiamo bisogno di un modo efficiente per il framework di propagare le modifiche.

Il motore React confronta il risultato del rendering con il risultato precedente e applica la differenza al DOM stesso. Questo modo di gestire la propagazione delle modifiche è chiamato DOM virtuale.

In SolidJS, questo viene fatto in modo più esplicito, con il suo negozio e gli elementi integrati. Ad esempio, l'elemento Show terrà traccia di ciò che è cambiato internamente, invece del DOM virtuale.

In Svelte viene generato il codice “reattivo”. Svelte sa quali eventi possono causare una modifica e genera un codice semplice che traccia il confine tra l'evento e la modifica DOM.

In Lit, la reattività viene ottenuta utilizzando le proprietà degli elementi, basandosi essenzialmente sulla reattività incorporata degli elementi personalizzati HTML.

Altro dopo il salto! Continua a leggere sotto ↓

Logica

Quando un framework fornisce un'interfaccia dichiarativa per l'associazione dei dati, con la sua implementazione della reattività, deve anche fornire un modo per esprimere parte della logica tradizionalmente scritta in modo imperativo. Gli elementi costitutivi di base della logica sono "se" e "per", e tutti i principali framework forniscono un'espressione di questi elementi costitutivi.

Condizionali

Oltre a vincolare dati di base come numeri e stringhe, ogni framework fornisce una primitiva "condizionale". In React, si presenta così:

 const [hasError, setHasError] = useState(false); return hasError ? <label>Message</label> : null; … setHasError(true);

SolidJS fornisce un componente condizionale integrato, Show :

 <Show when={state.error}> <label>Message</label> </Show>

Svelte fornisce la direttiva #if :

 {#if state.error} <label>Message</label> {/if}

In Lit, useresti un'operazione ternaria esplicita nella funzione di render :

 render() { return this.error ? html`<label>Message</label>`: null; }

Elenchi

L'altra primitiva del framework comune è la gestione delle liste. Gli elenchi sono una parte fondamentale delle interfacce utente (elenco di contatti, notifiche, ecc.) e per funzionare in modo efficiente, devono essere reattivi, non aggiornare l'intero elenco quando un elemento di dati cambia.

In React, la gestione degli elenchi è simile a questa:

 contacts.map((contact, index) => <li key={index}> {contact.name} </li>)

React utilizza l'attributo key speciale per differenziare gli elementi dell'elenco e si assicura che l'intero elenco non venga sostituito ad ogni rendering.

In SolidJS, vengono utilizzati gli elementi integrati for e index :

 <For each={state.contacts}> {contact => <DIV>{contact.name}</DIV> } </For>

Internamente, SolidJS utilizza il proprio archivio insieme a for e index per decidere quali elementi aggiornare quando gli elementi cambiano. È più esplicito di React, permettendoci di evitare la complessità del DOM virtuale.

Svelte utilizza la direttiva each , che viene trasferita in base ai suoi programmi di aggiornamento:

 {#each contacts as contact} <div>{contact.name}</div> {/each}

Lit fornisce una funzione di repeat , che funziona in modo simile alla mappatura dell'elenco basata su key di React:

 repeat(contacts, contact => contact.id, (contact, index) => html`<div>${contact.name}</div>`

Modello componente

Una cosa che non rientra nell'ambito di questo articolo è il modello dei componenti nei diversi framework e come può essere gestito utilizzando elementi HTML personalizzati.

Nota : questo è un argomento importante e spero di trattarlo in un articolo futuro perché questo diventerebbe troppo lungo. :)

Il costo

I framework forniscono associazione dati dichiarativa, primitive di flusso di controllo (condizionali ed elenchi) e un meccanismo reattivo per propagare le modifiche.

Forniscono anche altre cose importanti, come un modo per riutilizzare i componenti, ma questo è un argomento per un articolo separato.

I framework sono utili? Sì. Ci danno tutte queste comode funzionalità. Ma è la domanda giusta da porsi? L'uso di un framework ha un costo. Vediamo quali sono questi costi.

Dimensione del pacco

Quando guardo le dimensioni del pacchetto, mi piace guardare le dimensioni ridotte non Gzip'd. Questa è la dimensione più rilevante per il costo della CPU dell'esecuzione di JavaScript.

  • ReactDOM è di circa 120 KB.
  • SolidJS è di circa 18 KB.
  • Acceso è di circa 16 KB.
  • Svelte è di circa 2 KB, ma la dimensione del codice generato varia.

Sembra che i framework odierni stiano facendo un lavoro migliore di React nel mantenere piccole le dimensioni del pacchetto. Il DOM virtuale richiede molto JavaScript.

Costruisce

In qualche modo ci siamo abituati a "costruire" le nostre app web. È impossibile avviare un progetto front-end senza configurare Node.js e un bundler come Webpack, gestire alcune recenti modifiche alla configurazione nello starter pack Babel-TypeScript e tutto quel jazz.

Più espressivo e minore è la dimensione del pacchetto del framework, maggiore sarà il carico degli strumenti di compilazione e del tempo di traspirazione.

Svelte afferma che il DOM virtuale è un puro sovraccarico. Sono d'accordo, ma forse anche "costruzione" (come con Svelte e SolidJS) e motori di modelli lato client personalizzati (come con Lit) sono pure spese generali, di tipo diverso?

Debug

Con la costruzione e la traspirazione derivano un diverso tipo di costo.

Il codice che vediamo quando utilizziamo o eseguiamo il debug dell'app Web è totalmente diverso da quello che abbiamo scritto. Ora ci affidiamo a speciali strumenti di debug di varia qualità per decodificare ciò che accade sul sito Web e collegarlo a bug nel nostro codice.

In React, lo stack delle chiamate non è mai "tuo" — React gestisce la pianificazione per te. Funziona benissimo quando non ci sono bug. Ma prova a identificare la causa dei re-rendering a ciclo infinito e ti troverai in un mondo di dolore.

In Svelte, la dimensione del bundle della libreria stessa è piccola, ma spedirai ed eseguirai il debug di un intero gruppo di codice generato criptico che è l'implementazione della reattività di Svelte, personalizzata in base alle esigenze della tua app.

Con Lit, si tratta meno di costruire, ma per eseguire il debug in modo efficace devi comprendere il suo motore di modelli. Questo potrebbe essere il motivo principale per cui il mio sentimento nei confronti dei framework è scettico.

Quando cerchi soluzioni dichiarative personalizzate, ti ritrovi con un debug imperativo più doloroso. Gli esempi in questo documento utilizzano Typescript per la specifica API, ma il codice stesso non richiede la trascrizione.

Aggiornamenti

In questo documento, ho esaminato quattro framework, ma ci sono più framework di quanti ne possa contare (AngularJS, Ember.js e Vue.js, solo per citarne alcuni). Puoi contare sul framework, sui suoi sviluppatori, sulla sua condivisione mentale e sul suo ecosistema per lavorare per te mentre si evolve?

Una cosa che è più frustrante della correzione dei propri bug è dover trovare soluzioni alternative per i bug del framework. E una cosa che è più frustrante dei bug del framework sono i bug che si verificano quando si aggiorna un framework a una nuova versione senza modificare il codice.

È vero, questo problema esiste anche nei browser, ma quando si verifica, capita a tutti e nella maggior parte dei casi è imminente una correzione o una soluzione alternativa pubblicata. Inoltre, la maggior parte dei modelli in questo documento si basa su API di piattaforme web mature; non c'è sempre bisogno di andare con il bordo sanguinante.

Sommario

Ci siamo immersi un po' più a fondo nella comprensione dei problemi fondamentali che i framework cercano di risolvere e di come li risolvono, concentrandoci su data binding, reattività, condizionali ed elenchi. Abbiamo anche considerato il costo.

Nella parte 2, vedremo come questi problemi possono essere affrontati senza utilizzare affatto un framework e cosa possiamo imparare da esso. Rimani sintonizzato!

Un ringraziamento speciale alle seguenti persone per le revisioni tecniche: Yehonatan Daniv, Tom Bigelajzen, Benjamin Greenbaum, Nick Ribal e Louis Lazaris.