Creazione di un editor di codice Web

Pubblicato: 2022-03-10
Riepilogo rapido ↬ Se sei uno sviluppatore che sta pensando di creare una piattaforma che richieda un editor di codice in una forma o nell'altra, allora questo articolo fa per te. Questo articolo spiega come creare un editor di codice web che visualizzi il risultato in tempo reale con l'aiuto di HTML, CSS e JavaScript.

Un editor di codice Web online è particolarmente utile quando non si ha l'opportunità di utilizzare un'applicazione di editor di codice o quando si desidera provare rapidamente qualcosa sul Web con il computer o anche con il telefono cellulare. Questo è anche un progetto interessante su cui lavorare perché avere la conoscenza di come costruire un editor di codice ti darà idee su come affrontare altri progetti che richiedono l'integrazione di un editor di codice per mostrare alcune funzionalità.

Ecco alcuni concetti di React che devi conoscere per seguire in questo articolo:

  • Ganci,
  • struttura dei componenti,
  • componenti funzionali,
  • Puntelli.

Utilizzo di CodeMirror

Utilizzeremo una libreria denominata CodeMirror per creare il nostro editor. CodeMirror è un versatile editor di testo implementato in JavaScript per il browser. È specialmente per la modifica del codice e viene fornito con una serie di modalità linguistiche e componenti aggiuntivi per funzionalità di modifica più avanzate.

Sono disponibili una ricca API di programmazione e un sistema di temi CSS per personalizzare CodeMirror per adattarlo alla tua applicazione ed estenderlo con nuove funzionalità. Ci dà la funzionalità per creare un ricco editor di codice che gira sul web e ci mostra il risultato del nostro codice in tempo reale.

Nella prossima sezione, imposteremo il nostro nuovo progetto React e installeremo le librerie di cui abbiamo bisogno per costruire la nostra web app.

Creazione di un nuovo progetto React

Iniziamo creando un nuovo progetto React. Nella tua interfaccia a riga di comando, vai alla directory in cui vuoi creare il tuo progetto e creiamo un'applicazione React e code_editor :

 npx create-react-app code_editor

Dopo aver creato la nostra nuova applicazione React, andiamo alla directory di quel progetto nell'interfaccia della riga di comando:

 cd code_editor

Ci sono due librerie che dobbiamo installare qui: codemirror e react-codemirror2 .

 npm install codemirror react-codemirror2

Dopo aver installato le librerie di cui abbiamo bisogno per questo progetto, creiamo le nostre schede e abilitiamo il passaggio tra le tre schede che appariranno nel nostro editor (per HTML, CSS e JavaScript).

Altro dopo il salto! Continua a leggere sotto ↓

Componente pulsante

Invece di creare singoli pulsanti, rendiamo il pulsante un componente riutilizzabile. Nel nostro progetto, il pulsante avrebbe tre istanze, in base alle tre schede di cui abbiamo bisogno.

Crea una cartella denominata components nella cartella src . In questa nuova cartella components , crea un file JSX denominato Button.jsx .

Ecco tutto il codice necessario nel componente Button :

 import React from 'react' const Button = ({title, onClick}) => { return ( <div> <button style={{ maxWidth: "140px", minWidth: "80px", height: "30px", marginRight: "5px" }} onClick={onClick} > {title} </button> </div> ) } export default Button

Ecco una spiegazione completa di ciò che abbiamo fatto sopra:

  • Abbiamo creato un componente funzionale chiamato Button , che abbiamo poi esportato.
  • Abbiamo destrutturato title e onClick dagli oggetti di scena che entrano nel componente. Qui, il title sarebbe una stringa di testo e onClick sarebbe una funzione che viene chiamata quando si fa clic su un pulsante.
  • Successivamente, abbiamo utilizzato l'elemento button per dichiarare il nostro pulsante e abbiamo utilizzato gli attributi di style per dare uno stile al nostro pulsante in modo che appaia presentabile.
  • Abbiamo aggiunto l'attributo onClick e gli abbiamo passato la nostra funzione destrutturata onClick props.
  • L'ultima cosa che noterai che abbiamo fatto in questo componente è passare in {title} come contenuto del tag del button . Questo ci consente di visualizzare il titolo in modo dinamico, in base a quale prop viene passato all'istanza del componente button quando viene chiamato.

