Manuale di React

Il Manuale di React segue la regola 80/20: impara nel 20% delle volte l'80% di un argomento.

Trovo che questo approccio offra una panoramica completa. Questo libro non cerca di coprire tutto ciò che è sotto il sole relativo a React, ma dovrebbe darti i mattoni di base per uscire e diventare un grande sviluppatore di React. Se pensi che debba essere incluso un argomento specifico, dimmelo. Puoi contattarmi su Twitter @flaviocopes.

Spero che i contenuti di questo libro ti aiuteranno a ottenere ciò che desideri: apprendi le basi di React.

È possibile ottenere questo ebook in formato PDF, ePub e Mobi sul sito Webhandhandbook.com

Indice del libro

Sommario

Un'introduzione a React
Come usare create -eagire-app

SEZIONE 1: CONCETTI MODERNI DI JAVASCRIPT CHE DEVI SAPERE PER UTILIZZARE REACT

  • variabili
  • Funzioni freccia
  • Riposa e diffondi
  • Distruzione di oggetti e array
  • Letterali modello
  • Classi
  • callback
  • promesse
  • Async / Await
  • Moduli ES

SEZIONE 2: CONCETTI DI REACT

  • Applicazioni a pagina singola
  • Dichiarativo
  • Immutabilità
  • Purezza
  • Composizione
  • Il DOM virtuale
  • Flusso di dati unidirezionale

SEZIONE 3: REACT IN PROFONDITÀ

  • JSX
  • componenti
  • Stato
  • puntelli
  • Componenti di presentazione vs contenitore
  • Stato vs oggetti di scena
  • PropTypes
  • Reagire frammento
  • eventi
  • Eventi del ciclo di vita
  • Forme in React
  • Fai riferimento a un elemento DOM
  • Rendering lato server
  • L'API di contesto
  • Componenti di ordine superiore
  • Rendering di oggetti di scena
  • ganci
  • Frazionamento del codice

SEZIONE 4: ESEMPI PRATICI

  • Costruisci un semplice contatore
  • Recupera e visualizza le informazioni degli utenti di GitHub tramite API

SEZIONE 5: STYLING

  • CSS in React
  • SASS in React
  • Componenti in stile

SEZIONE 6: STRUMENTI

  • Babele
  • Webpack

SEZIONE 7: PROVE

  • Scherzare
  • Test dei componenti di React

SEZIONE 8: L'ECOSISTEMA DI REACT

  • Reagisci router
  • Redux
  • Next.js
  • Gatsby

Avvolgendo

Un'introduzione alla libreria della vista React

Che cos'è React?

React è una libreria JavaScript che mira a semplificare lo sviluppo di interfacce visive.

Sviluppato su Facebook e rilasciato nel mondo nel 2013, guida alcune delle app più utilizzate, alimentando Facebook e Instagram tra innumerevoli altre applicazioni.

Il suo obiettivo principale è rendere facile ragionare su un'interfaccia e il suo stato in qualsiasi momento, dividendo l'interfaccia utente in una raccolta di componenti.

Perché React è così popolare?

React ha preso d'assalto il mondo dello sviluppo web di frontend. Perché?

Meno complesso rispetto alle altre alternative

All'epoca in cui React fu annunciato, Ember.js e Angular 1.x erano le scelte predominanti come framework. Entrambi hanno imposto così tante convenzioni sul codice che il porting di un'app esistente non era affatto conveniente.

React ha fatto la scelta di essere molto facile da integrare in un progetto esistente, perché è così che hanno dovuto farlo su Facebook per introdurlo alla base di codice esistente. Inoltre, quei 2 framework hanno portato troppo alla tabella, mentre React ha scelto di implementare solo il layer View anziché l'intero stack MVC.

Tempismo perfetto

A quel tempo, Angular 2.x fu annunciato da Google, insieme all'incompatibilità all'indietro e ai principali cambiamenti che avrebbe portato. Passare da Angular 1 a 2 è stato come passare a un framework diverso, quindi questo, insieme ai miglioramenti della velocità di esecuzione promessi da React, ha reso qualcosa che gli sviluppatori erano desiderosi di provare.

Supportato da Facebook

Essere sostenuti da Facebook, ovviamente, andrà a beneficio di un progetto se si rivelerà un successo.

Facebook ha attualmente un forte interesse per React, vede il valore di essere Open Source e questo è un enorme vantaggio per tutti gli sviluppatori che lo usano nei propri progetti.

React è semplice da imparare?

Anche se ho detto che React è più semplice dei framework alternativi, immergersi in React è ancora complicato, ma principalmente a causa delle tecnologie corollarie che possono essere integrate con React, come Redux e GraphQL.

React in sé ha un'API molto piccola e per iniziare devi sostanzialmente comprendere 4 concetti:

  • componenti
  • JSX
  • Stato
  • puntelli

Tutti questi (e altro) sono spiegati in questo manuale.

Come installare React sul tuo computer di sviluppo

Come si installa React?

React è una libreria, quindi dire che install potrebbe sembrare un po 'strano. Forse l'installazione è una parola migliore, ma ottieni il concetto.

Esistono diversi modi per configurare React in modo che possa essere utilizzato sulla tua app o sul tuo sito.

Carica React direttamente nella pagina web

Il più semplice è aggiungere il file React JavaScript direttamente nella pagina. È preferibile quando l'app React interagirà con gli elementi presenti in una singola pagina e non controlla effettivamente l'intero aspetto della navigazione.

In questo caso, aggiungi 2 tag di script alla fine del tag body:


  ...
  
    ...
    

e carica i tuoi script con lo speciale tipo MIME text / babel:

Ora puoi aggiungere JSX nel tuo file app.js:

pulsante const = () => {
  return 
}
ReactDOM.render (

Dai un'occhiata a questo semplice esempio di Glitch: https://glitch.com/edit/#!/react-example-inline-jsx?path=script.js

Iniziare in questo modo con i tag di script è utile per la creazione di prototipi e consente un avvio rapido senza dover impostare un flusso di lavoro complesso.

Come usare create -eagire-app

create -eagire-app è un progetto volto a metterti al passo con React in pochissimo tempo, e qualsiasi app React che ha bisogno di superare una singola pagina troverà che create -eagire-app soddisfa tale esigenza.

Inizi usando npx, che è un modo semplice per scaricare ed eseguire i comandi Node.js senza installarli. npx viene fornito con npm (dalla versione 5.2) e se non hai già installato npm, fallo ora da https://nodejs.org (npm è installato con Node).

Se non si è sicuri della versione di npm in uso, eseguire npm -v per verificare se è necessario aggiornare.

Suggerimento: dai un'occhiata al mio tutorial sul terminale OSX se non hai familiarità con l'uso del terminale, vale anche per Linux - mi dispiace ma al momento non ho un tutorial per Windows, ma Google è tuo amico.

Quando si esegue npx create -eagire-app , npx scaricherà la versione più recente di create-reagire-app, eseguirla e quindi rimuoverla dal sistema. Questo è fantastico perché non avrai mai una versione obsoleta sul tuo sistema e ogni volta che lo esegui, otterrai l'ultimo e il più grande codice disponibile.

Iniziamo quindi:

todolist di npx create -eagire-app

Questo è quando ha finito di funzionare:

create-reply-app ha creato una struttura di file nella cartella che hai detto (todolist in questo caso) e ha inizializzato un repository Git.

Ha anche aggiunto alcuni comandi nel file package.json, in modo da poter avviare immediatamente l'app accedendo alla cartella ed eseguire npm start.

Oltre a npm start, create-reply-app ha aggiunto alcuni altri comandi:

  • npm run build: per creare i file dell'applicazione React nella cartella build, pronti per essere distribuiti su un server
  • npm test: per eseguire la suite di test usando Jest
  • npm eject: per espellere da create-reazioni-app

L'espulsione è l'atto di decidere che create-reply-app ha fatto abbastanza per te, ma tu vuoi fare più di quello che ti permette.

Dal momento che create-reply-app è un insieme di convenzioni con denominatori comuni e un numero limitato di opzioni, è probabile che ad un certo punto le tue esigenze richiedano qualcosa di unico che supera le capacità di create-reagire-app.

Quando si espelle, si perde la capacità degli aggiornamenti automatici ma si ottiene una maggiore flessibilità nella configurazione di Babel e Webpack.

Quando si espelle l'azione è irreversibile. Otterrai 2 nuove cartelle nella directory dell'applicazione, nella configurazione e negli script. Quelle contengono le configurazioni e ora puoi iniziare a modificarle.

Se hai già installato un'app React usando una versione precedente di React, controlla prima la versione aggiungendo console.log (React.version) nella tua app, quindi puoi eseguire l'aggiornamento eseguendo il thread aggiungi Reave@16.7 e il thread ti chiederà per aggiornare (scegli l'ultima versione disponibile). Ripetere l'operazione per il filato aggiungere reazioni-16@16.7 (cambiare "16.7" con qualunque sia la versione più recente di React al momento)

CodeSandbox

Un modo semplice per avere la struttura di create-reagire-app, senza installarla, è andare su https://codesandbox.io/s e scegliere "Reagisci".

CodeSandbox è un ottimo modo per avviare un progetto React senza doverlo installare localmente.

Codepen

Un'altra grande soluzione è Codepen.

È possibile utilizzare questo progetto di avviamento Codepen che è già preconfigurato con React, con supporto per Hooks: https://codepen.io/flaviocopes/pen/VqeaxB

Le "penne" Codepen sono fantastiche per progetti rapidi con un solo file JavaScript, mentre i "progetti" sono ideali per progetti con più file, come quelli che utilizzeremo di più quando creiamo app React.

Una cosa da notare è che in Codepen, a causa di come funziona internamente, non si utilizza la normale sintassi di importazione dei moduli ES, ma piuttosto per importare ad esempio useState, si usa

const {useState} = Reagisci

e non

importare {useState} da 'reagire'

SEZIONE 1: CONCETTI MODERNI DI JAVASCRIPT CHE DEVI SAPERE PER UTILIZZARE REACT

Scopri se devi imparare qualcosa prima di immergerti nell'apprendimento di React

Se sei disposto a imparare React, devi prima avere alcune cose alla cintura. Ci sono alcune tecnologie preliminari che devi conoscere, in particolare relative ad alcune delle più recenti funzioni JavaScript che utilizzerai più volte in React.

A volte le persone pensano che React fornisca una particolare funzionalità, ma invece è solo una sintassi JavaScript moderna.

Non ha senso essere subito un esperto di questi argomenti, ma più ti immergi in React, più dovrai padroneggiarli.

Citerò un elenco di cose per metterti al passo rapidamente.

variabili

Una variabile è un valore letterale assegnato a un identificatore, quindi è possibile fare riferimento e utilizzarlo in un secondo momento nel programma.

Alle variabili in JavaScript non è associato alcun tipo. Dopo aver assegnato un tipo letterale specifico a una variabile, è possibile in seguito riassegnare la variabile per ospitare qualsiasi altro tipo, senza errori di tipo o problemi.

Questo è il motivo per cui JavaScript viene talvolta definito "non tipizzato".

Una variabile deve essere dichiarata prima di poterla utilizzare. Ci sono 3 modi per farlo, usando var, let o const, e questi 3 modi differiscono nel modo in cui puoi interagire con la variabile in un secondo momento.

Usando var

Fino a ES2015, var era l'unico costrutto disponibile per la definizione delle variabili.

var a = 0

Se si dimentica di aggiungere var, si assegnerà un valore a una variabile non dichiarata e i risultati potrebbero variare.

Negli ambienti moderni, con la modalità rigorosa abilitata, verrà visualizzato un errore. Negli ambienti più vecchi (o con la modalità rigorosa disabilitata) questo semplicemente inizializzerà la variabile e la assegnerà all'oggetto globale.

Se non si inizializza la variabile quando la si dichiara, avrà il valore indefinito fino a quando non si assegna un valore ad essa.

var a // typeof a === 'undefined'

Puoi ripetere la dichiarazione più volte, sovrascrivendola:

var a = 1
var a = 2

Puoi anche dichiarare più variabili contemporaneamente nella stessa istruzione:

var a = 1, b = 2

L'ambito è la porzione di codice in cui la variabile è visibile.

Una variabile inizializzata con var al di fuori di qualsiasi funzione è assegnata all'oggetto globale, ha un ambito globale ed è visibile ovunque. Una variabile inizializzata con var all'interno di una funzione è assegnata a quella funzione, è locale ed è visibile solo al suo interno, proprio come un parametro di funzione.

Qualsiasi variabile definita in una funzione con lo stesso nome di una variabile globale ha la precedenza sulla variabile globale, oscurandola.

È importante capire che un blocco (identificato da una coppia di parentesi graffe) non definisce un nuovo ambito. Un nuovo ambito viene creato solo quando viene creata una funzione, perché var non ha un ambito blocco, ma un ambito funzione.

All'interno di una funzione, qualsiasi variabile definita in essa è visibile in tutto il codice della funzione, anche se la variabile è dichiarata alla fine della funzione, può ancora essere referenziata all'inizio, perché JavaScript prima di eseguire il codice sposta effettivamente tutte le variabili in alto (qualcosa che si chiama sollevamento). Per evitare confusione, dichiarare sempre le variabili all'inizio di una funzione.

Usando let

let è una nuova funzionalità introdotta in ES2015 ed è essenzialmente una versione con var a blocchi di var. Il suo ambito è limitato al blocco, all'istruzione o all'espressione in cui è definito e a tutti i blocchi interni contenuti.

Gli sviluppatori JavaScript moderni potrebbero scegliere di utilizzare solo let e scartare completamente l'uso di var.

Se let sembra un termine oscuro, basta leggere let color = 'red' in quanto lascia che il colore sia rosso e tutto ha molto più senso

Definire let al di fuori di qualsiasi funzione - contrariamente a var - non crea una variabile globale.

Utilizzando const

Le variabili dichiarate con var o let possono essere modificate in seguito nel programma e riassegnate. Una volta inizializzato un const, il suo valore non può più essere modificato e non può essere riassegnato a un valore diverso.

const a = 'test'

Non possiamo assegnare un valore letterale diverso alla const. Possiamo tuttavia mutare a se è un oggetto che fornisce metodi che mutano il suo contenuto.

const non fornisce immutabilità, si assicura solo che il riferimento non possa essere modificato.

const ha scope block, uguale a let.

Gli sviluppatori JavaScript moderni potrebbero scegliere di utilizzare sempre const per le variabili che non devono essere riassegnate più avanti nel programma.

Perché? Perché dovremmo sempre usare il costrutto più semplice disponibile per evitare errori lungo la strada.

Funzioni freccia

Le funzioni Arrow sono state introdotte in ES6 / ECMAScript 2015 e dalla loro introduzione hanno cambiato per sempre l'aspetto (e il funzionamento) del codice JavaScript.

A mio avviso, questo cambiamento è stato così accogliente che ora raramente vedi l'uso della parola chiave funzione nelle basi di codice moderne.

Visivamente, è una modifica semplice e gradita, che consente di scrivere funzioni con una sintassi più breve, da:

const myFunction = function () {
  // ...
}

per

const myFunction = () => {
  // ...
}

Se il corpo della funzione contiene solo una singola istruzione, è possibile omettere le parentesi e scrivere tutto su una sola riga:

const myFunction = () => doSomething ()

I parametri sono passati tra parentesi:

const myFunction = (param1, param2) => doSomething (param1, param2)

Se hai un (e solo un) parametro, potresti omettere completamente le parentesi:

const myFunction = param => doSomething (param)

Grazie a questa breve sintassi, le funzioni freccia incoraggiano l'uso di piccole funzioni.

Ritorno implicito

Le funzioni freccia consentono di ottenere un ritorno implicito: i valori vengono restituiti senza dover utilizzare la parola chiave return.

Funziona quando è presente un'istruzione di una riga nel corpo della funzione:

const myFunction = () => 'test'
myFunction () // 'test'

Un altro esempio, quando si restituisce un oggetto, ricordarsi di avvolgere le parentesi graffe tra parentesi per evitare che vengano considerate parentesi del corpo della funzione di avvolgimento:

const myFunction = () => ({value: 'test'})
myFunction () // {value: 'test'}

Come funziona nelle funzioni freccia

questo è un concetto che può essere complicato da comprendere, poiché varia molto a seconda del contesto e varia anche a seconda della modalità JavaScript (modalità rigorosa o meno).

È importante chiarire questo concetto perché le funzioni freccia si comportano in modo molto diverso rispetto alle funzioni normali.

Quando definito come metodo di un oggetto, in una normale funzione si riferisce all'oggetto, quindi puoi fare:

const car = {
  modello: 'Fiesta',
  produttore: "Ford",
  fullName: function () {
    restituisce `$ {this.manufacturer} $ {this.model}`
  }
}

chiamando car.fullName () restituirà "Ford Fiesta".

Questo ambito con funzioni freccia viene ereditato dal contesto di esecuzione. Una funzione freccia non lo associa affatto, quindi il suo valore verrà cercato nello stack di chiamate, quindi in questo codice car.fullName () non funzionerà e restituirà la stringa "undefined undefined":

const car = {
  modello: 'Fiesta',
  produttore: "Ford",
  fullName: () => {
    restituisce `$ {this.manufacturer} $ {this.model}`
  }
}

Per questo motivo, le funzioni freccia non sono adatte come metodi oggetto.

Neanche le funzioni freccia possono essere usate come costruttori, quando un'istanza di un oggetto genererà un TypeError.

È qui che dovrebbero essere usate le funzioni regolari, quando non è necessario il contesto dinamico.

Questo è anche un problema durante la gestione degli eventi. I listener di eventi DOM impostano questo come elemento di destinazione e se si fa affidamento su questo in un gestore di eventi, è necessaria una funzione regolare:

const link = document.querySelector ('# link')
link.addEventListener ('click', () => {
  // questa === finestra
})
const link = document.querySelector ('# link')
link.addEventListener ('click', function () {
  // questo === link
})

Riposa e diffondi

Puoi espandere un array, un oggetto o una stringa usando l'operatore spread ....

Cominciamo con un esempio di array. Dato

const a = [1, 2, 3]

puoi creare un nuovo array usando

const b = [... a, 4, 5, 6]

Puoi anche creare una copia di un array usando

const c = [... a]

Questo funziona anche per gli oggetti. Clona un oggetto con:

const newObj = {... oldObj}

Usando le stringhe, l'operatore spread crea un array con ogni carattere nella stringa:

const hey = 'hey'
const arrayized = [... hey] // ['h', 'e', ​​'y']

Questo operatore ha alcune applicazioni piuttosto utili. Il più importante è la capacità di utilizzare un array come argomento di funzione in un modo molto semplice:

const f = (foo, bar) => {}
const a = [1, 2]
fa)

(in passato potresti farlo usando f.apply (null, a) ma non è così bello e leggibile)

L'elemento rest è utile quando si lavora con la distruzione dell'array:

numeri const = = 1, 2, 3, 4, 5]
[primo, secondo, ... altro] = numeri

