Construirea unui editor de coduri web
Publicat: 2022-03-10Un 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_editorDupă 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-codemirror2După 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).
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 ButtonIată 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șionClickdin elementele de recuzită care vin în componentă. Aici,titlear fi un șir de text, iaronClickar fi o funcție care este apelată atunci când se face clic pe un buton. - Apoi, am folosit elementul
buttonpentru a ne declara butonul și am folosit atributele destylepentru a modela butonul pentru a arăta prezentabil. - Am adăugat atributul
onClickși i-am transmis elementele de recuzită ale funcțieionClickdestructurate. - 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 eticheteibutton. 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ă
divpentru a încheia butoanele de filă. Etichetadivpoartă unclassNamepe 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, componentaButtonare două elemente de recuzită,titleșionClick. În fiecare instanță a componenteiButton, aceste două elemente de recuzită sunt furnizate. - Propul
titleia titlul filei. - Propul
onClickpreia 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:

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:

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 EditorIată ce am făcut:
- Am importat React împreună cu cârligul
useStatepentru 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
Controlleddinreact-codemirror2, redenumindu-l înControlledEditorComponentpentru 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 undivgol, cu unclassNameî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 EditorSă 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:
- XML: acest mod este pentru HTML. Folosește termenul XML.
- JavaScript: Acest (
codemirror/mode/javascript/javascript) aduce în modul JavaScript. - 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 manipulatorulonChangepe 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ăvalueacestui atribut.valueprops 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 valoarealanguagefurnizată 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:

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
AtributulsrcDoceste 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 proprietateasrc. Î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 atributulsrcDoc. 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 valoareaallow-scripts. Deoarece lucrăm cu un editor JavaScript, acest lucru ar fi util rapid. -
frameBorder
Aceasta definește doar grosimea marginii cadrului iframe. -
widthsiheight
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:

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:

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:
- Puteți seta o clasă
activepe 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. - 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.
- 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).
- Î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.
- 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.
- 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
