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_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).
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
șionClick
din elementele de recuzită care vin în componentă. Aici,title
ar fi un șir de text, iaronClick
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 destyle
pentru a modela butonul pentru a arăta prezentabil. - Am adăugat atributul
onClick
și i-am transmis elementele de recuzită ale funcțieionClick
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 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ă
div
pentru a încheia butoanele de filă. Etichetadiv
poartă unclassName
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, componentaButton
are două elemente de recuzită,title
șionClick
. În fiecare instanță a componenteiButton
, 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:
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 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
dinreact-codemirror2
, redenumindu-l înControlledEditorComponent
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 undiv
gol, 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 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:
- 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 manipulatorulonChange
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 valoarealanguage
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:
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
AtributulsrcDoc
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 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. -
width
siheight
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ă
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. - 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