e diffondere elementi:

numeri const = = 1, 2, 3, 4, 5]
const const = (a, b, c, d, e) => a + b + c + d + e
const sumOfNumbers = sum (... numeri)

ES2018 introduce le proprietà di riposo, che sono le stesse ma per gli oggetti.

Proprietà di riposo:

const {primo, secondo, ... altri} = {
  primo: 1,
  secondo: 2,
  terzo: 3,
  quarto: 4,
  quinto: 5
}
prima // 1
secondo // 2
altri // {terzo: 3, quarto: 4, quinto: 5}

Le proprietà di diffusione consentono di creare un nuovo oggetto combinando le proprietà dell'oggetto passato dopo l'operatore di diffusione:

const items = {primo, secondo, ... altro}
articoli // {primo: 1, secondo: 2, terzo: 3, quarto: 4, quinto: 5}

Distruzione di oggetti e array

Dato un oggetto, usando la sintassi destrutturante è possibile estrarre solo alcuni valori e inserirli in variabili denominate:

const person = {
  firstName: 'Tom',
  lastName: 'Cruise',
  attore: vero,
  età: 54 // inventato
}
const {nome: età, nome} = persona // nome: Tom, età: 54 anni

nome ed età contengono i valori desiderati.

La sintassi funziona anche su array:

const a = [1, 2, 3, 4, 5]
const [primo, secondo] = a

Questa affermazione crea 3 nuove variabili ottenendo gli elementi con indice 0, 1, 4 dall'array a:

const [primo, secondo,, quinto] = a

Letterali modello

I modelli letterali sono una nuova funzionalità ES2015 / ES6 che consente di lavorare con le stringhe in modo nuovo rispetto a ES5 e versioni precedenti.

La sintassi a prima vista è molto semplice, basta usare i backtick invece delle virgolette singole o doppie:

const a_string = `qualcosa`

Sono unici perché forniscono molte funzionalità che le stringhe normali costruite con virgolette non, in particolare:

  • offrono una grande sintassi per definire stringhe multilinea
  • forniscono un modo semplice per interpolare variabili ed espressioni nelle stringhe
  • ti consentono di creare DSL con tag template (DSL significa linguaggio specifico del dominio, ed è ad esempio usato in React by Styled Components, per definire CSS per un componente)

Analizziamo ciascuno di questi in dettaglio.

Stringhe multilinea

Pre-ES6, per creare una stringa che si estende su due righe, è stato necessario utilizzare il carattere \ alla fine di una riga:

const string =
  'prima parte \
seconda parte'

Ciò consente di creare una stringa su 2 righe, ma viene renderizzato su una sola riga:

prima parte seconda parte

Per eseguire il rendering della stringa anche su più righe, è necessario aggiungere esplicitamente \ n alla fine di ogni riga, in questo modo:

const string =
  'prima riga \ n \
seconda linea'

o

const string = 'prima riga \ n' + 'seconda riga'

I letterali template rendono le stringhe multilinea molto più semplici.

Una volta che un modello letterale viene aperto con il backtick, premi semplicemente Invio per creare una nuova riga, senza caratteri speciali, e viene reso così com'è:

const string = `Hey
Questo
corda
è fantastico! `

Tieni presente che lo spazio è significativo, quindi:

const string = `Primo
                secondo`

creerà una stringa come questa:

Primo
                Secondo

un modo semplice per risolvere questo problema è avere una prima riga vuota e aggiungere il metodo trim () subito dopo il backtick di chiusura, che eliminerà qualsiasi spazio prima del primo carattere:

const string = `
Primo
Second`.trim ()

interpolazione

I letterali template forniscono un modo semplice per interpolare variabili ed espressioni in stringhe.

Puoi farlo usando la sintassi $ {...}:

const myVariable = 'test'
const string = `something $ {myVariable}` // qualcosa di test

all'interno di $ {} puoi aggiungere qualsiasi cosa, anche espressioni:

const string = `qualcosa $ {1 + 2 + 3}`
const string2 = `qualcosa $ {foo ()? 'x': 'y'} `

Classi

Nel 2015 lo standard ECMAScript 6 (ES6) ha introdotto le classi.

JavaScript ha un modo abbastanza raro di implementare l'ereditarietà: l'ereditarietà prototipica. L'ereditarietà prototipale, sebbene a mio avviso eccezionale, è diversa dall'implementazione dell'ereditarietà della maggior parte degli altri linguaggi di programmazione popolari, che è basata sulla classe.

Le persone provenienti da Java, Python o altre lingue hanno avuto difficoltà a comprendere le complessità dell'eredità prototipale, quindi il comitato ECMAScript ha deciso di cospargere lo zucchero sintattico sull'eredità prototipica in modo che assomigli a come l'eredità basata sulla classe funziona in altre implementazioni popolari.

Questo è importante: JavaScript sotto il cofano è sempre lo stesso e puoi accedere a un prototipo di oggetto nel solito modo.

Una definizione di classe

Ecco come appare una classe.

class Person {
  costruttore (nome) {
    this.name = name
  }
  Ciao() {
    return "Ciao, sono" + this.name + "."
  }
}

Una classe ha un identificatore, che possiamo usare per creare nuovi oggetti usando new ClassIdentifier ().

Quando l'oggetto viene inizializzato, viene chiamato il metodo di costruzione, con qualsiasi parametro passato.

Una classe ha anche tutti i metodi di cui ha bisogno. In questo caso, ciao è un metodo e può essere chiamato su tutti gli oggetti derivati ​​da questa classe:

const flavio = new Person ('Flavio')
flavio.hello ()

Eredità di classe

Una classe può estendere un'altra classe e gli oggetti inizializzati usando quella classe ereditano tutti i metodi di entrambe le classi.

Se la classe ereditata ha un metodo con lo stesso nome di una delle classi più alte nella gerarchia, il metodo più vicino ha la precedenza:

Il programmatore di classe estende la persona {
  Ciao() {
    return super.hello () + 'Sono un programmatore.'
  }
}
const flavio = new Programmer ('Flavio')
flavio.hello ()

(il programma sopra stampa "Ciao, sono Flavio. Sono un programmatore.")

Le classi non hanno dichiarazioni di variabili di classe esplicite, ma è necessario inizializzare qualsiasi variabile nel costruttore.

All'interno di una classe, puoi fare riferimento alla classe genitore che chiama super ().

Metodi statici

Normalmente i metodi sono definiti sull'istanza, non sulla classe.

I metodi statici vengono invece eseguiti sulla classe:

class Person {
  static genericHello () {
    ritorna "Ciao"
  }
}
Person.genericHello () // Ciao

Metodi privati

JavaScript non ha un modo integrato per definire metodi privati ​​o protetti.

Ci sono soluzioni alternative, ma non le descriverò qui.

Getter e setter

È possibile aggiungere metodi con prefisso get o set per creare un getter e un setter, che sono due diversi pezzi di codice che vengono eseguiti in base a ciò che si sta facendo: accedere alla variabile o modificarne il valore.

class Person {
  costruttore (nome) {
    this.name = name
  }
  imposta nome (valore) {
    this.name = value
  }
  get name () {
    restituire this.name
  }
}

Se hai solo un getter, la proprietà non può essere impostata e qualsiasi tentativo di farlo verrà ignorato:

class Person {
  costruttore (nome) {
    this.name = name
  }
  get name () {
    restituire this.name
  }
}

Se hai solo un setter, puoi modificare il valore ma non accedervi dall'esterno:

class Person {
  costruttore (nome) {
    this.name = name
  }
  imposta nome (valore) {
    this.name = value
  }
}

callback

I computer sono asincroni in base alla progettazione.