Ora che abbiamo creato un componente pulsante riutilizzabile, andiamo avanti e portiamo il nostro componente in App.js. Vai su App.js e importa il componente pulsante appena creato:

 import Button from './components/Button';

Per tenere traccia di quale scheda o editor è aperto, è necessario uno stato di dichiarazione per mantenere il valore dell'editor aperto. Usando l'hook useState React, imposteremo lo stato che memorizzerà il nome della scheda dell'editor che è attualmente aperta quando si fa clic sul pulsante di quella scheda.

Ecco come lo facciamo:

 import React, { useState } from 'react'; import './App.css'; import Button from './components/Button'; function App() { const [openedEditor, setOpenedEditor] = useState('html'); return ( <div className="App"> </div> ); } export default App;

Qui abbiamo dichiarato il nostro stato. Prende il nome dell'editor attualmente aperto. Poiché il valore html viene passato come valore predefinito dello stato, l'editor HTML sarebbe la scheda aperta per impostazione predefinita.

Andiamo avanti e scriviamo la funzione che utilizzerà setOpenedEditor per modificare il valore dello stato quando si fa clic su un pulsante di scheda.

Nota: due schede potrebbero non essere aperte contemporaneamente, quindi dovremo tenerne conto quando scriviamo la nostra funzione.

Ecco come appare la nostra funzione, denominata onTabClick :

 import React, { useState } from 'react'; import './App.css'; import Button from './components/Button'; function App() { ... const onTabClick = (editorName) => { setOpenedEditor(editorName); }; return ( <div className="App"> </div> ); } export default App;

Qui, abbiamo passato un singolo argomento di funzione, che è il nome della scheda attualmente selezionata. Questo argomento verrebbe fornito ovunque venga chiamata la funzione e verrebbe passato il nome pertinente di quella scheda.

Creiamo tre istanze del nostro Button per le tre schede di cui abbiamo bisogno:

 <div className="App"> <p>Welcome to the editor!</p> <div className="tab-button-container"> <Button title="HTML" onClick={() => { onTabClick('html') }} /> <Button title="CSS" onClick={() => { onTabClick('css') }} /> <Button title="JavaScript" onClick={() => { onTabClick('js') }} /> </div> </div>

Ecco cosa abbiamo fatto:

  • Abbiamo iniziato aggiungendo un tag p , fondamentalmente solo per dare un po' di contesto all'argomento della nostra applicazione.
  • Abbiamo utilizzato un tag div per avvolgere i pulsanti delle schede. Il tag div contiene un className che useremo per definire lo stile dei pulsanti in una visualizzazione a griglia nel file CSS più avanti in questo tutorial.
  • Successivamente, abbiamo dichiarato tre istanze del componente Button . Se ricordi, il componente Button richiede due oggetti di scena, title e onClick . In ogni istanza del componente Button , vengono forniti questi due oggetti di scena.
  • Il title prop prende il titolo della scheda.
  • Il prop onClick prende una funzione, onTabClick , che abbiamo appena creato e che accetta un solo argomento: il nome della scheda selezionata.

In base alla scheda attualmente selezionata, utilizzeremo l'operatore ternario JavaScript per visualizzare la scheda in modo condizionale. Ciò significa che se il valore dello stato openedEditor è impostato su html (ie setOpenedEditor('html') ), la scheda per la sezione HTML diventerebbe la scheda attualmente visibile. Lo capirai meglio mentre lo facciamo di seguito:

 ... return ( <div className="App"> ... <div className="editor-container"> { openedEditor === 'html' ? ( <p>The html editor is open</p> ) : openedEditor === 'css' ? ( <p>The CSS editor is open!!!!!!</p> ) : ( <p>the JavaScript editor is open</p> ) } </div> </div> ); ...

Esaminiamo il codice sopra in un inglese semplice. Se il valore di openedEditor è html , visualizza la sezione HTML. Altrimenti, se il valore di openedEditor è css , visualizza la sezione CSS. Altrimenti, se il valore non è né htmlcss , significa che il valore deve essere js , perché abbiamo solo tre valori possibili per lo stato openedEditor ; quindi, visualizzeremmo la scheda per JavaScript.

