Construirea unui editor de coduri web

Publicat: 2022-03-10
Rezumat rapid ↬ Dacă ești un dezvoltator care se gândește să construiască o platformă care necesită un editor de cod într-o formă sau alta, atunci acest articol este pentru tine. Acest articol explică cum să creați un editor de cod web care afișează rezultatul în timp real cu ajutorul unor coduri HTML, CSS și JavaScript.

Un editor de coduri web online este cel mai util atunci când nu aveți posibilitatea de a utiliza o aplicație de editare de coduri sau când doriți să încercați rapid ceva pe web cu computerul sau chiar cu telefonul mobil. Acesta este, de asemenea, un proiect interesant la care să lucrați, deoarece cunoștințele despre cum să construiți un editor de cod vă va oferi idei despre cum să abordați alte proiecte care necesită să integrați un editor de cod pentru a arăta anumite funcționalități.

Iată câteva concepte React pe care va trebui să le cunoașteți pentru a le urma în acest articol:

  • cârlige,
  • Structura componentelor,
  • Componente funcționale,
  • Recuzită.

Folosind CodeMirror

Vom folosi o bibliotecă numită CodeMirror pentru a construi editorul nostru. CodeMirror este un editor de text versatil implementat în JavaScript pentru browser. Este special pentru editarea codului și vine cu o serie de moduri de limbă și suplimente pentru o funcționalitate de editare mai avansată.

Un API de programare bogat și un sistem de tematică CSS sunt disponibile pentru personalizarea CodeMirror pentru a se potrivi aplicației dvs. și pentru a o extinde cu noi funcționalități. Ne oferă funcționalitatea de a crea un editor de cod bogat care rulează pe web și ne arată rezultatul codului nostru în timp real.

În secțiunea următoare, vom configura noul nostru proiect React și vom instala bibliotecile de care avem nevoie pentru a ne construi aplicația web.

Crearea unui nou proiect React

Să începem prin a crea un nou proiect React. În interfața de linie de comandă, navigați la directorul în care doriți să vă creați proiectul și să creăm o aplicație React și să o code_editor :

 npx create-react-app code_editor

După ce am creat noua noastră aplicație React, să navigăm la directorul acelui proiect în interfața de linie de comandă:

 cd code_editor

Există două biblioteci pe care trebuie să le instalăm aici: codemirror și react-codemirror2 .

 npm install codemirror react-codemirror2

După ce au instalat bibliotecile de care avem nevoie pentru acest proiect, să ne creăm filele și să activăm comutarea între cele trei file care vor apărea în editorul nostru (pentru HTML, CSS și JavaScript).

Mai multe după săritură! Continuați să citiți mai jos ↓

Componenta butonului

În loc să creăm butoane individuale, să facem din buton o componentă reutilizabilă. În proiectul nostru, butonul ar avea trei instanțe, conform celor trei file de care avem nevoie.

Creați un folder numit components în folderul src . În acest folder nou de components , creați un fișier JSX numit Button.jsx .

Iată tot codul necesar în componenta 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

Iată o explicație completă a ceea ce am făcut mai sus:

  • Am creat o componentă funcțională numită Button , pe care apoi am exportat-o.
  • Am destructurat title și onClick din elementele de recuzită care vin în componentă. Aici, title ar fi un șir de text, iar onClick ar fi o funcție care este apelată atunci când se face clic pe un buton.
  • Apoi, am folosit elementul button pentru a ne declara butonul și am folosit atributele de style pentru a modela butonul pentru a arăta prezentabil.
  • Am adăugat atributul onClick și i-am transmis elementele de recuzită ale funcției onClick destructurate.
  • Ultimul lucru pe care îl veți observa pe care l-am făcut în această componentă este să treceți în {title} ca conținut al etichetei button . Acest lucru ne permite să afișăm titlul în mod dinamic, în funcție de ce prop este transmis instanței componentei butonului atunci când este apelată.