Asincrono significa che le cose possono accadere indipendentemente dal flusso del programma principale.

Negli attuali computer consumer, ogni programma viene eseguito per un determinato intervallo di tempo, quindi interrompe l'esecuzione per consentire a un altro programma di continuare l'esecuzione. Questa cosa si svolge in un ciclo così veloce che è impossibile notare, e pensiamo che i nostri computer eseguano molti programmi contemporaneamente, ma questa è un'illusione (tranne che sui computer multiprocessore).

I programmi utilizzano internamente gli interrupt, un segnale che viene emesso al processore per attirare l'attenzione del sistema.

Non entrerò negli interni di questo, ma tieni presente che è normale che i programmi siano asincroni e ne interrompano l'esecuzione fino a quando non hanno bisogno di attenzione, e il computer può eseguire altre cose nel frattempo. Quando un programma è in attesa di una risposta dalla rete, non può arrestare il processore fino al termine della richiesta.

Normalmente, i linguaggi di programmazione sono sincroni e alcuni forniscono un modo per gestire l'asincronicità, nella lingua o attraverso le librerie. C, Java, C #, PHP, Go, Ruby, Swift, Python, sono tutti sincroni per impostazione predefinita. Alcuni gestiscono l'asincronizzazione usando i thread, generando un nuovo processo.

JavaScript è sincrono per impostazione predefinita ed è a thread singolo. Ciò significa che il codice non può creare nuovi thread ed essere eseguito in parallelo.

Le righe di codice vengono eseguite in serie, una dopo l'altra, ad esempio:

const a = 1
const b = 2
const c = a * b
console.log (c)
fare qualcosa()

Ma JavaScript è nato all'interno del browser, il suo compito principale, all'inizio, era rispondere alle azioni dell'utente, come onClick, onMouseOver, onChange, onSubmit e così via. Come potrebbe farlo con un modello di programmazione sincrona?

La risposta era nel suo ambiente. Il browser fornisce un modo per farlo fornendo un set di API in grado di gestire questo tipo di funzionalità.

Più recentemente, Node.js ha introdotto un ambiente I / O non bloccante per estendere questo concetto all'accesso ai file, alle chiamate di rete e così via.

Non puoi sapere quando un utente farà clic su un pulsante, quindi quello che fai è definire un gestore eventi per l'evento click. Questo gestore eventi accetta una funzione, che verrà chiamata quando viene attivato l'evento:

document.getElementById ('button'). addEventListener ('click', () => {
  // oggetto selezionato
})

Questo è il cosiddetto callback.

Un callback è una semplice funzione che viene passata come valore a un'altra funzione e verrà eseguita solo quando si verifica l'evento. Possiamo farlo perché JavaScript ha funzioni di prima classe, che possono essere assegnate a variabili e trasferite ad altre funzioni (chiamate funzioni di ordine superiore)

È comune racchiudere tutto il codice client in un listener di eventi load sull'oggetto window, che esegue la funzione di callback solo quando la pagina è pronta:

window.addEventListener ('load', () => {
  // finestra caricata
  //Fai quello che vuoi
})

I callback vengono utilizzati ovunque, non solo negli eventi DOM.

Un esempio comune è l'utilizzo dei timer:

setTimeout (() => {
  // viene eseguito dopo 2 secondi
}, 2000)

Le richieste XHR accettano anche un callback, in questo esempio assegnando una funzione a una proprietà che verrà chiamata quando si verifica un evento particolare (in questo caso, lo stato della richiesta cambia):

const xhr = new XMLHttpRequest ()
xhr.onreadystatechange = () => {
  if (xhr.readyState === 4) {
    xhr.status === 200? console.log (xhr.responseText): console.error ('errore')
  }
}
xhr.open ('GET', 'https://yoursite.com')
xhr.send ()

Gestione degli errori nei callback

Come gestite gli errori con i callback? Una strategia molto comune è quella di utilizzare ciò che Node.js ha adottato: il primo parametro in qualsiasi funzione di callback è l'oggetto error: callbacks first-error

Se non si verificano errori, l'oggetto è null. Se si verifica un errore, contiene una descrizione dell'errore e altre informazioni.

fs.readFile ('/ file.json', (err, data) => {
  if (err! == null) {
    // gestisce l'errore
    console.log (err)
    ritorno
  }
  // nessun errore, dati di processo
  console.log (dati)
})

Il problema con i callback

I callback sono ottimi per casi semplici!

Tuttavia, ogni callback aggiunge un livello di annidamento e quando si hanno molti callback, il codice inizia a essere complicato molto rapidamente:

window.addEventListener ('load', () => {
  document.getElementById ('button'). addEventListener ('click', () => {
    setTimeout (() => {
      items.forEach (item => {
        // il tuo codice qui
      })
    }, 2000)
  })
})

Questo è solo un semplice codice a 4 livelli, ma ho visto molti più livelli di annidamento e non è divertente.

Come lo risolviamo?

ALTERNATIVE ALLE CHIAMATE

A partire da ES6, JavaScript ha introdotto diverse funzionalità che ci aiutano con il codice asincrono che non prevede l'uso di callback:

  • Promesse (ES6)
  • Async / Await (ES8)

promesse

Le promesse sono un modo per gestire il codice asincrono, senza scrivere troppi callback nel codice.

Sebbene esistano da anni, sono stati standardizzati e introdotti in ES2015 e ora sono stati sostituiti in ES2017 da funzioni asincrone.

Le funzioni asincrone utilizzano l'API delle promesse come blocco predefinito, quindi la loro comprensione è fondamentale anche se nel codice più recente probabilmente utilizzerai le funzioni asincrone anziché le promesse.

Come funzionano le promesse, in breve

Una volta che una promessa è stata chiamata, inizierà in stato di attesa. Ciò significa che la funzione chiamante continua l'esecuzione, mentre attende che la promessa esegua la propria elaborazione e dia un feedback alla funzione chiamante.

A questo punto, la funzione chiamante attende che restituisca la promessa in uno stato risolto o in uno stato rifiutato, ma come sai JavaScript è asincrono, quindi la funzione continua la sua esecuzione mentre la promessa funziona.

Quale API JS usa le promesse?

Oltre al proprio codice e al codice della libreria, le promesse vengono utilizzate dalle API Web moderne standard come Fetch o Service Workers.

È improbabile che nella moderna JavaScript ti ritrovi a non usare le promesse, quindi iniziamo a immergerci direttamente in esse.

Creare una promessa

L'API Promise espone un costruttore Promise, che si inizializza usando new Promise ():

let done = true
const isItDoneYet = new Promise ((risoluzione, rifiuto) => {
  if (done) {
    const workDone = 'Ecco la cosa che ho costruito'
    risolvere (workDone)
  } altro {
    const why = 'Ancora lavorando su qualcos'altro'
    respingere (perché)
  }
})

Come puoi vedere, la promessa controlla la costante globale fatta e, se è vero, restituiamo una promessa risolta, altrimenti una promessa rifiutata.

Usando risoluzione e rifiuto possiamo comunicare un valore, nel caso precedente restituiamo solo una stringa, ma potrebbe anche essere un oggetto.

Consumare una promessa

Nell'ultima sezione, abbiamo presentato come viene creata una promessa.

Ora vediamo come la promessa può essere consumata o utilizzata.

const isItDoneYet = new Promise ()
// ...
const checkIfItsDone = () => {
  isItDoneYet
    .then (ok => {
      console.log (ok)
    })
    .catch (err => {
      Console.Error (err)
    })
}

L'esecuzione di checkIfItsDone () eseguirà la promessa isItDoneYet () e attenderà che si risolva, utilizzando quindi il callback, e se si verifica un errore, lo gestirà nel callback catch.

Concatenare promesse

Una promessa può essere restituita a un'altra promessa, creando una catena di promesse.

Un ottimo esempio di concatenamento delle promesse è dato dall'API Fetch, un livello in cima all'API XMLHttpRequest, che possiamo usare per ottenere una risorsa e mettere in coda una catena di promesse da eseguire quando la risorsa viene recuperata.

L'API Fetch è un meccanismo basato sulla promessa e la chiamata a fetch () equivale a definire la nostra promessa usando new Promise ().

Esempio:

const status = response => {
  if (response.status> = 200 && response.status <300) {
    return Promise.resolve (risposta)
  }
  return Promise.reject (nuovo errore (response.statusText))
}
const json = response => response.json ()
fetch ( '/ todos.json')
  .poi (stato)
  .poi (JSON)
  .then (data => {
    console.log ('Richiesta riuscita con risposta JSON', dati)
  })
  .catch (errore => {
    console.log ("Richiesta non riuscita", errore)
  })

In questo esempio, chiamiamo fetch () per ottenere un elenco di elementi TODO dal file todos.json trovato nella radice del dominio e creiamo una catena di promesse.

L'esecuzione di fetch () restituisce una risposta, che ha molte proprietà e all'interno di quelle a cui facciamo riferimento:

  • status, un valore numerico che rappresenta il codice di stato HTTP
  • statusText, un messaggio di stato, che è OK se la richiesta ha avuto esito positivo