Abbiamo utilizzato i tag di paragrafo ( p ) per le diverse sezioni nelle condizioni dell'operatore ternario. Mentre procediamo, creeremo i componenti dell'editor e sostituiremo i tag p con gli stessi componenti dell'editor.

Siamo già arrivati ​​così lontano! Quando si fa clic su un pulsante, viene attivata l'azione che imposta la scheda che rappresenta su true , rendendo quella scheda visibile. Ecco come appare attualmente la nostra app:

Una GIF che mostra l'interruttore di tabulazione che abbiamo attualmente.
Una GIF che mostra l'interruttore di tabulazione che abbiamo attualmente. (Grande anteprima)

Aggiungiamo un po' di CSS al contenitore div che contiene i pulsanti. Vogliamo che i pulsanti vengano visualizzati in una griglia, invece che impilati verticalmente come nell'immagine sopra. Vai al tuo file App.css e aggiungi il codice seguente:

 .tab-button-container{ display: flex; }

Ricordiamo che abbiamo aggiunto className="tab-button-container" come attributo nel tag div contenente i pulsanti a tre schede. Qui, abbiamo disegnato quel contenitore, usando CSS per impostarne la visualizzazione su flex . Questo è il risultato:

Usiamo CSS per impostare la sua visualizzazione in modo flessibile
(Grande anteprima)

Sii orgoglioso di quanto hai fatto per arrivare a questo punto. Nella prossima sezione creeremo i nostri editor, sostituendo i tag p con essi.

Creazione degli editori

Poiché abbiamo già installato le librerie su cui lavoreremo all'interno del nostro editor CodeMirror, andiamo avanti e creiamo il nostro file Editor.jsx nella cartella dei components .

componenti > Editor.jsx

Dopo aver creato il nostro nuovo file, scriviamo del codice iniziale al suo interno:

 import React, { useState } from 'react'; import 'codemirror/lib/codemirror.css'; import { Controlled as ControlledEditorComponent } from 'react-codemirror2'; const Editor = ({ language, value, setEditorState }) => { return ( <div className="editor-container"> </div> ) } export default Editor

Ecco cosa abbiamo fatto:

  • Abbiamo importato React insieme useState perché ne avremo bisogno.
  • Abbiamo importato il file CSS CodeMirror (che proviene dalla libreria CodeMirror che abbiamo installato, quindi non devi installarlo in alcun modo speciale).
  • Abbiamo importato Controlled da react-codemirror2 , rinominandolo in ControlledEditorComponent per renderlo più chiaro. Lo useremo a breve.
  • Quindi, abbiamo dichiarato il nostro componente funzionale Editor e abbiamo un'istruzione return con un div vuoto, con un className return per ora.

Nel nostro componente funzionale, abbiamo destrutturato alcuni valori dagli oggetti di scena, inclusi language , value e setEditorState . Questi tre prop verrebbero forniti in qualsiasi istanza dell'editor quando viene chiamato in App.js .

Usiamo ControlledEditorComponent per scrivere il codice per il nostro editor. Ecco cosa faremo:

 import React, { useState } from 'react'; import 'codemirror/lib/codemirror.css'; import 'codemirror/mode/xml/xml'; import 'codemirror/mode/javascript/javascript'; import 'codemirror/mode/css/css'; import { Controlled as ControlledEditorComponent } from 'react-codemirror2'; const Editor = ({ language, value, setEditorState }) => { return ( <div className="editor-container"> <ControlledEditorComponent onBeforeChange={handleChange} value= {value} className="code-mirror-wrapper" options={{ lineWrapping: true, lint: true, mode: language, lineNumbers: true, }} /> </div> ) } export default Editor

Esaminiamo ciò che abbiamo fatto qui, spiegando alcuni termini di CodeMirror.

Le modalità CodeMirror specificano a quale lingua è destinato un editor. Abbiamo importato tre modalità perché abbiamo tre editor per questo progetto:

  1. XML: questa modalità è per HTML. Usa il termine XML.
  2. JavaScript: questo ( codemirror/mode/javascript/javascript ) porta in modalità JavaScript.
  3. CSS: questo ( codemirror/mode/css/css ) porta in modalità CSS.