Acum că am creat o componentă de buton reutilizabilă, să mergem mai departe și să aducem componenta noastră în App.js. Accesați App.js și importați componenta de buton nou creată:

 import Button from './components/Button';

Pentru a urmări ce filă sau editor este deschis, avem nevoie de o stare de declarare pentru a păstra valoarea editorului care este deschis. Folosind cârligul useState React, vom configura starea care va stoca numele filei editor care este deschisă în prezent când se face clic pe butonul acelei file.

Iată cum facem asta:

 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;

Aici ne-am declarat statul. Ia numele editorului care este deschis în prezent. Deoarece valoarea html este transmisă ca valoare implicită a statului, editorul HTML ar fi fila deschisă în mod implicit.

Să trecem mai departe și să scriem funcția care va folosi setOpenedEditor pentru a schimba valoarea stării atunci când se face clic pe butonul de tab.

Notă: Este posibil ca două file să nu fie deschise în același timp, așa că va trebui să luăm în considerare acest lucru atunci când scriem funcția noastră.

Iată cum arată funcția noastră, numită 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;

Aici, am trecut un singur argument de funcție, care este numele filei selectate în prezent. Acest argument va fi furnizat oriunde este apelată funcția și va fi transmis numele relevant al acelei file.

Să creăm trei instanțe ale Button nostru pentru cele trei file de care avem nevoie:

 <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>

Iată ce am făcut:

  • Am început prin a adăuga o etichetă p , practic doar pentru a da un context la ceea ce este aplicația noastră.
  • Am folosit o etichetă div pentru a încheia butoanele de filă. Eticheta div poartă un className pe care îl vom folosi pentru a modela butoanele într-un afișaj cu grilă în fișierul CSS mai târziu în acest tutorial.
  • Apoi, am declarat trei instanțe ale componentei Button . Dacă vă amintiți, componenta Button are două elemente de recuzită, title și onClick . În fiecare instanță a componentei Button , aceste două elemente de recuzită sunt furnizate.
  • Propul title ia titlul filei.
  • Propul onClick preia o funcție, onTabClick , pe care tocmai am creat-o și care ia un singur argument: numele filei selectate.

Pe baza filei selectate în prezent, vom folosi operatorul ternar JavaScript pentru a afișa fila în mod condiționat. Aceasta înseamnă că dacă valoarea stării openedEditor este setată la html (adică setOpenedEditor('html') ), atunci fila pentru secțiunea HTML va deveni fila vizibilă în prezent. Veți înțelege mai bine acest lucru așa cum o facem mai jos:

 ... 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> ); ...

Să trecem peste codul de mai sus în limba engleză simplă. Dacă valoarea openedEditor este html , atunci afișați secțiunea HTML. În caz contrar, dacă valoarea openedEditor este css , atunci afișați secțiunea CSS. În caz contrar, dacă valoarea nu este nici html , nici css , atunci aceasta înseamnă că valoarea trebuie să fie js , deoarece avem doar trei valori posibile pentru starea openedEditor ; deci, atunci am afișa fila pentru JavaScript.

Am folosit etichete de paragraf ( p ) pentru diferitele secțiuni în condițiile operatorului ternar. Pe măsură ce continuăm, vom crea componentele editorului și vom înlocui etichetele p cu componentele editorului în sine.

Am ajuns deja atât de departe! Când se face clic pe un buton, acesta declanșează acțiunea care setează fila pe care o reprezintă la true , făcând acea filă vizibilă. Iată cum arată aplicația noastră în prezent:

Un GIF care arată comutatorul de filă pe care îl avem în prezent.
Un GIF care arată comutatorul de filă pe care îl avem în prezent. (Previzualizare mare)

Să adăugăm un mic CSS în containerul div care ține butoanele. Dorim ca butoanele să fie afișate într-o grilă, în loc să fie stivuite vertical ca în imaginea de mai sus. Accesați fișierul App.css și adăugați următorul cod:

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