response ha anche un metodo json (), che restituisce una promessa che si risolverà con il contenuto del corpo elaborato e trasformato in JSON.

Quindi, date queste premesse, ecco cosa succede: la prima promessa nella catena è una funzione che abbiamo definito, chiamato status (), che controlla lo stato della risposta e se non è una risposta positiva (tra 200 e 299), rifiuta il promettere.

Questa operazione farà sì che la catena di promesse salti tutte le promesse concatenate elencate e salterà direttamente all'istruzione catch () in basso, registrando il testo Richiesta non riuscita insieme al messaggio di errore.

Se invece riesce, chiama la funzione json () che abbiamo definito. Poiché la promessa precedente, quando ha avuto successo, ha restituito l'oggetto response, lo otteniamo come input per la seconda promessa.

In questo caso, restituiamo i dati elaborati da JSON, quindi la terza promessa riceve direttamente JSON:

.then ((data) => {
  console.log ('Richiesta riuscita con risposta JSON', dati)
})

e lo accediamo semplicemente alla console.

Gestione degli errori

Nell'esempio sopra, nella sezione precedente, abbiamo avuto un problema che è stato aggiunto alla catena di promesse.

Quando qualcosa nella catena di promesse fallisce e genera un errore o rifiuta la promessa, il controllo passa all'istruzione catch () più vicina lungo la catena.

nuova promessa ((risoluzione, rifiuto) => {
  genera nuovo errore ("Errore")
}). catch (err => {
  Console.Error (err)
})
// o
nuova promessa ((risoluzione, rifiuto) => {
  rifiutare ( 'Errore')
}). catch (err => {
  Console.Error (err)
})

Errori a cascata

Se all'interno del catch () si genera un errore, è possibile aggiungere un secondo catch () per gestirlo, e così via.

nuova promessa ((risoluzione, rifiuto) => {
  genera nuovo errore ("Errore")
})
  .catch (err => {
    genera nuovo errore ("Errore")
  })
  .catch (err => {
    Console.Error (err)
  })

Orchestrare le promesse con Promise.all ()

Se devi sincronizzare promesse diverse, Promise.all () ti aiuta a definire un elenco di promesse ed eseguire qualcosa quando sono tutte risolte.

Esempio:

const f1 = fetch ('/ something.json')
const f2 = fetch ('/ something2.json')
Promise.all ([f1, f2])
  .then (res => {
    console.log ('Array of results', res)
  })
  .catch (err => {
    Console.Error (err)
  })

La sintassi di assegnazione destrutturante ES2015 consente anche di fare

Promise.all ([f1, f2]). Then (([res1, res2]) => {
  console.log ('Risultati', res1, res2)
})

Ovviamente non sei limitato all'utilizzo del recupero, qualsiasi promessa è buona.

Orchestrare le promesse con Promise.race ()

Promise.race () viene eseguito non appena una delle promesse passate si risolve e esegue il callback allegato solo una volta con il risultato della prima promessa risolto.

Esempio:

const promiseOne = new Promise ((risoluzione, rifiuto) => {
  setTimeout (risoluzione, 500, 'uno')
})
const promiseTwo = new Promise ((risoluzione, rifiuto) => {
  setTimeout (risoluzione, 100, 'due')
})
Promise.race ([promiseOne, promiseTwo]). Then (risultato => {
  console.log (risultato) // 'two'
})

Async / Await

JavaScript si è evoluto in pochissimo tempo dai callback alle promesse (ES2015) e dal momento che JavaScript asincrono ES2017 è ancora più semplice con la sintassi async / waitit.

Le funzioni asincrone sono una combinazione di promesse e generatori e, in sostanza, sono un'astrazione di livello superiore rispetto alle promesse. Consentitemi di ripetere: async / waitit si basa sulle promesse.

Perché sono stati introdotti async / waitit?

Riducono il piatto attorno alle promesse e la limitazione "non spezzare la catena" delle promesse concatenate.

Quando le promesse furono introdotte in ES2015, avevano lo scopo di risolvere un problema con il codice asincrono, e lo fecero, ma nel corso dei 2 anni che separarono ES2015 ed ES2017, era chiaro che le promesse non potevano essere la soluzione finale.

Furono introdotte promesse per risolvere il famoso problema dell'inferno del callback, ma introdussero la complessità da soli e la complessità della sintassi.

Erano buoni primitivi attorno ai quali una migliore sintassi poteva essere esposta agli sviluppatori, quindi quando era il momento giusto ottenevamo funzioni asincrone.

Fanno sembrare il codice sincrono, ma è asincrono e non bloccante dietro le quinte.

Come funziona

Una funzione asincrona restituisce una promessa, come in questo esempio:

const doSomethingAsync = () => {
  ritorna nuova promessa (risoluzione => {
    setTimeout (() => resolve ('Ho fatto qualcosa'), 3000)
  })
}

Quando si desidera chiamare questa funzione, attendere anticipatamente e il codice chiamante si interromperà fino a quando la promessa non viene risolta o rifiutata. Un avvertimento: la funzione client deve essere definita asincrona. Ecco un esempio:

const doSomething = async () => {
  console.log (await doSomethingAsync ())
}

Un rapido esempio

Questo è un semplice esempio di asincrono / wait usato per eseguire una funzione in modo asincrono:

const doSomethingAsync = () => {
  ritorna nuova promessa (risoluzione => {
    setTimeout (() => resolve ('Ho fatto qualcosa'), 3000)
  })
}
const doSomething = async () => {
  console.log (await doSomethingAsync ())
}
console.log ( 'Prima')
fare qualcosa()
console.log ( 'After')

Il codice precedente stampa quanto segue sulla console del browser:

Prima
Dopo
Ho fatto qualcosa // dopo 3 secondi

Prometti tutte le cose

Preparare la parola chiave asincrona a qualsiasi funzione significa che la funzione restituirà una promessa.

Anche se non lo fa esplicitamente, internamente gli farà tornare una promessa.

Ecco perché questo codice è valido:

const aFunction = async () => {
  restituisce 'test'
}
aFunction (). then (alert) // Questo avviserà 'test'

ed è lo stesso di:

const aFunction = async () => {
  return Promise.resolve ('test')
}
aFunction (). then (alert) // Questo avviserà 'test'

Il codice è molto più semplice da leggere

Come puoi vedere nell'esempio sopra, il nostro codice sembra molto semplice. Confrontalo con il codice usando semplici promesse, con funzioni di concatenamento e callback.

E questo è un esempio molto semplice, i maggiori vantaggi sorgeranno quando il codice è molto più complesso.

Ad esempio, ecco come ottenere una risorsa JSON e analizzarla utilizzando le promesse:

const getFirstUserData = () => {
  return fetch ('/ users.json') // ottiene l'elenco utenti
    .then (response => response.json ()) // analizza JSON
    .then (users => users [0]) // seleziona il primo utente
    .then (user => fetch (`/ users / $ {user.name}`)) // recupera i dati dell'utente
    .then (userResponse => userResponse.json ()) // analizzare JSON
}
getFirstUserData ()

Ed ecco le stesse funzionalità fornite usando await / async:

const getFirstUserData = async () => {
  const response = await fetch ('/ users.json') // ottiene l'elenco utenti
  const users = await response.json () // parse JSON
  const user = users [0] // seleziona il primo utente
  const userResponse = await fetch (`/ users / $ {user.name}`) // recupera i dati dell'utente
  const userData = await userResponse.json () // parse JSON
  restituire userData
}
getFirstUserData ()

Molteplici funzioni asincrone in serie

Le funzioni asincrone possono essere concatenate molto facilmente e la sintassi è molto più leggibile rispetto alle semplici promesse:

const promiseToDoSomething = () => {
  ritorna nuova promessa (risoluzione => {
    setTimeout (() => resolve ('Ho fatto qualcosa'), 10000)
  })
}
const watchOverSomeoneDoingSomething = async () => {
  const qualcosa = attende promessaToDoSomething ()
  restituire qualcosa + "e ho visto"
}
const watchOverSomeoneWatchingSomeoneDoingSomething = async () => {
  const qualcosa = await watchOverSomeoneDoingSomething ()
  restituire qualcosa + 'e ho visto anche'
}
watchOverSomeoneWatchingSomeoneDoingSomething (). then (res => {
  console.log (res)
})

Stampa:

Ho fatto qualcosa e ho guardato e ho anche guardato

Debug più semplice

Le promesse di debug sono difficili perché il debugger non passerà oltre il codice asincrono.

Async / await rende tutto molto semplice perché per il compilatore è proprio come un codice sincrono.

Moduli ES

Moduli ES è lo standard ECMAScript per lavorare con i moduli.

Mentre Node.js utilizza lo standard CommonJS da anni, il browser non ha mai avuto un sistema di moduli, poiché ogni decisione importante come un sistema di moduli deve essere prima standardizzata da ECMAScript e quindi implementata dal browser.