Nota: poiché l'editor è creato come componente riutilizzabile, non è possibile inserire una modalità diretta nell'editor. Quindi, forniamo la modalità attraverso il supporto language che abbiamo destrutturato. Ma questo non cambia il fatto che le modalità devono essere importate per funzionare.

Quindi, discutiamo le cose in ControlledEditorComponent :

  • onBeforeChange
    Viene chiamato ogni volta che scrivi o rimuovi dall'editor. Pensa a questo come al gestore onChange che normalmente avresti in un campo di input per tenere traccia delle modifiche. Usando questo, saremo in grado di ottenere il valore del nostro editor ogni volta che c'è una nuova modifica e salvarlo nello stato del nostro editor. Scriveremo la funzione {handleChange} mentre procediamo.
  • value = {value}
    Questo è solo il contenuto dell'editor in un dato momento. Abbiamo passato un prop destrutturato denominato value a questo attributo. Il value props è lo stato che detiene il valore di quell'editor. Questo verrebbe fornito dall'istanza dell'editor.
  • className ="code-mirror-wrapper"
    Questo nome di classe non è uno stile che creiamo noi stessi. Viene fornito dal file CSS di CodeMirror, che abbiamo importato sopra.
  • options
    Questo è un oggetto che prende le diverse funzionalità che vogliamo che il nostro editor abbia. Ci sono molte fantastiche opzioni in CodeMirror. Diamo un'occhiata a quelli che abbiamo usato qui:
    • lineWrapping: true
      Ciò significa che il codice dovrebbe andare a capo alla riga successiva quando la riga è piena.
    • lint: true
      Ciò consente di sfilacciare.
    • mode: language
      Questa modalità, come discusso in precedenza, utilizza la lingua per cui verrà utilizzato l'editor. La lingua è già stata importata sopra, ma l'editor applicherà una lingua basata sul valore della language fornito all'editor tramite il prop.
    • lineNumbers: true
      Questo specifica che l'editor deve avere numeri di riga per ogni riga.

Successivamente, possiamo scrivere la funzione handleChange per il gestore onBeforeChange :

 const handleChange = (editor, data, value) => { setEditorState(value); }

Il gestore onBeforeChange ci dà accesso a tre cose: editor, data, value .

Abbiamo solo bisogno del value perché è quello che vogliamo passare nel nostro setEditorState prop. La prop setEditorState rappresenta il valore impostato per ogni stato dichiarato in App.js , mantenendo il valore per ogni editor. Mentre andiamo avanti, vedremo come passare questo come supporto al componente Editor .

Successivamente, aggiungeremo un menu a discesa che ci consente di selezionare diversi temi per l'editor. Quindi, diamo un'occhiata ai temi in CodeMirror.

Temi CodeMirror

CodeMirror ha più temi tra cui possiamo scegliere. Visita il sito ufficiale per vedere le demo dei diversi temi disponibili. Creiamo un menu a discesa con diversi temi tra cui l'utente può scegliere nel nostro editor. Per questo tutorial, aggiungeremo cinque temi, ma puoi aggiungerne quanti ne vuoi.

Innanzitutto, importiamo i nostri temi nel componente Editor.js :

 import 'codemirror/theme/dracula.css'; import 'codemirror/theme/material.css'; import 'codemirror/theme/mdn-like.css'; import 'codemirror/theme/the-matrix.css'; import 'codemirror/theme/night.css';

Quindi, crea una matrice di tutti i temi che abbiamo importato:

 const themeArray = ['dracula', 'material', 'mdn-like', 'the-matrix', 'night']

Dichiariamo un hook useState per mantenere il valore del tema selezionato e impostiamo il tema predefinito come dracula :

 const [theme, setTheme] = useState("dracula")