Amintiți-vă că am adăugat className="tab-button-container" ca atribut în eticheta div care deține butoanele cu trei file. Aici, am stilat acel container, folosind CSS pentru a-și seta afișarea la flex . Acesta este rezultatul:

Folosim CSS pentru a-și seta afișarea la flex
(Previzualizare mare)

Fii mândru de cât de mult ai făcut pentru a ajunge în acest punct. În secțiunea următoare, vom crea editorii noștri, înlocuind etichetele p cu ele.

Crearea editorilor

Deoarece am instalat deja bibliotecile la care vom lucra în editorul nostru CodeMirror, haideți să creăm fișierul Editor.jsx în folderul components .

componente > Editor.jsx

După ce am creat noul nostru fișier, să scriem un cod inițial în el:

 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

Iată ce am făcut:

  • Am importat React împreună cu cârligul useState pentru că vom avea nevoie de el.
  • Am importat fișierul CodeMirror CSS (care provine din biblioteca CodeMirror pe care am instalat-o, astfel încât să nu fie nevoie să îl instalați în vreun mod special).
  • Am importat Controlled din react-codemirror2 , redenumindu-l în ControlledEditorComponent pentru a fi mai clar. Îl vom folosi în curând.
  • Apoi, am declarat componenta noastră funcțională Editor și avem o instrucțiune return cu un div gol, cu un className în instrucțiunea return pentru moment.

În componenta noastră funcțională, am destructurat unele valori din elemente de recuzită, inclusiv language , value și setEditorState . Aceste trei elemente de recuzită vor fi furnizate în orice instanță a editorului atunci când este apelată în App.js

Să folosim ControlledEditorComponent pentru a scrie codul pentru editorul nostru. Iată ce vom face:

 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

Să trecem prin ceea ce am făcut aici, explicând câțiva termeni CodeMirror.

Modurile CodeMirror specifică limba pentru care este destinat un editor. Am importat trei moduri deoarece avem trei editori pentru acest proiect:

  1. XML: acest mod este pentru HTML. Folosește termenul XML.
  2. JavaScript: Acest ( codemirror/mode/javascript/javascript ) aduce în modul JavaScript.
  3. CSS: Aceasta ( codemirror/mode/css/css ) aduce în modul CSS.

Notă: Deoarece editorul este construit ca o componentă care este reutilizabilă, nu putem pune un mod direct în editor. Deci, furnizăm modul prin suportul language pe care l-am destructurat. Dar acest lucru nu schimbă faptul că modurile trebuie importate pentru a funcționa.

În continuare, să discutăm lucrurile din ControlledEditorComponent :

  • onBeforeChange
    Aceasta se numește oricând scrieți sau eliminați din editor. Gândiți-vă la asta ca la manipulatorul onChange pe care l-ați avea în mod normal într-un câmp de intrare pentru a urmări modificările. Folosind aceasta, vom putea obține valoarea editorului nostru oricând apare o nouă modificare și o vom salva în starea editorului nostru. Vom scrie funcția {handleChange} pe măsură ce continuăm.
  • value = {value}
    Acesta este doar conținutul editorului la un moment dat. Am trecut o prop destructurată numită value acestui atribut. value props este starea care deține valoarea acelui editor. Aceasta ar fi furnizată de la instanța editorului.
  • className ="code-mirror-wrapper"
    Acest nume de clasă nu este un stil pe care îl facem noi înșine. Este furnizat din fișierul CSS al CodeMirror, pe care l-am importat mai sus.
  • options
    Acesta este un obiect care preia diferitele funcționalități pe care vrem să le aibă editorul nostru. Există multe opțiuni uimitoare în CodeMirror. Să ne uităm la cele pe care le-am folosit aici:
    • lineWrapping: true
      Aceasta înseamnă că codul ar trebui să se încadreze la următoarea linie atunci când linia este plină.
    • lint: true
      Acest lucru permite scame.
    • mode: language
      Acest mod, așa cum sa discutat mai sus, folosește limba pentru care va fi folosit editorul. Limba a fost deja importată mai sus, dar editorul va aplica o limbă bazată pe valoarea language furnizată editorului prin prop.
    • lineNumbers: true
      Aceasta specifică faptul că editorul ar trebui să aibă numere de linie pentru fiecare linie.