Questo processo di standardizzazione completato con ES6 e i browser ha iniziato a implementare questo standard cercando di mantenere tutto ben allineato, lavorando allo stesso modo, e ora i moduli ES sono supportati in Chrome, Safari, Edge e Firefox (dalla versione 60).

I moduli sono molto interessanti, perché ti consentono di incapsulare tutti i tipi di funzionalità ed esporre questa funzionalità ad altri file JavaScript, come librerie.

La sintassi dei moduli ES

La sintassi per importare un modulo è:

pacchetto di importazione da 'nome-modulo'

mentre CommonJS usa

const package = request ('nome-modulo')

Un modulo è un file JavaScript che esporta uno o più valori (oggetti, funzioni o variabili), usando la parola chiave export. Ad esempio, questo modulo esporta una funzione che restituisce una stringa maiuscola:

uppercase.js
export default str => str.toUpperCase ()

In questo esempio, il modulo definisce una singola esportazione predefinita, quindi può essere una funzione anonima. Altrimenti avrebbe bisogno di un nome per distinguerlo dalle altre esportazioni.

Ora, qualsiasi altro modulo JavaScript può importare la funzionalità offerta da uppercase.js importandolo.

Una pagina HTML può aggiungere un modulo utilizzando un tag

Nota: l'importazione di questo modulo si comporta come un carico di script differito. Guarda caricare in modo efficiente JavaScript con differimento e asincrono

È importante notare che qualsiasi script caricato con type = "module" viene caricato in modalità rigorosa.

In questo esempio, il modulo uppercase.js definisce un'esportazione predefinita, quindi quando lo importiamo, possiamo assegnargli un nome che preferiamo:

importa inUpperCase da './uppercase.js'

e possiamo usarlo:

toUpperCase ('test') // 'TEST'

È inoltre possibile utilizzare un percorso assoluto per l'importazione del modulo, per fare riferimento ai moduli definiti su un altro dominio:

import toUpperCase da 'https://flavio-es-modules-example.glitch.me/uppercase.js'

Questa è anche una sintassi di importazione valida:

import {foo} da '/uppercase.js'
import {foo} da '../uppercase.js'

Questo non è:

import {foo} da 'uppercase.js'
import {foo} da 'utils / uppercase.js'

È assoluto o ha un ./ o / prima del nome.

Altre opzioni di importazione / esportazione

Abbiamo visto questo esempio sopra:

export default str => str.toUpperCase ()

Questo crea un'esportazione predefinita. In un file, tuttavia, puoi esportare più di una cosa, usando questa sintassi:

const a = 1
const b = 2
const c = 3
export {a, b, c}

Un altro modulo può importare tutte quelle esportazioni usando

import * da 'module'

Puoi importare solo alcune di queste esportazioni, usando il compito di destrutturazione:

importare {a} da 'module'
importare {a, b} da 'module'

Puoi rinominare qualsiasi importazione, per comodità, usando come:

importare {a, b come due} da 'module'

Puoi importare l'esportazione predefinita e qualsiasi esportazione non predefinita per nome, come in questa comune importazione React:

import React, {Component} da 'reagire'

Puoi vedere un esempio di moduli ES qui: https://glitch.com/edit/#!/flavio-es-modules-example?path=index.html

CORS

I moduli vengono recuperati utilizzando CORS. Ciò significa che se fai riferimento a script di altri domini, devono avere un'intestazione CORS valida che consenta il caricamento su più siti (come Access-Control-Allow-Origin: *)

Che dire dei browser che non supportano i moduli?

Utilizzare una combinazione di type = "module" e nomodule:


I moduli ES sono una delle più grandi funzionalità introdotte nei browser moderni. Fanno parte di ES6 ma la strada per implementarli è stata lunga.

Ora possiamo usarli! Ma dobbiamo anche ricordare che avere più di un paio di moduli avrà un impatto sulle prestazioni sulle nostre pagine, poiché è un ulteriore passo che il browser deve eseguire in fase di esecuzione.

Probabilmente Webpack continuerà a essere un grande giocatore anche se i moduli ES arrivano nel browser, ma avere una tale funzione direttamente costruita nella lingua è enorme per l'unificazione di come funzionano i moduli sul lato client e su Node.js.

SEZIONE 2: CONCETTI DI REACT

Applicazioni a pagina singola

Le applicazioni React sono anche chiamate applicazioni a pagina singola. Cosa significa questo?

In passato, quando i browser erano molto meno capaci di oggi e le prestazioni di JavaScript erano scarse, ogni pagina proveniva da un server. Ogni volta che hai fatto clic su qualcosa, è stata effettuata una nuova richiesta al server e il browser ha successivamente caricato la nuova pagina.

Solo i prodotti molto innovativi hanno funzionato diversamente e hanno sperimentato nuovi approcci.

Oggi, resa popolare dai moderni framework JavaScript front-end come React, un'app è solitamente costruita come un'applicazione a pagina singola: carichi il codice dell'applicazione (HTML, CSS, JavaScript) una volta sola e quando interagisci con l'applicazione, ciò che accade generalmente è che JavaScript intercetta gli eventi del browser e invece di effettuare una nuova richiesta al server che quindi restituisce un nuovo documento, il client richiede del JSON o esegue un'azione sul server ma la pagina visualizzata dall'utente non viene mai cancellata completamente e si comporta di più come un'applicazione desktop.

Le applicazioni a pagina singola sono integrate in JavaScript (o almeno compilate in JavaScript) e funzionano nel browser.

La tecnologia è sempre la stessa, ma la filosofia e alcuni componenti chiave di come funziona l'applicazione sono diversi.

Esempi di applicazioni a pagina singola

Alcuni esempi notevoli:

  • Gmail
  • Google Maps
  • Facebook
  • cinguettio
  • Google Drive

Pro e contro delle SPA

Una SPA si sente molto più veloce per l'utente, perché invece di attendere che avvenga la comunicazione client-server e attendere che il browser ritorni nuovamente la pagina, ora puoi avere un feedback immediato. Questa è la responsabilità del produttore dell'applicazione, ma puoi avere transizioni e spinner e qualsiasi tipo di miglioramento della UX che è sicuramente migliore del flusso di lavoro tradizionale.

Oltre a rendere l'esperienza più veloce per l'utente, il server consumerà meno risorse poiché puoi concentrarti sulla fornitura di un'API efficiente anziché sulla creazione di layout lato server.

Questo lo rende ideale se si sviluppa anche un'app mobile sull'API, poiché è possibile riutilizzare completamente il codice lato server esistente.

Le applicazioni a pagina singola sono facili da trasformare in Progressive Web App, che a loro volta ti consentono di fornire la memorizzazione nella cache locale e di supportare esperienze offline per i tuoi servizi (o semplicemente un messaggio di errore migliore se gli utenti devono essere online).

Le SPA sono utilizzate al meglio quando non è necessario SEO (ottimizzazione per i motori di ricerca). Ad esempio per le app che funzionano dietro un accesso.

I motori di ricerca, migliorando ogni giorno, hanno ancora problemi a indicizzare i siti creati con un approccio SPA piuttosto che le tradizionali pagine rese dal server. Questo è il caso dei blog. Se hai intenzione di fare affidamento sui motori di ricerca, non preoccuparti nemmeno di creare un'applicazione a pagina singola senza avere anche un server reso parte.

Quando si codifica una SPA, si scriverà molto JavaScript. Poiché l'app può essere di lunga durata, dovrai prestare molta più attenzione alle possibili perdite di memoria - se in passato la tua pagina aveva una durata che veniva contata in pochi minuti, ora una SPA potrebbe rimanere aperta per ore in un tempo e se c'è qualche problema di memoria che aumenterà molto di più l'utilizzo della memoria del browser e causerà un'esperienza spiacevolmente lenta se non te ne occupi.

Le SPA sono fantastiche quando si lavora in gruppo. Gli sviluppatori di back-end possono semplicemente concentrarsi sull'API e gli sviluppatori di front-end possono concentrarsi sulla creazione della migliore esperienza utente, facendo uso dell'API integrata nel back-end.

Come contro, le app a pagina singola si basano fortemente su JavaScript. Ciò potrebbe rendere l'utilizzo di un'applicazione in esecuzione su dispositivi a bassa potenza una scarsa esperienza in termini di velocità. Inoltre, alcuni dei tuoi visitatori potrebbero avere JavaScript disabilitato e devi anche considerare l'accessibilità per tutto ciò che costruisci.

Sostituire la navigazione

Poiché ti sbarazzi della navigazione predefinita del browser, gli URL devono essere gestiti manualmente.

Questa parte di un'applicazione è chiamata router. Alcuni framework si prendono già cura di loro per te (come Ember), altri richiedono librerie che faranno questo lavoro (come React Router).