Creiamo il menu a tendina:

 ... return ( <div className="editor-container"> <div style={{marginBottom: "10px"}}> <label for="cars">Choose a theme: </label> <select name="theme" onChange={(el) => { setTheme(el.target.value) }}> { themeArray.map( theme => ( <option value={theme}>{theme}</option> )) } </select> </div> // the rest of the code comes below... </div> ) ...

Nel codice sopra, abbiamo utilizzato il tag HTML label per aggiungere un'etichetta al nostro menu a discesa, quindi abbiamo aggiunto il tag HTML select per creare il nostro menu a discesa. Il tag option nell'elemento select definisce le opzioni disponibili nel menu a discesa.

Poiché dovevamo riempire il menu a discesa con i nomi dei temi nel themeArray che abbiamo creato, abbiamo utilizzato il metodo dell'array .map per mappare themeArray e visualizzare i nomi individualmente utilizzando il tag option .

Aspetta: non abbiamo finito di spiegare il codice sopra. Nel tag di select di apertura, abbiamo passato l'attributo onChange per tenere traccia e aggiornare lo stato del theme ogni volta che viene selezionato un nuovo valore nel menu a discesa. Ogni volta che viene selezionata una nuova opzione nel menu a discesa, il valore viene ottenuto dall'oggetto restituito. Successivamente, utilizziamo setTheme dal nostro hook di stato per impostare il nuovo valore in modo che sia il valore che contiene lo stato.

A questo punto, abbiamo creato il nostro menu a discesa, impostato lo stato del nostro tema e scritto la nostra funzione per impostare lo stato con il nuovo valore. L'ultima cosa che dobbiamo fare per fare in modo che CodeMirror utilizzi il nostro tema è passare il tema all'oggetto options in ControlledEditorComponent . Nell'oggetto options , aggiungiamo un valore denominato theme e impostiamo il suo valore sul valore dello stato per il tema selezionato, anch'esso denominato theme .

Ecco come sarebbe ControlledEditorComponent ora:

 <ControlledEditorComponent onBeforeChange={handleChange} value= {value} className="code-mirror-wrapper" options={{ lineWrapping: true, lint: true, mode: language, lineNumbers: true, theme: theme, }} />

Ora abbiamo creato un menu a discesa di diversi temi che possono essere selezionati nell'editor.

Ecco come appare al momento il codice completo in Editor.js :

 import React, { useState } from 'react'; import 'codemirror/lib/codemirror.css'; import 'codemirror/theme/dracula.css'; import 'codemirror/theme/material.css'; import 'codemirror/theme/mdn-like.css'; import 'codemirror/theme/the-matrix.css'; import 'codemirror/theme/night.css'; import 'codemirror/mode/xml/xml'; import 'codemirror/mode/javascript/javascript'; import 'codemirror/mode/css/css'; import { Controlled as ControlledEditorComponent } from 'react-codemirror2'; const Editor = ({ language, value, setEditorState }) => { const [theme, setTheme] = useState("dracula") const handleChange = (editor, data, value) => { setEditorState(value); } const themeArray = ['dracula', 'material', 'mdn-like', 'the-matrix', 'night'] return ( <div className="editor-container"> <div style={{marginBottom: "10px"}}> <label for="themes">Choose a theme: </label> <select name="theme" onChange={(el) => { setTheme(el.target.value) }}> { themeArray.map( theme => ( <option value={theme}>{theme}</option> )) } </select> </div> <ControlledEditorComponent onBeforeChange={handleChange} value= {value} className="code-mirror-wrapper" options={{ lineWrapping: true, lint: true, mode: language, lineNumbers: true, theme: theme, }} /> </div> ) } export default Editor

C'è solo un className di cui abbiamo bisogno per lo stile. Vai su App.css e aggiungi il seguente stile:

 .editor-container{ padding-top: 0.4%; }

Ora che i nostri editor sono pronti, torniamo ad App.js e usiamoli lì.

src > App.js

La prima cosa che dobbiamo fare è importare il componente Editor.js qui:

 import Editor from './components/Editor';

In App.js dichiariamo gli stati che conterranno rispettivamente il contenuto degli editor HTML, CSS e JavaScript.

 const [html, setHtml] = useState(''); const [css, setCss] = useState(''); const [js, setJs] = useState('');

Se ricordi, dovremo utilizzare questi stati per contenere e fornire i contenuti dei nostri editori.

Quindi, sostituiamo i tag paragrafo ( p ) che abbiamo usato per HTML, CSS e JavaScript nei rendering condizionali con i componenti dell'editor che abbiamo appena creato e passeremo anche l'appropriato prop a ciascuna istanza dell'editor componente:

 function App() { ... return ( <div className="App"> <p>Welcome to the edior</p> // This is where the tab buttons container is... <div className="editor-container"> { htmlEditorIsOpen ? ( <Editor language="xml" value={html} setEditorState={setHtml} /> ) : cssEditorIsOpen ? ( <Editor language="css" value={css} setEditorState={setCss} /> ) : ( <Editor language="javascript" value={js} setEditorState={setJs} /> ) } </div> </div> ); } export default App;

Se hai seguito fino ad ora, capirai cosa abbiamo fatto nel blocco di codice sopra.

Eccolo in parole povere: abbiamo sostituito i tag p (che erano presenti come segnaposto) con istanze dei componenti dell'editor. Quindi, abbiamo fornito le loro props language , value e setEditorState , rispettivamente, in modo che corrispondano ai loro stati corrispondenti.

Siamo arrivati ​​così lontano! Ecco come appare ora la nostra app:

Come appare ora la nostra app
(Grande anteprima)

Introduzione agli iframe

Utilizzeremo i frame inline (iframe) per visualizzare il risultato del codice inserito nell'editor.

Secondo MDN:

L'elemento HTML Inline Frame ( <iframe> ) rappresenta un contesto di navigazione nidificato, incorporando un'altra pagina HTML in quella corrente.

Come funzionano gli Iframe in React

Gli iframe sono normalmente usati con HTML semplice. L'uso di Iframes con React non richiede molte modifiche, la principale è la conversione dei nomi degli attributi in camelcase. Un esempio di ciò è che srcdoc diventerebbe srcDoc .

Il futuro degli iframe sul Web

Gli iframe continuano ad essere davvero utili nello sviluppo web. Qualcosa che potresti voler controllare è Portals. Come spiega Daniel Brain:

“I portali introducono una nuova potente serie di funzionalità in questo mix. Ora è possibile creare qualcosa che sembri un iframe, che può animare e trasformarsi senza problemi e occupare l'intera finestra del browser".

Una delle cose che Portals cerca di risolvere è il problema della barra degli URL. Quando si utilizza iframe, i componenti visualizzati nell'iframe non hanno un URL univoco nella barra degli indirizzi; in quanto tale, questo potrebbe non essere ottimo per l'esperienza dell'utente, a seconda del caso d'uso. Vale la pena dare un'occhiata a Portals e ti suggerisco di farlo, ma poiché non è l'obiettivo del nostro articolo, questo è tutto ciò che dirò al riguardo qui.

Creazione dell'iframe per ospitare il nostro risultato

Andiamo avanti con il nostro tutorial creando un iframe per ospitare il risultato dei nostri editor.

 return ( <div className="App"> // ... <div> <iframe srcDoc={srcDoc} title="output" sandbox="allow-scripts" frameBorder="1" width="100%" height="100%" /> </div> </div> );

Qui abbiamo creato l'iframe e lo abbiamo ospitato in un tag contenitore div . Nell'iframe, abbiamo passato alcuni attributi di cui abbiamo bisogno:

  • srcDoc
    L'attributo srcDoc è scritto in camelcase perché questo è il modo in cui scrivere gli attributi iframe in React. Quando si utilizza un iframe, è possibile incorporare una pagina Web esterna nella pagina o eseguire il rendering del contenuto HTML specificato. Per caricare e incorporare una pagina esterna, utilizzeremo invece la proprietà src . Nel nostro caso, non stiamo caricando una pagina esterna; piuttosto, vogliamo creare un nuovo documento HTML interno che contenga il nostro risultato; per questo, abbiamo bisogno dell'attributo srcDoc . Questo attributo prende il documento HTML che vogliamo incorporare (non l'abbiamo ancora creato, ma lo faremo presto).
  • title
    L'attributo title viene utilizzato per descrivere il contenuto del frame inline.
  • sandbox
    Questa proprietà ha molti scopi. Nel nostro caso, lo stiamo utilizzando per consentire l'esecuzione di script nel nostro iframe con il valore allow-scripts . Poiché stiamo lavorando con un editor JavaScript, questo sarebbe utile rapidamente.
  • frameBorder
    Questo definisce semplicemente lo spessore del bordo dell'iframe.
  • width e height
    Questo definisce la larghezza e l'altezza dell'iframe.

Questi termini ora dovrebbero avere più senso per te. Andiamo avanti e dichiariamo lo stato che conterrà il documento modello HTML per srcDoc . Se osservi attentamente il blocco di codice sopra, vedrai che abbiamo passato un valore all'attributo srcDoc : srcDoc ={srcDoc} . Usiamo il nostro useState() React per dichiarare lo stato srcDoc . Per fare ciò, nel file App.js , vai dove abbiamo definito gli altri stati e aggiungi questo:

 const [srcDoc, setSrcDoc] = useState(` `);

Ora che abbiamo creato lo stato, la prossima cosa da fare è visualizzare il risultato nello stato ogni volta che digitiamo nell'editor di codice. Ma quello che non vogliamo è ridisegnare il componente ad ogni singolo tasto premuto. Con questo in mente, procediamo.

Configurazione dell'iframe per visualizzare il risultato

Ogni volta che c'è un cambiamento in uno qualsiasi degli editor per HTML, CSS e JavaScript, rispettivamente, vogliamo che useEffect() venga attivato e questo renderà il risultato aggiornato nell'iframe. Scriviamo useEffect() per farlo nel file App.js :

Per prima cosa, importa l' useEffect() :

 import React, { useState, useEffect } from 'react';

Scriviamo useEffect() in questo modo:

 useEffect(() => { const timeOut = setTimeout(() => { setSrcDoc( ` <html> <body>${html}</body> <style>${css}</style> <script>${js}</script> </html> ` ) }, 250); return () => clearTimeout(timeOut) }, [html, css, js])

Qui, abbiamo scritto un useEffect() che verrà sempre eseguito ogni volta che i valori dichiarati per gli editor HTML, CSS e JavaScript vengono modificati o aggiornati.

Perché abbiamo dovuto usare setTimeout() ? Bene, se lo scrivessimo senza di esso, ogni volta che viene premuto un singolo tasto in un editor, il nostro iframe verrebbe aggiornato, e questo non è eccezionale per le prestazioni in generale. Quindi usiamo setTimeout() per ritardare l'aggiornamento di 250 millisecondi, dandoci abbastanza tempo per sapere se l'utente sta ancora digitando. Cioè, ogni volta che l'utente preme un tasto, riavvia il conteggio, quindi l'iframe verrà aggiornato solo quando l'utente è rimasto inattivo (non digitando) per 250 millisecondi. Questo è un ottimo modo per evitare di dover aggiornare l'iframe ogni volta che viene premuto un tasto.

La prossima cosa che abbiamo fatto sopra è stata aggiornare srcDoc con le nuove modifiche. Il componente srcDoc , come spiegato sopra, esegue il rendering del contenuto HTML specificato nell'iframe. Nel nostro codice, abbiamo passato un modello HTML, prendendo lo stato html che contiene il codice che l'utente ha digitato nell'editor HTML e inserendolo tra i tag body del nostro modello. Abbiamo anche preso lo stato css che contiene gli stili che l'utente ha digitato nell'editor CSS e lo abbiamo passato tra i tag di style . Infine, abbiamo preso lo stato js che contiene il codice JavaScript che l'utente ha digitato nell'editor JavaScript e lo abbiamo passato tra i tag di script .

Si noti che nell'impostazione setSrcDoc , abbiamo usato i backtick ( ` ` ) invece delle virgolette normali ( ' ' ). Questo perché i backtick ci consentono di passare i valori di stato corrispondenti, come abbiamo fatto nel codice sopra.

L'istruzione return useEffect() è una funzione di pulizia che cancella setTimeout() quando è completa, per evitare perdite di memoria. La documentazione contiene ulteriori informazioni su useEffect .

Ecco come si presenta il nostro progetto in questo momento:

Come appare il nostro progetto in questo momento
(Grande anteprima)

Componenti aggiuntivi CodeMirror

Con i componenti aggiuntivi di CodeMirror, possiamo migliorare il nostro editor con più del tipo di funzionalità che troveremmo in altri editor di codice. Esaminiamo un esempio di tag di chiusura aggiunti automaticamente quando viene digitato un tag di apertura e un altro esempio di parentesi che si chiude automaticamente quando viene immessa la parentesi di apertura:

La prima cosa da fare è importare l'addon per questo nel nostro file App.js :

 import 'codemirror/addon/edit/closetag'; import 'codemirror/addon/edit/closebrackets';

Passiamolo nelle opzioni di ControlledEditorComponent :

 <ControlledEditorComponent ... options={{ ... autoCloseTags: true, autoCloseBrackets: true, }} />

Ora ecco cosa abbiamo:

Come appare il nostro progetto
(Grande anteprima)

Potresti aggiungere un sacco di questi componenti aggiuntivi al tuo editor per dargli funzionalità più ricche. Non potremmo assolutamente esaminarli tutti qui.

Ora che abbiamo finito con questo, discutiamo brevemente delle cose che potremmo fare per migliorare l'accessibilità e le prestazioni della nostra app.

Prestazioni e accessibilità della soluzione

Guardando il nostro editor di codice web, alcune cose potrebbero sicuramente essere migliorate.

Poiché abbiamo prestato attenzione principalmente alla funzionalità, potremmo aver trascurato un po' il design. Per una migliore accessibilità, ecco alcune cose che potresti fare per migliorare questa soluzione:

  1. Puoi impostare una classe active sul pulsante per l'editor attualmente aperto. L'evidenziazione del pulsante migliorerebbe l'accessibilità fornendo agli utenti una chiara indicazione dell'editor su cui stanno attualmente lavorando.
  2. Potresti volere che l'editor occupi più spazio sullo schermo di quello che abbiamo qui. Un'altra cosa che potresti provare è far apparire l'iframe con il clic di un pulsante che è agganciato da qualche parte a lato. In questo modo si darebbe all'editor più spazio sullo schermo.
  3. Questo tipo di editor sarebbe utile per le persone che desiderano eseguire un rapido esercizio sul proprio dispositivo mobile, quindi sarebbe necessario adattarlo completamente al dispositivo mobile (per non parlare di entrambi i punti sul dispositivo mobile sopra).
  4. Attualmente, siamo in grado di cambiare il tema del componente dell'editor tra i molteplici temi che abbiamo caricato, ma il tema generale della pagina rimane lo stesso. Puoi consentire all'utente di passare da un tema scuro a uno chiaro per l'intero layout. Questo sarebbe positivo per l'accessibilità, alleviando lo sforzo sugli occhi delle persone dal guardare uno schermo luminoso per troppo tempo.
  5. Non abbiamo esaminato i problemi di sicurezza con il nostro iframe, principalmente perché stavamo caricando un documento HTML interno nell'iframe, piuttosto che un documento esterno. Quindi non dobbiamo considerarlo troppo attentamente perché gli iframe si adattano bene al nostro caso d'uso.
  6. Con gli iframe, un'altra considerazione sarebbe il tempo di caricamento della pagina, perché il contenuto caricato nell'iframe sarebbe normalmente fuori dal tuo controllo. Nella nostra app, questo non è un problema perché il nostro contenuto iframe non è esterno.

Prestazioni e accessibilità meritano molta considerazione durante la creazione di un'applicazione perché determineranno l'utilità e l'usabilità dell'applicazione per i suoi utenti.

Shedrack ha svolto un buon lavoro nello spiegare i metodi per migliorare e ottimizzare le prestazioni nelle app React. Vale la pena dare un'occhiata!

Conclusione

Lavorare su diversi progetti ci aiuta a conoscere una vasta gamma di argomenti. Ora che hai esaminato questo articolo, sentiti libero di espandere la tua esperienza sperimentando più componenti aggiuntivi per rendere più ricco l'editor di codice, rinnovando l'interfaccia utente e risolvendo i problemi di accessibilità e prestazioni descritti sopra.

  • L'intera base di codice per questo progetto è disponibile su GitHub.

Ecco la demo su Codesandbox:

Collegamenti e materiale

  • "I portali di Google Chrome: come gli iframe, ma migliori e peggiori", Daniel Brain
  • "Ottimizzazione delle prestazioni", documentazione React
  • “Manuale d'uso e guida di riferimento”, documentazione CodeMirror