Apoi, putem scrie funcția handleChange pentru handlerul onBeforeChange :

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

onBeforeChange ne oferă acces la trei lucruri: editor, data, value .

Avem nevoie doar de value pentru că este ceea ce vrem să transmitem în prop setEditorState . setEditorState reprezintă valoarea setată pentru fiecare stare pe care am declarat-o în App.js , păstrând valoarea pentru fiecare editor. Pe măsură ce trecem mai departe, ne vom uita la cum să transmitem acest lucru ca recuzită la componenta Editor .

În continuare, vom adăuga un meniu derulant care ne permite să selectăm diferite teme pentru editor. Deci, să ne uităm la temele din CodeMirror.

Teme CodeMirror

CodeMirror are mai multe teme din care putem selecta. Vizitați site-ul oficial pentru a vedea demonstrații ale diferitelor teme disponibile. Să facem un dropdown cu diferite teme din care utilizatorul le poate alege în editorul nostru. Pentru acest tutorial, vom adăuga cinci teme, dar puteți adăuga câte doriți.

Mai întâi, să importam temele noastre în componenta 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';

Apoi, creați o serie cu toate temele pe care le-am importat:

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

Să declarăm un cârlig useState pentru a păstra valoarea temei selectate și să setăm tema implicită ca dracula :

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

Să creăm meniul derulant:

 ... 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> ) ...

În codul de mai sus, am folosit label HTML label pentru a adăuga o etichetă la meniul nostru drop-down, apoi am adăugat eticheta HTML select pentru a crea meniul nostru drop-down. Eticheta de option din elementul select definește opțiunile disponibile în meniul drop-down.

Deoarece trebuia să completăm meniul drop-down cu numele temelor în themeArray pe care l-am creat, am folosit metoda .map array pentru a mapa themeArray și pentru a afișa numele individual folosind eticheta option .

Stai, nu am terminat de explicat codul de mai sus. În eticheta de select de deschidere, am transmis atributul onChange pentru a urmări și actualiza starea theme ori de câte ori este selectată o nouă valoare în meniul drop-down. Ori de câte ori o nouă opțiune este selectată în meniul derulant, valoarea este obținută de la obiectul returnat nouă. Apoi, folosim setTheme din hook-ul nostru de stat pentru a seta noua valoare să fie valoarea pe care o deține starea.

În acest moment, am creat meniul drop-down, am configurat starea temei și am scris funcția noastră pentru a seta starea cu noua valoare. Ultimul lucru pe care trebuie să-l facem pentru ca CodeMirror să utilizeze tema noastră este să trecem tema obiectului options din ControlledEditorComponent . În obiectul options , să adăugăm o valoare numită theme și să setăm valoarea acesteia la valoarea stării pentru tema selectată, numită și theme .

Iată cum ar arăta ControlledEditorComponent acum:

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

Acum, am creat un dropdown cu diferite teme din care pot fi selectate în editor.

Iată cum arată codul complet din Editor.js în acest moment:

 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

Există un singur className pe care trebuie să-l stilăm. Accesați App.css și adăugați următorul stil:

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

Acum că editorii noștri sunt pregătiți, să revenim la App.js și să le folosim acolo.

src > App.js

Primul lucru pe care trebuie să-l facem este să importam componenta Editor.js aici:

 import Editor from './components/Editor';

În App.js , să declarăm stările care vor deține conținutul editorilor HTML, CSS și, respectiv, JavaScript.

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

Dacă vă amintiți, va trebui să folosim aceste stări pentru a păstra și furniza conținutul editorilor noștri.