Qual è il problema? All'inizio, questo era un ripensamento per gli sviluppatori che costruivano applicazioni a pagina singola. Ciò ha causato il problema comune del "pulsante Indietro rotto": durante la navigazione all'interno dell'applicazione l'URL non è cambiato (poiché la navigazione predefinita del browser è stata dirottata) e premendo il pulsante Indietro, un'operazione comune che gli utenti fanno per passare alla schermata precedente, potrebbe spostarsi su un sito Web visitato molto tempo fa.

Questo problema può ora essere risolto utilizzando l'API di cronologia offerta dai browser, ma la maggior parte delle volte utilizzerai una libreria che utilizza internamente tale API, come React Router.

Dichiarativo

Cosa significa quando leggi che React è dichiarativo? Ti imbatterai in articoli che descrivono React come un approccio dichiarativo alla creazione di interfacce utente.

React rese il suo "approccio dichiarativo" piuttosto popolare e diretto, quindi permeava il mondo frontend insieme a React.

In realtà non è un nuovo concetto, ma React ha preso la creazione di interfacce utente molto più dichiaratamente che con i modelli HTML:

  • puoi costruire interfacce Web senza nemmeno toccare direttamente il DOM
  • puoi avere un sistema di eventi senza dover interagire con gli attuali eventi DOM.

L'opposto di dichiarativo è imperativo. Un esempio comune di approccio imperativo è la ricerca di elementi nel DOM utilizzando eventi jQuery o DOM. Dì al browser esattamente cosa fare, invece di dirgli ciò di cui hai bisogno.

L'approccio dichiarativo di React lo sottrae a noi. Diciamo solo a React che vogliamo che un componente venga renderizzato in un modo specifico e non dobbiamo mai interagire con il DOM per fare riferimento in un secondo momento.

Immutabilità

Un concetto che probabilmente incontrerai quando programmerai in React è l'immutabilità (e il suo contrario, la mutabilità).

È un argomento controverso, ma qualunque cosa tu possa pensare del concetto di immutabilità, React e la maggior parte del suo ecosistema lo forza, quindi devi almeno avere una comprensione del perché è così importante e delle sue implicazioni.

Nella programmazione, una variabile è immutabile quando il suo valore non può cambiare dopo che è stata creata.

Stai già usando variabili immutabili senza saperlo quando manipoli una stringa. Le stringhe sono immutabili per impostazione predefinita, quando le cambi nella realtà crei una nuova stringa e la assegni allo stesso nome di variabile.

Una variabile immutabile non può mai essere modificata. Per aggiornare il suo valore, si crea una nuova variabile.

Lo stesso vale per oggetti e matrici.

Invece di modificare un array, per aggiungere un nuovo elemento si crea un nuovo array concatenando il vecchio array, più il nuovo elemento.

Un oggetto non viene mai aggiornato, ma copiato prima di modificarlo.

Questo vale per React in molti luoghi.

Ad esempio, non si dovrebbe mai mutare direttamente la proprietà state di un componente, ma solo attraverso il metodo setState ().

In Redux, non mutate mai direttamente lo stato, ma solo attraverso i riduttori, che sono funzioni.

La domanda è: perché?

Ci sono varie ragioni, le più importanti delle quali sono:

  • Le mutazioni possono essere centralizzate, come nel caso di Redux, il che migliora le capacità di debug e riduce le fonti di errori.
  • Il codice sembra più pulito e più semplice da capire. Non ti aspetti mai che una funzione cambi un valore a tua insaputa, il che ti dà prevedibilità. Quando una funzione non muta gli oggetti ma restituisce semplicemente un nuovo oggetto, viene chiamata funzione pura.
  • La libreria può ottimizzare il codice perché, ad esempio, JavaScript è più veloce quando si scambia un riferimento a un oggetto vecchio con un oggetto completamente nuovo, anziché mutare un oggetto esistente. Questo ti dà prestazioni.

Purezza

In JavaScript, quando una funzione non muta gli oggetti ma restituisce semplicemente un nuovo oggetto, viene chiamata funzione pura.

Una funzione o un metodo per essere chiamato puro non dovrebbe causare effetti collaterali e dovrebbe restituire lo stesso output quando viene chiamato più volte con lo stesso input.

Una funzione pura accetta un input e restituisce un output senza modificarlo né altro.

Il suo output è determinato solo dagli argomenti. È possibile chiamare questa funzione 1 milione di volte e, dato lo stesso insieme di argomenti, l'output sarà sempre lo stesso.

React applica questo concetto ai componenti. Un componente React è un componente puro quando il suo output dipende solo dai suoi oggetti di scena.

Tutti i componenti funzionali sono componenti puri:

pulsante const = props => {
  return 
}

I componenti di classe possono essere puri se il loro output dipende solo dai puntelli:

Il pulsante class estende React.Component {
  render () {
    return 
  }
}

Composizione

Nella programmazione, la composizione consente di creare funzionalità più complesse combinando funzioni piccole e mirate.

Ad esempio, pensa a usare map () per creare un nuovo array da un set iniziale e quindi a filtrare il risultato usando filter ():

const list = ['Apple', 'Orange', 'Egg']
list.map (item => item [0]). filter (item => item === 'A') // 'A'

In React, la composizione ti consente di avere alcuni vantaggi piuttosto interessanti.

È possibile creare componenti piccoli e snelli e utilizzarli per comporre più funzionalità al di sopra di essi. Come?

Crea una versione specializzata di un componente

Utilizzare un componente esterno per espandere e specializzare un componente più generico:

pulsante const = props => {
  return 
}
const SubmitButton = () => {
  return 
const LoginButton = () => {
  return 

Passa metodi come oggetti di scena

Un componente può concentrarsi sul monitoraggio di un evento clic, ad esempio, e ciò che effettivamente accade quando si verifica l'evento clic dipende dal componente contenitore:

pulsante const = props => {
  return 
}
const LoginButton = props => {
  return 
const Contenitore = () => {
  const onClickHandler = () => {
    alert ( 'cliccato')
  }
  return 
}

Usando i bambini

La proprietà props.children consente di iniettare componenti all'interno di altri componenti.

Il componente deve generare props.children nel suo JSX:

const Sidebar = props => {
  return 
}

e hai incorporato più componenti in esso in modo trasparente:


  
  

Componenti di ordine superiore

Quando un componente riceve un componente come prop e restituisce un componente, viene chiamato componente di ordine superiore.

Li vedremo tra poco.

Il DOM virtuale

Molti framework esistenti, prima che React entrasse in scena, stavano manipolando direttamente il DOM ad ogni cambiamento.

Innanzitutto, cos'è il DOM?

Il DOM (Document Object Model) è una rappresentazione ad albero della pagina, a partire dal tag , che scende in ogni figlio, che sono chiamati nodi.

È conservato nella memoria del browser e direttamente collegato a ciò che vedi in una pagina. Il DOM ha un'API che puoi usare per attraversarlo, accedere a ogni singolo nodo, filtrarli, modificarli.

L'API è la sintassi familiare che probabilmente hai visto molte volte, se non stavi utilizzando l'API astratta fornita da jQuery e dagli amici:

document.getElementById (id)
document.getElementsByTagName (nome)
document.createElement (nome)
parentNode.appendChild (nodo)
element.innerHTML
element.style.left
element.setAttribute ()
element.getAttribute ()
element.addEventListener ()
window.content
window.onload
window.dump ()
window.scrollTo ()

React conserva una copia della rappresentazione DOM, per ciò che riguarda il rendering React: il DOM virtuale

Il DOM virtuale spiegato

Ogni volta che il DOM cambia, il browser deve eseguire due operazioni intensive: ridipingere (modifiche visive o di contenuto a un elemento che non influisce sul layout e sul posizionamento rispetto ad altri elementi) e ridisporre (ricalcolare il layout di una parte della pagina - o l'intero layout di pagina).

React utilizza un DOM virtuale per aiutare il browser a utilizzare meno risorse quando è necessario apportare modifiche a una pagina.

Quando si chiama setState () su un componente, specificando uno stato diverso da quello precedente, React contrassegna quel componente come sporco. Questa è la chiave: React si aggiorna solo quando un Componente cambia esplicitamente lo stato.

Quello che succede dopo è:

  • React aggiorna il DOM virtuale relativamente ai componenti contrassegnati come sporchi (con alcuni controlli aggiuntivi, come l'attivazione di DovComponentUpdate ())
  • Esegue l'algoritmo diffing per riconciliare le modifiche
  • Aggiorna il vero DOM

Perché il DOM virtuale è utile: batch

La cosa fondamentale è che React esegue il batch di gran parte delle modifiche ed esegue un aggiornamento univoco al DOM reale, modificando tutti gli elementi che devono essere modificati contemporaneamente, quindi il ridisegno e il reflow del browser devono eseguire per rendere le modifiche eseguito solo una volta.

Flusso di dati unidirezionale

Lavorando con React potresti incontrare il termine Flusso di dati unidirezionale. Cosa significa? Il flusso di dati unidirezionale non è un concetto unico di React, ma come sviluppatore JavaScript potrebbe essere la prima volta che lo senti.

In generale, questo concetto significa che i dati hanno un solo e solo un modo per essere trasferiti ad altre parti dell'applicazione.

In React questo significa che:

  • lo stato viene passato alla vista e ai componenti figlio
  • le azioni sono attivate dalla vista
  • le azioni possono aggiornare lo stato
  • la modifica dello stato viene passata alla vista e ai componenti figlio

La vista è il risultato dello stato dell'applicazione. Lo stato può cambiare solo quando si verificano azioni. Quando si verificano azioni, lo stato viene aggiornato.

Grazie ai collegamenti unidirezionali, i dati non possono fluire in modo opposto (come accadrebbe, ad esempio, con i collegamenti bidirezionali) e ciò presenta alcuni vantaggi chiave:

  • è meno soggetto a errori, poiché hai un maggiore controllo sui tuoi dati
  • è più facile eseguire il debug, poiché sai da dove viene
  • è più efficiente, poiché la libreria sa già quali sono i limiti di ciascuna parte del sistema

Uno stato è sempre di proprietà di un componente. Tutti i dati che sono interessati da questo stato possono influenzare solo i componenti sottostanti: i suoi figli.

La modifica dello stato di un componente non influirà mai sul suo genitore, sui suoi fratelli o su qualsiasi altro componente nell'applicazione: solo i suoi figli.

Questo è il motivo per cui lo stato viene spesso spostato verso l'alto nella struttura dei componenti, in modo che possa essere condiviso tra i componenti che devono accedervi.

SEZIONE 3: REACT IN PROFONDITÀ

JSX

JSX è una tecnologia che è stata introdotta da React.

Sebbene React possa funzionare perfettamente senza utilizzare JSX, è una tecnologia ideale per lavorare con i componenti, quindi React beneficia molto di JSX.

All'inizio, potresti pensare che usare JSX sia come mescolare HTML e JavaScript (e come vedrai CSS).

Ma questo non è vero, perché ciò che stai realmente facendo quando usi la sintassi JSX è scrivere una sintassi dichiarativa di come dovrebbe essere una UI componente.

E stai descrivendo che l'interfaccia utente non utilizza stringhe, ma invece utilizza JavaScript, che ti consente di fare molte cose carine.

Un primer JSX

Ecco come si definisce un tag h1 contenente una stringa:

const element = 

Ciao, mondo!

Sembra uno strano mix di JavaScript e HTML, ma in realtà è tutto JavaScript.

Quello che sembra HTML, in realtà è zucchero sintattico per la definizione dei componenti e il loro posizionamento all'interno del markup.

All'interno di un'espressione JSX, gli attributi possono essere inseriti molto facilmente:

const myId = 'test'
elemento const = 

Ciao, mondo!

Devi solo prestare attenzione quando un attributo ha un trattino (-) che viene invece convertito in sintassi camelCase e questi 2 casi speciali:

  • la classe diventa className
  • per diventa htmlPer

perché sono parole riservate in JavaScript.

Ecco uno snippet JSX che avvolge due componenti in un tag div:

     

Un tag deve sempre essere chiuso, perché è più XML che HTML (se ricordi i giorni XHTML, questo sarà familiare, ma da allora ha vinto la sintassi allentata HTML5). In questo caso viene utilizzato un tag a chiusura automatica.

Nota come ho avvolto i 2 componenti in un div. Perché? Poiché la funzione render () può restituire solo un singolo nodo, quindi nel caso in cui si desideri restituire 2 fratelli, basta aggiungere un genitore. Può essere qualsiasi tag, non solo div.

Transpiling JSX

Un browser non può eseguire file JavaScript contenenti codice JSX. Devono essere prima trasformati in normali JS.

Come? Eseguendo un processo chiamato transpiling.

Abbiamo già detto che JSX è facoltativo, perché per ogni linea JSX è disponibile un'alternativa JavaScript semplice corrispondente, ed è ciò a cui viene trascritto JSX.

Ad esempio i seguenti due costrutti sono equivalenti:

Semplice JS
ReactDOM.render (
  React.DOM.div (
    {id: 'test'},
    React.DOM.h1 (null, 'A title'),
    React.DOM.p (null, 'A paragrafo')
  ),
  document.getElementById ( 'myapp')
)
JSX
ReactDOM.render (
  
    

Un titolo

    

Un paragrafo

  ,   document.getElementById ( 'myapp') )

Questo esempio molto semplice è solo il punto di partenza, ma puoi già vedere quanto sia complicata la semplice sintassi JS rispetto all'utilizzo di JSX.

Al momento in cui scrivo, il modo più popolare per eseguire il transpilation è usare Babel, che è l'opzione predefinita quando si esegue create -eagire-app, quindi se lo si utilizza non ci si deve preoccupare, tutto accade sotto il cofano per voi.

Se non usi la app create-reazioni, devi configurare Babel da solo.

JS in JSX

JSX accetta qualsiasi tipo di JavaScript miscelato in esso.

Ogni volta che devi aggiungere un po 'di JS, inseriscilo tra parentesi graffe {}. Ad esempio, ecco come utilizzare un valore costante definito altrove:

const comma = 'Un paragrafo'
ReactDOM.render (
  
    

Un titolo

    

{paragraph}   ,   document.getElementById ( 'myapp') )

Questo è un esempio di base. Le parentesi graffe accettano qualsiasi codice JS:

const comma = 'Un paragrafo'
ReactDOM.render (
  
    {rows.map ((row, i) => {
      return  {row.text} 
    })}
  ,
  document.getElementById ( 'myapp')
)

Come puoi vedere, abbiamo annidato JavaScript all'interno di JSX definito all'interno di JavaScript annidato in JSX. Puoi andare tanto in profondità quanto ti serve.

HTML in JSX

JSX assomiglia molto a HTML, ma in realtà è una sintassi XML.

Alla fine si esegue il rendering dell'HTML, quindi è necessario conoscere alcune differenze tra il modo in cui si definiscono alcune cose in HTML e il modo in cui le si definisce in JSX.

Devi chiudere tutti i tag

Proprio come in XHTML, se l'hai mai usato, devi chiudere tutti i tag: non più
, ma usa invece il tag a chiusura automatica:
(lo stesso vale per gli altri tag)

camelCase è il nuovo standard

In HTML troverai gli attributi senza nessun caso (ad esempio onchange). In JSX, vengono rinominati nel loro equivalente camelCase:

  • onchange => onChange
  • onclick => onClick
  • onsubmit => onSubmit

la classe diventa className

A causa del fatto che JSX è JavaScript e class è una parola riservata, non puoi scrivere

ma devi usare

Lo stesso vale per il quale è tradotto in htmlFor.

CSS in React

JSX fornisce un modo interessante per definire CSS.

Se hai una piccola esperienza con gli stili HTML inline, a prima vista ti ritroverai indietro di 10 o 15 anni, in un mondo in cui i CSS inline erano completamente normali (al giorno d'oggi è demonizzato e di solito solo una "soluzione rapida" soluzione).

Lo stile JSX non è la stessa cosa: prima di tutto, invece di accettare una stringa contenente proprietà CSS, l'attributo dello stile JSX accetta solo un oggetto. Ciò significa che si definiscono le proprietà in un oggetto:

var divStyle = {
  colore bianco'
}
ReactDOM.render (
Hello World!
, mountNode)

o

ReactDOM.render (
Hello World!
, mountNode)

I valori CSS che scrivi in ​​JSX sono leggermente diversi dai semplici CSS:

  • i nomi delle proprietà delle chiavi sono camelCased
  • i valori sono solo stringhe
  • separi ogni tupla con una virgola

Perché questo è preferito al semplice CSS / SASS / LESS?

CSS è un problema irrisolto. Fin dal suo inizio, dozzine di strumenti attorno ad esso si sollevarono e poi caddero. Il problema principale con JS è che non esiste alcun ambito ed è facile scrivere CSS che non sia applicato in alcun modo, quindi una "correzione rapida" può avere un impatto su elementi che non devono essere toccati.

JSX consente ai componenti (definiti ad esempio in React) di incapsulare completamente il loro stile.

È questa la soluzione ideale?

Gli stili incorporati in JSX sono buoni fino a quando non è necessario

  1. scrivere query multimediali
  2. animazioni di stile
  3. pseudo classi di riferimento (ad es. hover)
  4. pseudo elementi di riferimento (ad es .: prima lettera)

In breve, coprono le basi, ma non è la soluzione finale.

Moduli in JSX

JSX aggiunge alcune modifiche al funzionamento dei moduli HTML, con l'obiettivo di semplificare le cose per lo sviluppatore.

value e defaultValue

L'attributo value contiene sempre il valore corrente del campo.

L'attributo defaultValue contiene il valore predefinito impostato al momento della creazione del campo.

Questo aiuta a risolvere alcuni strani comportamenti della normale interazione DOM durante l'ispezione di input.value e input.getAttribute ('valore') che restituisce uno il valore corrente e uno il valore predefinito originale.

Questo vale anche per il campo textarea, ad es.

ma invece