Erstellen eines Webcode-Editors
Veröffentlicht: 2022-03-10Ein Online-Webcode-Editor ist am nützlichsten, wenn Sie keine Möglichkeit haben, eine Code-Editor-Anwendung zu verwenden, oder wenn Sie mit Ihrem Computer oder sogar Ihrem Mobiltelefon schnell etwas im Web ausprobieren möchten. Dies ist auch ein interessantes Projekt, an dem Sie arbeiten können, denn wenn Sie wissen, wie man einen Code-Editor erstellt, erhalten Sie Ideen, wie Sie andere Projekte angehen können, bei denen Sie einen Code-Editor integrieren müssen, um einige Funktionen zu zeigen.
Hier sind ein paar React-Konzepte, die Sie kennen müssen, um diesem Artikel folgen zu können:
- Haken,
- Komponentenstruktur,
- Funktionskomponenten,
- Requisiten.
Verwenden von CodeMirror
Wir werden eine Bibliothek namens CodeMirror verwenden, um unseren Editor zu erstellen. CodeMirror ist ein vielseitiger Texteditor, der in JavaScript für den Browser implementiert ist. Es ist speziell zum Bearbeiten von Code gedacht und wird mit einer Reihe von Sprachmodi und Add-Ons für erweiterte Bearbeitungsfunktionen geliefert.
Eine reichhaltige Programmier-API und ein CSS-Designsystem stehen zur Verfügung, um CodeMirror an Ihre Anwendung anzupassen und um neue Funktionen zu erweitern. Es gibt uns die Funktionalität, einen reichhaltigen Code-Editor zu erstellen, der im Web läuft und uns das Ergebnis unseres Codes in Echtzeit anzeigt.
Im nächsten Abschnitt werden wir unser neues React-Projekt einrichten und die Bibliotheken installieren, die wir zum Erstellen unserer Web-App benötigen.
Erstellen eines neuen React-Projekts
Beginnen wir mit der Erstellung eines neuen React-Projekts. Navigieren Sie in Ihrer Befehlszeilenschnittstelle zu dem Verzeichnis, in dem Sie Ihr Projekt erstellen möchten, und erstellen Sie eine React-Anwendung und nennen Sie sie code_editor
:
npx create-react-app code_editor
Nachdem wir unsere neue React-Anwendung erstellt haben, navigieren wir in der Befehlszeilenschnittstelle zum Verzeichnis dieses Projekts:
cd code_editor
Es gibt zwei Bibliotheken, die wir hier installieren müssen: codemirror
und react-codemirror2
.
npm install codemirror react-codemirror2
Nachdem wir die Bibliotheken installiert haben, die wir für dieses Projekt benötigen, erstellen wir unsere Registerkarten und aktivieren die Registerkartenumschaltung zwischen den drei Registerkarten, die in unserem Editor angezeigt werden (für HTML, CSS und JavaScript).
Schaltflächenkomponente
Anstatt einzelne Schaltflächen zu erstellen, machen wir die Schaltfläche zu einer Komponente, die wiederverwendbar ist. In unserem Projekt hätte die Schaltfläche drei Instanzen, entsprechend den drei Registerkarten, die wir benötigen.
Erstellen Sie im Ordner src
einen Ordner namens components
. Erstellen Sie in diesem neuen components
eine JSX-Datei namens Button.jsx
.
Hier ist der gesamte Code, der in der Button
-Komponente benötigt wird:
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
Hier ist eine vollständige Erklärung dessen, was wir oben getan haben:
- Wir haben eine funktionale Komponente namens
Button
erstellt, die wir dann exportiert haben. - Wir haben
title
undonClick
von den Requisiten, die in die Komponente kommen, destrukturiert. Hier wäretitle
eine Textzeichenfolge undonClick
eine Funktion, die aufgerufen wird, wenn auf eine Schaltfläche geklickt wird. - Als Nächstes haben wir das
button
-Element verwendet, um unsere Schaltfläche zu deklarieren, und diestyle
Attribute verwendet, um unsere Schaltfläche so zu gestalten, dass sie präsentabel aussieht. - Wir haben das
onClick
Attribut hinzugefügt und unsere destrukturiertenonClick
Funktionsprops daran übergeben. - Als letztes werden Sie feststellen, dass wir in dieser Komponente
{title}
als Inhalt desbutton
-Tags übergeben haben. Dadurch können wir den Titel dynamisch anzeigen, basierend darauf, welche Prop an die Instanz der Schaltflächenkomponente übergeben wird, wenn sie aufgerufen wird.
Nachdem wir nun eine wiederverwendbare Schaltflächenkomponente erstellt haben, fahren wir fort und bringen unsere Komponente in App.js.
Gehen Sie zu App.js
und importieren Sie die neu erstellte Schaltflächenkomponente:
import Button from './components/Button';
Um nachzuverfolgen, welche Registerkarte oder welcher Editor geöffnet ist, benötigen wir einen Deklarationsstatus, der den Wert des geöffneten Editors enthält. Mit dem Hook useState
React richten wir den Zustand ein, der den Namen des Editor-Tabs speichert, der gerade geöffnet ist, wenn auf die Schaltfläche dieses Tabs geklickt wird.
So machen wir das:
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;
Hier erklärten wir unseren Staat. Es nimmt den Namen des aktuell geöffneten Editors an. Da der Wert html
als Standardwert des Bundesstaates übergeben wird, wäre der HTML-Editor die Registerkarte standardmäßig geöffnet.
Lassen Sie uns weitermachen und die Funktion schreiben, die setOpenedEditor
verwendet, um den Wert des Zustands zu ändern, wenn auf eine Tabulatorschaltfläche geklickt wird.
Hinweis: Zwei Registerkarten dürfen nicht gleichzeitig geöffnet sein, daher müssen wir dies beim Schreiben unserer Funktion berücksichtigen.
So sieht unsere Funktion mit dem Namen onTabClick
aus:
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;
Hier haben wir ein einzelnes Funktionsargument übergeben, das der Name der aktuell ausgewählten Registerkarte ist. Dieses Argument würde überall dort bereitgestellt, wo die Funktion aufgerufen wird, und der relevante Name dieser Registerkarte würde übergeben.
Lassen Sie uns drei Instanzen unseres Button
für die drei benötigten Registerkarten erstellen:
<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>
Folgendes haben wir getan:
- Wir begannen mit dem Hinzufügen eines
p
-Tags, im Grunde genommen nur, um dem, worum es in unserer Anwendung geht, einen Kontext zu geben. - Wir haben ein
div
-Tag verwendet, um unsere Tab-Schaltflächen zu umschließen. Dasdiv
-Tag trägt einenclassName
, den wir später in diesem Tutorial verwenden werden, um die Schaltflächen in einer Rasteranzeige in der CSS-Datei zu formatieren. - Als Nächstes haben wir drei Instanzen der
Button
Komponente deklariert. Wenn Sie sich erinnern, nimmt dieButton
-Komponente zwei Requisiten,title
undonClick
. In jeder Instanz derButton
Komponente werden diese beiden Requisiten bereitgestellt. - Die
title
übernimmt den Titel des Tabs. - Die
onClick
Prop übernimmt eine Funktion,onTabClick
, die wir gerade erstellt haben und die ein einziges Argument akzeptiert: den Namen der ausgewählten Registerkarte.
Basierend auf der aktuell ausgewählten Registerkarte würden wir den ternären JavaScript-Operator verwenden, um die Registerkarte bedingt anzuzeigen. Das bedeutet, dass, wenn der Wert des Status „ openedEditor
“ auf „ html
“ gesetzt ist (dh setOpenedEditor('html')
), die Registerkarte für den HTML-Abschnitt zur aktuell sichtbaren Registerkarte wird. Sie werden dies besser verstehen, wenn wir es unten tun:
... 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> ); ...
Lassen Sie uns den obigen Code im Klartext durchgehen. Wenn der Wert von openedEditor
html
ist, dann zeige den HTML-Abschnitt an. Wenn andernfalls der Wert von openedEditor
css
ist, dann zeigen Sie den CSS-Abschnitt an. Andernfalls, wenn der Wert weder html
noch css
ist, bedeutet dies, dass der Wert js
sein muss, da wir nur drei mögliche Werte für den Zustand openedEditor
haben; Also, dann würden wir die Registerkarte für JavaScript anzeigen.
Wir haben Absatz-Tags ( p
) für die verschiedenen Abschnitte in den ternären Operatorbedingungen verwendet. Im weiteren Verlauf erstellen wir die Editor-Komponenten und ersetzen die p
-Tags durch die Editor-Komponenten selbst.
Wir sind schon so weit gekommen! Wenn auf eine Schaltfläche geklickt wird, wird die Aktion ausgelöst, die die Registerkarte, die sie darstellt, auf true
setzt und diese Registerkarte sichtbar macht. So sieht unsere App derzeit aus:
Fügen wir dem div
-Container, der die Schaltflächen enthält, ein wenig CSS hinzu. Wir möchten, dass die Schaltflächen in einem Raster angezeigt werden, anstatt wie im Bild oben vertikal gestapelt. Gehen Sie zu Ihrer App.css
-Datei und fügen Sie den folgenden Code hinzu:
.tab-button-container{ display: flex; }
Denken Sie daran, dass wir className="tab-button-container"
als Attribut im div
-Tag hinzugefügt haben, das die Schaltflächen mit drei Registerkarten enthält. Hier haben wir diesen Container gestaltet, indem wir CSS verwendet haben, um seine Anzeige auf flex
zu setzen. Das ist das Ergebnis:
Seien Sie stolz darauf, wie viel Sie getan haben, um an diesen Punkt zu gelangen. Im nächsten Abschnitt werden wir unsere Editoren erstellen und die p
-Tags durch sie ersetzen.
Erstellen der Editoren
Da wir die Bibliotheken, an denen wir arbeiten werden, bereits in unserem CodeMirror-Editor installiert haben, lassen Sie uns fortfahren und unsere Editor.jsx
-Datei im components
erstellen.
Komponenten > Editor.jsx
Nachdem wir unsere neue Datei erstellt haben, schreiben wir einen anfänglichen Code hinein:
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
Folgendes haben wir getan:
- Wir haben React zusammen mit dem
useState
Hook importiert, weil wir es brauchen werden. - Wir haben die CodeMirror-CSS-Datei importiert (die aus der von uns installierten CodeMirror-Bibliothek stammt, sodass Sie sie nicht auf besondere Weise installieren müssen).
- Wir haben „
Controlled
“ ausreact-codemirror2
importiert und es in „ControlledEditorComponent
“ umbenannt, um es klarer zu machen. Wir werden dies in Kürze verwenden. - Dann haben wir unsere
Editor
-Funktionskomponente deklariert, und wir haben eine Rückgabeanweisung mit einem leerendiv
und vorerst einemclassName
in der Rückgabeanweisung.
In unserer funktionalen Komponente haben wir einige Werte aus den Requisiten destrukturiert, darunter language
, value
und setEditorState
. Diese drei Props würden in jeder Instanz des Editors bereitgestellt, wenn er in App.js
.
Lassen Sie uns ControlledEditorComponent
verwenden, um den Code für unseren Editor zu schreiben. Folgendes werden wir tun:
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
Lassen Sie uns durchgehen, was wir hier getan haben, und einige CodeMirror-Begriffe erklären.
Die CodeMirror-Modi geben an, für welche Sprache ein Editor gedacht ist. Wir haben drei Modi importiert, weil wir drei Editoren für dieses Projekt haben:
- XML: Dieser Modus ist für HTML. Es verwendet den Begriff XML.
- JavaScript: Dies (
codemirror/mode/javascript/javascript
) bringt den JavaScript-Modus. - CSS: Dies (
codemirror/mode/css/css
) bringt den CSS-Modus ein.
Hinweis: Da der Editor als wiederverwendbare Komponente aufgebaut ist, können wir keinen direkten Modus in den Editor einfügen. Also liefern wir den Modus durch die language
, die wir destrukturiert haben. Das ändert aber nichts daran, dass die Modi importiert werden müssen, damit sie funktionieren.
Als nächstes besprechen wir die Dinge in ControlledEditorComponent
:
-
onBeforeChange
Dies wird immer dann aufgerufen, wenn Sie in den Editor schreiben oder daraus entfernen. Stellen Sie sich das wie denonChange
Handler vor, den Sie normalerweise in einem Eingabefeld haben würden, um Änderungen zu verfolgen. Auf diese Weise können wir den Wert unseres Editors jederzeit abrufen, wenn es eine neue Änderung gibt, und sie im Zustand unseres Editors speichern. Wir werden die Funktion{handleChange}
schreiben, während wir fortfahren. -
value = {value}
Dies ist jeweils nur der Inhalt des Editors. Wir haben diesem Attribut eine destrukturierte Eigenschaft namensvalue
übergeben. Dievalue
sind der Zustand, der den Wert dieses Editors enthält. Dies würde von der Instanz des Editors geliefert werden. -
className
="code-mirror-wrapper"
Dieser Klassenname ist kein Stil, den wir uns selbst machen. Es wird aus der CSS-Datei von CodeMirror bereitgestellt, die wir oben importiert haben. -
options
Dies ist ein Objekt, das die verschiedenen Funktionen übernimmt, die unser Editor haben soll. Es gibt viele erstaunliche Optionen in CodeMirror. Schauen wir uns die an, die wir hier verwendet haben:-
lineWrapping: true
Das bedeutet, dass der Code in die nächste Zeile umbrechen sollte, wenn die Zeile voll ist. -
lint: true
Dies ermöglicht ein Fusseln. -
mode: language
Dieser Modus nimmt, wie oben besprochen, die Sprache an, für die der Editor verwendet werden soll. Die Sprache wurde oben bereits importiert, aber der Editor wird eine Sprache anwenden, die auf demlanguage
basiert, der dem Editor über das Prop geliefert wird. -
lineNumbers: true
Dies gibt an, dass der Editor Zeilennummern für jede Zeile haben sollte.
-
Als nächstes können wir die handleChange
Funktion für den onBeforeChange
Handler schreiben:
const handleChange = (editor, data, value) => { setEditorState(value); }
Der Handler onBeforeChange
gibt uns Zugriff auf drei Dinge: editor, data, value
.
Wir brauchen den value
nur, weil wir ihn in unserer setEditorState
Prop übergeben möchten. Die setEditorState
stellt den festgelegten Wert für jeden Zustand dar, den wir in App.js
deklariert haben, und enthält den Wert für jeden Editor. Im weiteren Verlauf werden wir uns ansehen, wie dies als Stütze an die Editor
Komponente übergeben werden kann.
Als Nächstes fügen wir ein Dropdown-Menü hinzu, mit dem wir verschiedene Themen für den Editor auswählen können. Schauen wir uns also die Themen in CodeMirror an.
CodeMirror-Designs
CodeMirror hat mehrere Themen, aus denen wir auswählen können. Besuchen Sie die offizielle Website, um Demos der verschiedenen verfügbaren Themen zu sehen. Lassen Sie uns ein Dropdown-Menü mit verschiedenen Themen erstellen, aus denen der Benutzer in unserem Editor auswählen kann. Für dieses Tutorial fügen wir fünf Themen hinzu, aber Sie können so viele hinzufügen, wie Sie möchten.
Importieren wir zunächst unsere Designs in die Komponente 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';
Erstellen Sie als Nächstes ein Array aller von uns importierten Themen:
const themeArray = ['dracula', 'material', 'mdn-like', 'the-matrix', 'night']
Lassen Sie uns einen useState
-Hook deklarieren, der den Wert des ausgewählten Themas enthält, und das Standardthema als dracula
festlegen:
const [theme, setTheme] = useState("dracula")
Lassen Sie uns das Dropdown erstellen:
... 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> ) ...
Im obigen Code haben wir das label
-HTML-Tag verwendet, um ein Label zu unserer Dropdown-Liste hinzuzufügen, und dann das select
-HTML-Tag hinzugefügt, um unsere Dropdown-Liste zu erstellen. Das option
-Tag im select
-Element definiert die in der Dropdown-Liste verfügbaren Optionen.
Da wir das Dropdown-Menü mit den Themennamen in dem von uns erstellten themeArray
füllen mussten, verwendeten wir die .map
Array-Methode, um themeArray
und die Namen einzeln mit dem option
-Tag anzuzeigen.
Warten Sie – wir sind noch nicht fertig mit der Erklärung des obigen Codes. Im öffnenden select
-Tag haben wir das onChange
Attribut übergeben, um den Designstatus zu verfolgen und zu aktualisieren, wenn ein neuer Wert in der theme
-Liste ausgewählt wird. Immer wenn eine neue Option in der Dropdown-Liste ausgewählt wird, wird der Wert aus dem an uns zurückgegebenen Objekt abgerufen. Als nächstes verwenden wir das setTheme
von unserem Status-Hook, um den neuen Wert auf den Wert festzulegen, den der Status enthält.
An diesem Punkt haben wir unsere Dropdown-Liste erstellt, den Status unseres Themas eingerichtet und unsere Funktion geschrieben, um den Status mit dem neuen Wert festzulegen. Das Letzte, was wir tun müssen, damit CodeMirror unser Design verwendet, ist, das Design an das options
in ControlledEditorComponent
zu übergeben. Lassen Sie uns im options
einen Wert namens theme
hinzufügen und seinen Wert auf den Wert des Zustands für das ausgewählte Design setzen, das auch theme
heißt.
So würde ControlledEditorComponent
jetzt aussehen:
<ControlledEditorComponent onBeforeChange={handleChange} value= {value} className="code-mirror-wrapper" options={{ lineWrapping: true, lint: true, mode: language, lineNumbers: true, theme: theme, }} />
Jetzt haben wir ein Dropdown-Menü mit verschiedenen Themen erstellt, die im Editor ausgewählt werden können.
So sieht der vollständige Code in Editor.js
im Moment aus:
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
Es gibt nur einen className
, den wir formatieren müssen. Gehen Sie zu App.css
und fügen Sie den folgenden Stil hinzu:
.editor-container{ padding-top: 0.4%; }
Nachdem unsere Editoren nun bereit sind, gehen wir zurück zu App.js
und verwenden sie dort.
src > App.js
Als erstes müssen wir die Editor.js
Komponente hier importieren:
import Editor from './components/Editor';
Lassen Sie uns in App.js
die Zustände deklarieren, die die Inhalte der HTML-, CSS- bzw. JavaScript-Editoren enthalten.
const [html, setHtml] = useState(''); const [css, setCss] = useState(''); const [js, setJs] = useState('');
Wenn Sie sich erinnern, müssen wir diese Zustände verwenden, um die Inhalte unserer Redakteure zu speichern und bereitzustellen.
Als Nächstes ersetzen wir die Absatz-Tags ( p
), die wir für HTML, CSS und JavaScript in den bedingten Renderings verwendet haben, durch die soeben erstellten Editor-Komponenten, und wir übergeben auch die entsprechende Prop an jede Instanz des Editors Komponente:
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;
Wenn Sie bis jetzt mitverfolgt haben, werden Sie verstehen, was wir im obigen Codeblock getan haben.
Hier ist es im Klartext: Wir haben die p
-Tags (die als Platzhalter da waren) durch Instanzen der Editor-Komponenten ersetzt. Dann haben wir ihre language
- , value
- und setEditorState
, um sie mit ihren entsprechenden Zuständen abzugleichen.
Wir sind so weit gekommen! So sieht unsere App jetzt aus:
Einführung in Iframes
Wir verwenden Inline-Frames (Iframes), um das Ergebnis des im Editor eingegebenen Codes anzuzeigen.
Laut MDN:
Das HTML-Inline-Frame-Element (
<iframe>
) stellt einen verschachtelten Browsing-Kontext dar, der eine andere HTML-Seite in die aktuelle einbettet.
Funktionsweise von Iframes in React
Iframes werden normalerweise mit einfachem HTML verwendet. Die Verwendung von Iframes mit React erfordert nicht viele Änderungen, die wichtigste besteht darin, Attributnamen in Camelcase umzuwandeln. Ein Beispiel dafür wäre, dass srcdoc
zu srcDoc
werden würde.
Die Zukunft von Iframes im Web
Iframes sind weiterhin sehr nützlich in der Webentwicklung. Etwas, das Sie vielleicht ausprobieren möchten, ist Portals. Wie Daniel Brain erklärt:
„Portale bringen eine leistungsstarke Reihe neuer Funktionen in diese Mischung ein. Jetzt ist es möglich, etwas zu bauen, das sich wie ein iFrame anfühlt, das sich nahtlos animieren und morphen lässt und das gesamte Browserfenster einnimmt.“
Eines der Dinge, die Portals zu lösen versucht, ist das Problem mit der URL-Leiste. Bei der Verwendung von iframe tragen im iframe gerenderte Komponenten keine eindeutige URL in der Adressleiste; Daher ist dies je nach Anwendungsfall möglicherweise nicht gut für die Benutzererfahrung. Portale sind einen Besuch wert, und ich würde vorschlagen, dass Sie das tun, aber da es nicht der Schwerpunkt unseres Artikels ist, werde ich hier nur das sagen.
Erstellen des Iframes zur Unterbringung unseres Ergebnisses
Fahren wir mit unserem Tutorial fort, indem wir einen Iframe erstellen, um das Ergebnis unserer Editoren aufzunehmen.
return ( <div className="App"> // ... <div> <iframe srcDoc={srcDoc} title="output" sandbox="allow-scripts" frameBorder="1" width="100%" height="100%" /> </div> </div> );
Hier haben wir den iframe erstellt und in einem div
-Container-Tag untergebracht. Im Iframe haben wir einige Attribute übergeben, die wir brauchen:
-
srcDoc
Das AttributsrcDoc
ist in Camelcase geschrieben, da so Iframe-Attribute in React geschrieben werden. Bei der Verwendung eines Iframes können wir entweder eine externe Webseite auf der Seite einbetten oder bestimmte HTML-Inhalte rendern. Um eine externe Seite zu laden und einzubetten, würden wir stattdessen die Eigenschaftsrc
verwenden. In unserem Fall laden wir keine externe Seite; Vielmehr möchten wir ein neues internes HTML-Dokument erstellen, das unser Ergebnis enthält. Dazu benötigen wir das AttributsrcDoc
. Dieses Attribut nimmt das HTML-Dokument, das wir einbetten möchten (das haben wir noch nicht erstellt, aber wir werden es bald tun). -
title
Das title-Attribut wird verwendet, um den Inhalt des Inline-Frames zu beschreiben. -
sandbox
Diese Eigenschaft hat viele Zwecke. In unserem Fall verwenden wir es, um zuzulassen, dass Skripts in unserem Iframe mit dem Wertallow-scripts
ausgeführt werden. Da wir mit einem JavaScript-Editor arbeiten, wäre dies schnell praktisch. -
frameBorder
Dies definiert lediglich die Rahmenstärke des Iframes. -
width
undheight
Dies definiert die Breite und Höhe des Iframes.
Diese Begriffe sollten jetzt für Sie sinnvoller sein. Lassen Sie uns weitermachen und den Zustand deklarieren, der das HTML-Vorlagendokument für srcDoc
. Wenn Sie sich den Codeblock oben genau ansehen, sehen Sie, dass wir einen Wert an das Attribut srcDoc
haben: srcDoc
={srcDoc}
. Lassen Sie uns unseren useState()
React-Hook verwenden, um den srcDoc
-Zustand zu deklarieren. Gehen Sie dazu in der Datei App.js
dorthin, wo wir die anderen Zustände definiert haben, und fügen Sie diesen hinzu:
const [srcDoc, setSrcDoc] = useState(` `);
Nachdem wir den Status erstellt haben, müssen Sie als Nächstes das Ergebnis im Status anzeigen, wenn wir den Code-Editor eingeben. Aber was wir nicht wollen, ist, die Komponente bei jedem einzelnen Tastendruck neu zu rendern. Lassen Sie uns in diesem Sinne fortfahren.
Iframe konfigurieren, um das Ergebnis anzuzeigen
Jedes Mal, wenn es eine Änderung in einem der Editoren für HTML, CSS bzw. JavaScript gibt, möchten wir, dass useEffect()
ausgelöst wird und das aktualisierte Ergebnis im Iframe rendert. Schreiben wir dazu useEffect()
in die Datei App.js
:
Importieren Sie zuerst den Hook useEffect()
:
import React, { useState, useEffect } from 'react';
Schreiben useEffect()
so:
useEffect(() => { const timeOut = setTimeout(() => { setSrcDoc( ` <html> <body>${html}</body> <style>${css}</style> <script>${js}</script> </html> ` ) }, 250); return () => clearTimeout(timeOut) }, [html, css, js])
Hier haben wir einen useEffect()
Hook geschrieben, der immer dann ausgeführt wird, wenn die Wertzustände, die wir für die HTML-, CSS- und JavaScript-Editoren deklariert haben, geändert oder aktualisiert werden.
Warum mussten wir setTimeout()
verwenden? Nun, wenn wir dies ohne es geschrieben hätten, dann würde jedes Mal, wenn ein einzelner Tastendruck in einem Editor erfolgt, unser Iframe aktualisiert werden, und das ist im Allgemeinen nicht gut für die Leistung. Daher verwenden wir setTimeout()
, um die Aktualisierung um 250 Millisekunden zu verzögern, sodass wir genügend Zeit haben, um zu wissen, ob der Benutzer noch tippt. Das heißt, jedes Mal, wenn der Benutzer eine Taste drückt, wird die Zählung neu gestartet, sodass der Iframe nur aktualisiert wird, wenn der Benutzer 250 Millisekunden lang inaktiv war (nicht tippt). Dies ist eine coole Methode, um zu vermeiden, dass der Iframe jedes Mal aktualisiert werden muss, wenn eine Taste gedrückt wird.
Als nächstes haben wir oben srcDoc
mit den neuen Änderungen aktualisiert. Die srcDoc
Komponente rendert, wie oben erklärt, den angegebenen HTML-Inhalt im Iframe. In unserem Code haben wir eine HTML-Vorlage übergeben, wobei wir den html
-Status genommen haben, der den Code enthält, den der Benutzer in den HTML-Editor eingegeben hat, und ihn zwischen den body
-Tags unserer Vorlage platziert haben. Wir haben auch den css
-Zustand genommen, der die Stile enthält, die der Benutzer in den CSS-Editor eingegeben hat, und diesen zwischen den style
Tags übergeben. Schließlich haben wir den js
-Zustand genommen, der den JavaScript-Code enthält, den der Benutzer in den JavaScript-Editor eingegeben hat, und wir haben ihn zwischen den script
-Tags übergeben.
Beachten Sie, dass wir beim Setzen setSrcDoc
Backticks ( ` `
) anstelle von normalen Anführungszeichen ( ' '
) verwendet haben. Dies liegt daran, dass Backticks es uns ermöglichen, entsprechende Statuswerte zu übergeben, wie wir es im obigen Code getan haben.
Die return
-Anweisung im useEffect()
Hook ist eine Bereinigungsfunktion, die setTimeout()
löscht, wenn sie abgeschlossen ist, um Speicherverluste zu vermeiden. Die Dokumentation hat mehr über useEffect
.
So sieht unser Projekt im Moment aus:
CodeMirror-Add-Ons
Mit CodeMirror-Addons können wir unseren Editor mit mehr Funktionen erweitern, die wir in anderen Code-Editoren finden würden. Lassen Sie uns ein Beispiel für schließende Tags durchgehen, die automatisch hinzugefügt werden, wenn ein öffnendes Tag eingegeben wird, und ein weiteres Beispiel für eine Klammer, die automatisch geschlossen wird, wenn die öffnende Klammer eingegeben wird:
Als erstes müssen Sie das Addon dafür in unsere App.js
-Datei importieren:
import 'codemirror/addon/edit/closetag'; import 'codemirror/addon/edit/closebrackets';
Übergeben wir es in den ControlledEditorComponent
-Optionen:
<ControlledEditorComponent ... options={{ ... autoCloseTags: true, autoCloseBrackets: true, }} />
Hier ist, was wir haben:
Sie könnten Ihrem Editor eine Menge dieser Addons hinzufügen, um ihm umfangreichere Funktionen zu verleihen. Wir können unmöglich alle hier durchgehen.
Nachdem wir damit fertig sind, lassen Sie uns kurz besprechen, was wir tun könnten, um die Zugänglichkeit und Leistung unserer App zu verbessern.
Leistung und Zugänglichkeit der Lösung
Wenn man sich unseren Webcode-Editor ansieht, könnten einige Dinge definitiv verbessert werden.
Da wir in erster Linie auf die Funktionalität geachtet haben, haben wir das Design vielleicht etwas vernachlässigt. Für eine bessere Zugänglichkeit sind hier einige Dinge, die Sie tun könnten, um diese Lösung zu verbessern:
- Sie könnten eine
active
Klasse auf die Schaltfläche für den aktuell geöffneten Editor setzen. Das Hervorheben der Schaltfläche würde die Zugänglichkeit verbessern, indem den Benutzern klar angezeigt wird, an welchem Editor sie gerade arbeiten. - Vielleicht möchten Sie, dass der Editor mehr Platz auf dem Bildschirm einnimmt als wir hier haben. Eine andere Sache, die Sie versuchen könnten, ist, den Iframe mit einem Klick auf eine Schaltfläche, die irgendwo an der Seite angedockt ist, zu öffnen. Dies würde dem Editor mehr Platz auf dem Bildschirm verschaffen.
- Diese Art von Editor wäre nützlich für Leute, die eine schnelle Übung auf ihrem Mobilgerät ausführen möchten, daher wäre eine vollständige Anpassung an Mobilgeräte erforderlich (ganz zu schweigen von den beiden obigen Punkten zu Mobilgeräten).
- Derzeit sind wir in der Lage, das Design der Editor-Komponente zwischen den verschiedenen Designs zu wechseln, die wir geladen haben, aber das allgemeine Design der Seite bleibt gleich. Sie könnten dem Benutzer ermöglichen, für das gesamte Layout zwischen einem dunklen und einem hellen Design zu wechseln. Dies wäre gut für die Zugänglichkeit und entlastet die Augen der Menschen, wenn sie zu lange auf einen hellen Bildschirm schauen.
- Wir haben uns bei unserem Iframe nicht mit Sicherheitsproblemen befasst, hauptsächlich weil wir ein internes HTML-Dokument in den Iframe geladen haben und nicht ein externes Dokument. Wir müssen dies also nicht zu sorgfältig prüfen, da Iframes für unseren Anwendungsfall gut geeignet sind.
- Bei Iframes wäre eine weitere Überlegung die Seitenladezeit, da der Inhalt, der in den Iframe geladen wird, normalerweise außerhalb Ihrer Kontrolle liegt. In unserer App ist dies kein Problem, da unser Iframe-Inhalt nicht extern ist.
Leistung und Zugänglichkeit sind beim Erstellen einer Anwendung eine große Überlegung wert, da sie bestimmen, wie nützlich und benutzerfreundlich Ihre Anwendung für ihre Benutzer ist.
Shedrack hat gute Arbeit geleistet, Methoden zur Verbesserung und Optimierung der Leistung in React-Apps zu erklären. Reinschauen lohnt sich!
Fazit
Die Arbeit an verschiedenen Projekten hilft uns, eine breite Palette von Themen kennenzulernen. Nachdem Sie diesen Artikel durchgearbeitet haben, können Sie Ihre Erfahrungen erweitern, indem Sie mit weiteren Add-Ons experimentieren, um den Code-Editor reichhaltiger zu machen, die Benutzeroberfläche zu überarbeiten und die oben beschriebenen Zugänglichkeits- und Leistungsprobleme zu beheben.
- Die gesamte Codebasis für dieses Projekt ist auf GitHub verfügbar.
Hier ist die Demo auf Codesandbox:
Links und Materialien
- „Die Portale von Google Chrome: Wie Iframes, aber besser und schlechter“, Daniel Brain
- „Leistungsoptimierung“, React-Dokumentation
- „Benutzerhandbuch und Referenzhandbuch“, CodeMirror-Dokumentation