În continuare, să înlocuim etichetele de paragraf ( p ) pe care le-am folosit pentru HTML, CSS și JavaScript în randările condiționate cu componentele editorului pe care tocmai le-am creat și vom transmite, de asemenea, suportul corespunzător fiecărei instanțe a editorului. componenta:

 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;

Dacă ați urmărit până acum, veți înțelege ce am făcut în blocul de cod de mai sus.

Aici este în limba engleză simplă: am înlocuit etichetele p (care erau acolo ca substituenți) cu instanțe ale componentelor editorului. Apoi, le-am furnizat language , value și, respectiv, setEditorState props, pentru a se potrivi cu stările corespunzătoare.

Am ajuns atât de departe! Iată cum arată aplicația noastră acum:

Cum arată aplicația noastră acum
(Previzualizare mare)

Introducere în Iframes

Vom folosi cadrele inline (iframes) pentru a afișa rezultatul codului introdus în editor.

Potrivit MDN:

Elementul HTML Inline Frame ( <iframe> ) reprezintă un context de navigare imbricat, încorporând o altă pagină HTML în cea curentă.

Cum funcționează Iframe-urile în React

Iframe-urile sunt utilizate în mod normal cu HTML simplu. Utilizarea Iframes cu React nu necesită multe modificări, cea mai importantă fiind convertirea numelor de atribute în camelcase. Un exemplu în acest sens este că srcdoc ar deveni srcDoc .

Viitorul Iframe-urilor pe web

Iframe-urile continuă să fie cu adevărat utile în dezvoltarea web. Ceva pe care ați putea dori să verificați este Portals. După cum explică Daniel Brain:

„Portalele introduc un nou set puternic de capabilități în acest mix. Acum este posibil să construiți ceva care să se simtă ca un iframe, care să poată anima și transforma fără probleme și să preia întreaga fereastră a browserului.”

Unul dintre lucrurile pe care Portals încearcă să le rezolve este problema barei URL. Când utilizați iframe, componentele redate în iframe nu poartă o adresă URL unică în bara de adrese; ca atare, acest lucru ar putea să nu fie grozav pentru experiența utilizatorului, în funcție de cazul de utilizare. Portalurile merită verificate și ți-aș sugera să faci asta, dar pentru că nu este punctul central al articolului nostru, asta este tot ce voi spune despre asta aici.

Crearea iframe-ului pentru a găzdui rezultatul nostru

Să mergem mai departe cu tutorialul nostru creând un iframe pentru a găzdui rezultatul editorilor noștri.

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

Aici, am creat iframe-ul și l-am găzduit într-o etichetă de container div . În iframe, am trecut câteva atribute de care avem nevoie:

  • srcDoc
    Atributul srcDoc este scris în camelcase, deoarece așa se scrie atributele iframe în React. Când folosim un iframe, putem fie încorpora o pagină web externă în pagină, fie reda conținutul HTML specificat. Pentru a încărca și încorpora o pagină externă, am folosi în schimb proprietatea src . În cazul nostru, nu încărcăm o pagină externă; mai degrabă, dorim să creăm un nou document HTML intern care să găzduiască rezultatul nostru; pentru aceasta, avem nevoie de atributul srcDoc . Acest atribut preia documentul HTML pe care vrem să-l încorporam (nu l-am creat încă, dar îl vom face în curând).
  • title
    Atributul title este folosit pentru a descrie conținutul cadrului inline.
  • sandbox
    Această proprietate are multe scopuri. În cazul nostru, îl folosim pentru a permite rularea scripturilor în cadrul nostru iframe cu valoarea allow-scripts . Deoarece lucrăm cu un editor JavaScript, acest lucru ar fi util rapid.
  • frameBorder
    Aceasta definește doar grosimea marginii cadrului iframe.
  • width si height
    Aceasta definește lățimea și înălțimea iframe-ului.

Acești termeni ar trebui să aibă acum mai mult sens pentru tine. Să mergem mai departe și să declarăm starea care va deține documentul șablon HTML pentru srcDoc . Dacă vă uitați cu atenție la blocul de cod de mai sus, veți vedea că am transmis o valoare atributului srcDoc : srcDoc ={srcDoc} . Să folosim cârligul nostru useState() React pentru a declara starea srcDoc . Pentru a face acest lucru, în fișierul App.js , mergeți acolo unde am definit celelalte stări și adăugați aceasta:

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

Acum că am creat starea, următorul lucru de făcut este să afișăm rezultatul în stare ori de câte ori introducem în editorul de cod. Dar ceea ce nu dorim este să redăm din nou componenta la fiecare apăsare de tastă. Având în vedere asta, să continuăm.

Configurarea Iframe pentru a afișa rezultatul

De fiecare dată când există o schimbare în oricare dintre editorii pentru HTML, CSS și, respectiv, JavaScript, dorim ca useEffect() să fie declanșat, iar acest lucru va afișa rezultatul actualizat în iframe. Să scriem useEffect() pentru a face acest lucru în fișierul App.js :

Mai întâi, importați cârligul useEffect() :

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

Să scriem useEffect() astfel:

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

Aici, am scris un cârlig useEffect() care va rula întotdeauna de fiecare dată când valoarea pe care am declarat-o pentru editorii HTML, CSS și JavaScript este modificată sau actualizată.

De ce a trebuit să folosim setTimeout() ? Ei bine, dacă am scris asta fără el, atunci de fiecare dată când se face o singură apăsare de tastă într-un editor, iframe-ul nostru ar fi actualizat, iar asta nu este grozav pentru performanță în general. Așa că folosim setTimeout() pentru a întârzia actualizarea cu 250 de milisecunde, oferindu-ne suficient timp pentru a ști dacă utilizatorul încă scrie. Adică, de fiecare dată când utilizatorul apasă o tastă, repornește contorizarea, astfel încât iframe-ul va fi actualizat doar atunci când utilizatorul a fost inactiv (nu a tastat) timp de 250 de milisecunde. Acesta este un mod minunat de a evita actualizarea iframe-ului de fiecare dată când este apăsată o tastă.

Următorul lucru pe care l-am făcut mai sus a fost să actualizăm srcDoc cu noile modificări. Componenta srcDoc , așa cum am explicat mai sus, redă conținutul HTML specificat în iframe. În codul nostru, am trecut un șablon HTML, luând starea html care conține codul pe care utilizatorul l-a tastat în editorul HTML și plasându-l între etichetele body ale șablonului nostru. Am luat și starea css care conține stilurile pe care utilizatorul le-a tastat în editorul CSS și am trecut-o între etichetele de style . În cele din urmă, am luat starea js care conține codul JavaScript pe care utilizatorul l-a tastat în editorul JavaScript și am trecut-o între etichetele de script .

Observați că la setarea setSrcDoc , am folosit backticks ( ` ` ) în loc de ghilimele normale ( ' ' ). Acest lucru se datorează faptului că backtick-urile ne permit să transmitem valorile de stare corespunzătoare, așa cum am făcut în codul de mai sus.

Declarația de return din useEffect() este o funcție de curățare care șterge setTimeout() când este complet, pentru a evita pierderea memoriei. Documentația conține mai multe despre useEffect .

Iată cum arată proiectul nostru în acest moment:

Cum arată proiectul nostru în acest moment
(Previzualizare mare)

Suplimente CodeMirror

Cu suplimentele CodeMirror, ne putem îmbunătăți editorul cu mai multe tipuri de funcționalități pe care le-am găsi în alte editoare de cod. Să parcurgem un exemplu de etichete de închidere care sunt adăugate automat atunci când este introdusă o etichetă de deschidere și un alt exemplu de paranteză care se închide automat când este introdus paranteza de deschidere:

Primul lucru de făcut este să importați suplimentul pentru aceasta în fișierul nostru App.js :

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

Să-l transmitem în opțiunile ControlledEditorComponent :

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

Acum iată ce avem:

Cum arată proiectul nostru
(Previzualizare mare)

Ai putea adăuga o mulțime de aceste suplimente la editorul tău pentru a-i oferi funcții mai bogate. Nu le-am putea parcurge pe toate aici.

Acum că am terminat cu asta, să discutăm pe scurt despre lucrurile pe care le-am putea face pentru a îmbunătăți accesibilitatea și performanța aplicației noastre.

Performanța și accesibilitatea soluției

Privind la editorul nostru de coduri web, unele lucruri ar putea fi cu siguranță îmbunătățite.

Deoarece am acordat atenție în primul rând funcționalității, este posibil să fi neglijat puțin designul. Pentru o mai bună accesibilitate, iată câteva lucruri pe care le puteți face pentru a îmbunătăți această soluție:

  1. Puteți seta o clasă active pe butonul pentru editorul deschis în prezent. Evidențierea butonului ar îmbunătăți accesibilitatea, oferind utilizatorilor o indicație clară asupra editorului la care lucrează în prezent.
  2. Poate doriți ca editorul să ocupe mai mult spațiu pe ecran decât ceea ce avem aici. Un alt lucru pe care l-ați putea încerca este să faceți iframe să apară cu un clic pe un buton care este andocat undeva în lateral. Procedând astfel, editorul ar oferi mai mult spațiu pe ecran.
  3. Acest tip de editor ar fi util pentru persoanele care doresc să execute un exercițiu rapid pe dispozitivul lor mobil, așa că ar fi necesară adaptarea completă la mobil (să nu mai vorbim de ambele puncte despre mobil de mai sus).
  4. În prezent, putem schimba tema componentei editor dintre temele multiple în care le-am încărcat, dar tema generală a paginii rămâne aceeași. Puteți permite utilizatorului să comute între o temă întunecată și cea deschisă pentru întregul aspect. Acest lucru ar fi bun pentru accesibilitate, ușurând presiunea asupra ochilor oamenilor de a privi un ecran luminos pentru prea mult timp.
  5. Nu ne-am uitat la problemele de securitate cu iframe-ul nostru, în principal pentru că încărcam un document HTML intern în iframe, mai degrabă decât un document extern. Deci nu trebuie să luăm în considerare acest lucru cu prea multă atenție, deoarece cadrele iframe sunt potrivite pentru cazul nostru de utilizare.
  6. Cu iframe, o altă considerație ar fi timpul de încărcare a paginii, deoarece conținutul care este încărcat în iframe ar fi în mod normal în afara controlului dumneavoastră. În aplicația noastră, aceasta nu este o problemă, deoarece conținutul nostru iframe nu este extern.

Performanța și accesibilitatea merită mult luate în considerare atunci când construiți orice aplicație, deoarece acestea vor determina cât de utilă și de utilizabilă este aplicația dvs. pentru utilizatorii săi.

Shedrack a făcut o treabă bună explicând metodele de îmbunătățire și optimizare a performanței în aplicațiile React. Merită verificat!

Concluzie

Lucrul prin diferite proiecte ne ajută să învățăm despre o gamă largă de subiecte. Acum că ați parcurs acest articol, nu ezitați să vă extindeți experiența experimentând cu mai multe suplimente pentru a îmbogăți editorul de cod, renovând interfața de utilizare și remediați problemele de accesibilitate și performanță prezentate mai sus.

  • Întreaga bază de cod pentru acest proiect este disponibilă pe GitHub.

Iată demonstrația pe Codesandbox:

Legături și materiale

  • „Portalele Google Chrome: ca Iframes, dar mai bune și mai rele”, Daniel Brain
  • „Optimizarea performanței”, documentația React
  • „Manual de utilizare și ghid de referință”, documentația CodeMirror