Construire un éditeur de code Web

Publié: 2022-03-10
Résumé rapide ↬ Si vous êtes un développeur qui envisage de créer une plate-forme nécessitant un éditeur de code sous une forme ou une autre, cet article est pour vous. Cet article explique comment créer un éditeur de code web qui affiche le résultat en temps réel à l'aide de HTML, CSS et JavaScript.

Un éditeur de code Web en ligne est particulièrement utile lorsque vous n'avez pas la possibilité d'utiliser une application d'édition de code ou lorsque vous souhaitez essayer rapidement quelque chose sur le Web avec votre ordinateur ou même votre téléphone portable. Il s'agit également d'un projet intéressant sur lequel travailler, car savoir comment créer un éditeur de code vous donnera des idées sur la manière d'aborder d'autres projets nécessitant l'intégration d'un éditeur de code pour afficher certaines fonctionnalités.

Voici quelques concepts React que vous devrez connaître pour suivre cet article :

  • Crochets,
  • Structuration des composants,
  • Composants fonctionnels,
  • Accessoires.

Utilisation de CodeMirror

Nous utiliserons une bibliothèque nommée CodeMirror pour construire notre éditeur. CodeMirror est un éditeur de texte polyvalent implémenté en JavaScript pour le navigateur. Il est spécialement conçu pour l'édition de code et est livré avec un certain nombre de modes de langage et de modules complémentaires pour des fonctionnalités d'édition plus avancées.

Une API de programmation riche et un système de thématisation CSS sont disponibles pour personnaliser CodeMirror en fonction de votre application et l'étendre avec de nouvelles fonctionnalités. Il nous donne la fonctionnalité de créer un éditeur de code riche qui s'exécute sur le Web et nous montre le résultat de notre code en temps réel.

Dans la section suivante, nous allons configurer notre nouveau projet React et installer les bibliothèques dont nous avons besoin pour créer notre application Web.

Créer un nouveau projet React

Commençons par créer un nouveau projet React. Dans votre interface de ligne de commande, accédez au répertoire dans lequel vous souhaitez créer votre projet, et créons une application React et nommons-la code_editor :

 npx create-react-app code_editor

Après avoir créé notre nouvelle application React, naviguons vers le répertoire de ce projet dans l'interface de ligne de commande :

 cd code_editor

Il y a deux bibliothèques que nous devons installer ici : codemirror et react-codemirror2 .

 npm install codemirror react-codemirror2

Après avoir installé les bibliothèques dont nous avons besoin pour ce projet, créons nos onglets et activons le changement d'onglet entre les trois onglets qui apparaîtront dans notre éditeur (pour HTML, CSS et JavaScript).

Plus après saut! Continuez à lire ci-dessous ↓

Composant de bouton

Au lieu de créer des boutons individuels, faisons du bouton un composant réutilisable. Dans notre projet, le bouton aurait trois instances, selon les trois onglets dont nous avons besoin.

Créez un dossier nommé components dans le dossier src . Dans ce nouveau dossier de components , créez un fichier JSX nommé Button.jsx .

Voici tout le code nécessaire dans le composant 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

Voici une explication complète de ce que nous avons fait ci-dessus :

  • Nous avons créé un composant fonctionnel nommé Button , que nous avons ensuite exporté.
  • Nous avons déstructuré title et onClick des props entrant dans le composant. Ici, le title serait une chaîne de texte et onClick serait une fonction appelée lorsqu'un bouton est cliqué.
  • Ensuite, nous avons utilisé l'élément button pour déclarer notre bouton et utilisé les attributs de style pour styliser notre bouton afin qu'il soit présentable.
  • Nous avons ajouté l'attribut onClick et lui avons transmis nos accessoires de fonction onClick déstructurés.
  • La dernière chose que vous remarquerez que nous avons faite dans ce composant est de passer {title} comme contenu de la balise du button . Cela nous permet d'afficher le titre de manière dynamique, en fonction de l'accessoire passé à l'instance du composant de bouton lorsqu'il est appelé.

Maintenant que nous avons créé un composant de bouton réutilisable, passons à autre chose et intégrons notre composant dans App.js. Accédez à App.js et importez le composant de bouton nouvellement créé :

 import Button from './components/Button';

Pour suivre quel onglet ou éditeur est ouvert, nous avons besoin d'un état de déclaration pour contenir la valeur de l'éditeur qui est ouvert. À l'aide du crochet useState React, nous allons configurer l'état qui stockera le nom de l'onglet de l'éditeur actuellement ouvert lorsque le bouton de cet onglet est cliqué.

Voici comment nous procédons :

 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;

Ici, nous avons déclaré notre état. Il prend le nom de l'éditeur actuellement ouvert. Étant donné que la valeur html est transmise comme valeur par défaut de l'état, l'éditeur HTML serait l'onglet ouvert par défaut.

Passons à autre chose et écrivons la fonction qui utilisera setOpenedEditor pour modifier la valeur de l'état lorsqu'un bouton de tabulation est cliqué.

Remarque : deux onglets peuvent ne pas être ouverts en même temps, nous devrons donc en tenir compte lors de l'écriture de notre fonction.

Voici à quoi ressemble notre fonction, nommée 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;

Ici, nous avons passé un seul argument de fonction, qui est le nom de l'onglet actuellement sélectionné. Cet argument serait fourni partout où la fonction est appelée, et le nom correspondant de cet onglet serait transmis.

Créons trois instances de notre Button pour les trois onglets dont nous avons besoin :

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

Voici ce que nous avons fait :

  • Nous avons commencé par ajouter une balise p , simplement pour donner un peu de contexte à notre application.
  • Nous avons utilisé une balise div pour envelopper nos boutons de tabulation. La balise div porte un nom de className que nous utiliserons pour styliser les boutons dans un affichage de grille dans le fichier CSS plus tard dans ce didacticiel.
  • Ensuite, nous avons déclaré trois instances du composant Button . Si vous vous en souvenez, le composant Button prend deux props, title et onClick . Dans chaque instance du composant Button , ces deux accessoires sont fournis.
  • L'accessoire de title prend le titre de l'onglet.
  • La prop onClick prend une fonction, onTabClick , que nous venons de créer et qui prend un seul argument : le nom de l'onglet sélectionné.

En fonction de l'onglet actuellement sélectionné, nous utiliserions l'opérateur ternaire JavaScript pour afficher l'onglet de manière conditionnelle. Cela signifie que si la valeur de l'état openedEditor est définie sur html (c'est-à-dire setOpenedEditor('html') ), alors l'onglet de la section HTML deviendrait l'onglet actuellement visible. Vous comprendrez mieux cela car nous le faisons ci-dessous:

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

Passons en revue le code ci-dessus en langage clair. Si la valeur de openedEditor est html , affichez la section HTML. Sinon, si la valeur de openedEditor est css , alors affichez la section CSS. Sinon, si la valeur n'est ni html ni css , cela signifie que la valeur doit être js , car nous n'avons que trois valeurs possibles pour l'état openedEditor ; alors, nous afficherions l'onglet pour JavaScript.

Nous avons utilisé des balises de paragraphe ( p ) pour les différentes sections dans les conditions de l'opérateur ternaire. Au fur et à mesure, nous allons créer les composants de l'éditeur et remplacer les balises p par les composants de l'éditeur eux-mêmes.

Nous sommes déjà arrivés si loin ! Lorsqu'un bouton est cliqué, il déclenche l'action qui définit l'onglet qu'il représente sur true , rendant cet onglet visible. Voici à quoi ressemble actuellement notre application :

Un GIF montrant la bascule d'onglet que nous avons actuellement.
Un GIF montrant la bascule d'onglet que nous avons actuellement. ( Grand aperçu )

Ajoutons un peu de CSS au conteneur div contenant les boutons. Nous voulons que les boutons soient affichés dans une grille, au lieu d'être empilés verticalement comme dans l'image ci-dessus. Accédez à votre fichier App.css et ajoutez le code suivant :

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

Rappelez-vous que nous avons ajouté className="tab-button-container" comme attribut dans la balise div contenant les boutons à trois onglets. Ici, nous avons stylisé ce conteneur, en utilisant CSS pour définir son affichage sur flex . Voici le résultat :

Nous utilisons CSS pour définir son affichage sur flex
( Grand aperçu )

Soyez fier de tout ce que vous avez fait pour en arriver là. Dans la section suivante, nous allons créer nos éditeurs en remplaçant les balises p par eux.

Création des éditeurs

Comme nous avons déjà installé les bibliothèques sur lesquelles nous allons travailler dans notre éditeur CodeMirror, allons-y et créons notre fichier Editor.jsx dans le dossier des components .

composants > Editor.jsx

Après avoir créé notre nouveau fichier, écrivons-y un code initial :

 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

Voici ce que nous avons fait :

  • Nous avons importé React avec le hook useState car nous allons en avoir besoin.
  • Nous avons importé le fichier CSS CodeMirror (qui provient de la bibliothèque CodeMirror que nous avons installée, vous n'avez donc pas à l'installer de manière particulière).
  • Nous avons importé Controlled de react-codemirror2 , en le renommant ControlledEditorComponent pour le rendre plus clair. Nous allons l'utiliser sous peu.
  • Ensuite, nous avons déclaré notre composant fonctionnel Editor , et nous avons une instruction return avec un div vide, avec un className dans l'instruction return pour l'instant.

Dans notre composant fonctionnel, nous avons déstructuré certaines valeurs des accessoires, notamment language , value et setEditorState . Ces trois accessoires seraient fournis dans n'importe quelle instance de l'éditeur lorsqu'il est appelé dans App.js .

Utilisons ControlledEditorComponent pour écrire le code de notre éditeur. Voici ce que nous allons faire :

 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

Passons en revue ce que nous avons fait ici, en expliquant certains termes de CodeMirror.

Les modes CodeMirror spécifient le langage auquel un éditeur est destiné. Nous avons importé trois modes car nous avons trois éditeurs pour ce projet :

  1. XML : ce mode est destiné au HTML. Il utilise le terme XML.
  2. JavaScript : Ceci ( codemirror/mode/javascript/javascript ) introduit le mode JavaScript.
  3. CSS : Ceci ( codemirror/mode/css/css ) amène en mode CSS.

Remarque : L'éditeur étant conçu comme un composant réutilisable, nous ne pouvons pas mettre de mode direct dans l'éditeur. Donc, nous fournissons le mode à travers le prop de language que nous avons déstructuré. Mais cela ne change rien au fait que les modes doivent être importés pour fonctionner.

Ensuite, discutons des choses dans ControlledEditorComponent :

  • onBeforeChange
    Ceci est appelé chaque fois que vous écrivez ou supprimez de l'éditeur. Considérez cela comme le gestionnaire onChange que vous auriez normalement dans un champ de saisie pour suivre les modifications. En utilisant cela, nous pourrons obtenir la valeur de notre éditeur chaque fois qu'il y aura un nouveau changement et l'enregistrer dans l'état de notre éditeur. Nous écrirons la fonction {handleChange} au fur et à mesure.
  • value = {value}
    C'est juste le contenu de l'éditeur à un moment donné. Nous avons passé une prop déstructurée nommée value à cet attribut. La value props est l'état contenant la valeur de cet éditeur. Cela serait fourni à partir de l'instance de l'éditeur.
  • className ="code-mirror-wrapper"
    Ce nom de classe n'est pas un style que nous fabriquons nous-mêmes. Il est fourni à partir du fichier CSS de CodeMirror, que nous avons importé ci-dessus.
  • options
    C'est un objet qui prend les différentes fonctionnalités que nous voulons que notre éditeur ait. Il existe de nombreuses options étonnantes dans CodeMirror. Regardons ceux que nous avons utilisés ici :
    • lineWrapping: true
      Cela signifie que le code doit passer à la ligne suivante lorsque la ligne est pleine.
    • lint: true
      Cela permet le peluchage.
    • mode: language
      Ce mode, comme discuté ci-dessus, prend la langue pour laquelle l'éditeur va être utilisé. La langue a déjà été importée ci-dessus, mais l'éditeur va appliquer une langue basée sur la valeur de language fournie à l'éditeur via la prop.
    • lineNumbers: true
      Cela spécifie que l'éditeur doit avoir des numéros de ligne pour chaque ligne.

Ensuite, nous pouvons écrire la fonction handleChange pour le gestionnaire onBeforeChange :

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

Le gestionnaire onBeforeChange nous donne accès à trois éléments : editor, data, value .

Nous avons seulement besoin de la value car c'est ce que nous voulons transmettre dans notre accessoire setEditorState . Le prop setEditorState représente la valeur définie pour chaque état que nous avons déclaré dans App.js , contenant la valeur pour chaque éditeur. Au fur et à mesure que nous avançons, nous verrons comment le transmettre comme accessoire au composant Editor .

Ensuite, nous ajouterons une liste déroulante qui nous permettra de sélectionner différents thèmes pour l'éditeur. Alors, regardons les thèmes dans CodeMirror.

Thèmes CodeMirror

CodeMirror propose plusieurs thèmes parmi lesquels nous pouvons sélectionner. Visitez le site officiel pour voir des démos des différents thèmes disponibles. Créons une liste déroulante avec différents thèmes parmi lesquels l'utilisateur peut choisir dans notre éditeur. Pour ce didacticiel, nous ajouterons cinq thèmes, mais vous pouvez en ajouter autant que vous le souhaitez.

Tout d'abord, importons nos thèmes dans le composant 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';

Ensuite, créez un tableau de tous les thèmes que nous avons importés :

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

Déclarons un hook useState pour contenir la valeur du thème sélectionné et définissons le thème par défaut comme dracula :

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

Créons la liste déroulante :

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

Dans le code ci-dessus, nous avons utilisé la balise HTML label pour ajouter une étiquette à notre liste déroulante, puis ajouté la balise HTML select pour créer notre liste déroulante. La balise option dans l'élément select définit les options disponibles dans la liste déroulante.

Parce que nous devions remplir la liste déroulante avec les noms de thèmes dans le themeArray que nous avons créé, nous avons utilisé la méthode de tableau .map pour mapper themeArray et afficher les noms individuellement à l'aide de la balise option .

Attendez - nous n'avons pas fini d'expliquer le code ci-dessus. Dans la balise de select d'ouverture, nous avons passé l'attribut onChange pour suivre et mettre à jour l'état du theme chaque fois qu'une nouvelle valeur est sélectionnée dans la liste déroulante. Chaque fois qu'une nouvelle option est sélectionnée dans la liste déroulante, la valeur est obtenue à partir de l'objet qui nous est renvoyé. Ensuite, nous utilisons le setTheme de notre crochet d'état pour définir la nouvelle valeur comme étant la valeur que l'état contient.

À ce stade, nous avons créé notre liste déroulante, configuré l'état de notre thème et écrit notre fonction pour définir l'état avec la nouvelle valeur. La dernière chose que nous devons faire pour que CodeMirror utilise notre thème est de passer le thème à l'objet options dans ControlledEditorComponent . Dans l'objet options , ajoutons une valeur nommée theme , et définissons sa valeur sur la valeur de l'état pour le thème sélectionné, également nommé theme .

Voici à quoi ressemblerait ControlledEditorComponent :

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

Maintenant, nous avons créé une liste déroulante de différents thèmes pouvant être sélectionnés dans l'éditeur.

Voici à quoi ressemble le code complet dans Editor.js pour le 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

Il n'y a qu'un seul nom de className que nous devons styliser. Accédez à App.css et ajoutez le style suivant :

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

Maintenant que nos éditeurs sont prêts, revenons à App.js et utilisons-les là-bas.

src > App.js

La première chose que nous devons faire est d'importer le composant Editor.js ici :

 import Editor from './components/Editor';

Dans App.js , déclarons les états qui contiendront respectivement le contenu des éditeurs HTML, CSS et JavaScript.

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

Si vous vous en souvenez, nous aurons besoin d'utiliser ces états pour conserver et fournir le contenu de nos éditeurs.

Ensuite, remplaçons les balises de paragraphe ( p ) que nous avons utilisées pour le HTML, le CSS et le JavaScript dans les rendus conditionnels par les composants de l'éditeur que nous venons de créer, et nous transmettrons également l'accessoire approprié à chaque instance de l'éditeur composant:

 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;

Si vous avez suivi jusqu'à présent, vous comprendrez ce que nous avons fait dans le bloc de code ci-dessus.

Le voici en langage clair : nous avons remplacé les balises p (qui étaient là en tant qu'espaces réservés) par des instances des composants de l'éditeur. Ensuite, nous avons fourni leurs props language , value et setEditorState , respectivement, pour correspondre à leurs états correspondants.

Nous sommes venus si loin ! Voici à quoi ressemble notre application maintenant :

À quoi ressemble notre application maintenant
( Grand aperçu )

Présentation des Iframes

Nous utiliserons des cadres en ligne (iframes) pour afficher le résultat du code saisi dans l'éditeur.

Selon MDN :

L'élément HTML Inline Frame ( <iframe> ) représente un contexte de navigation imbriqué, incorporant une autre page HTML dans la page actuelle.

Comment fonctionnent les iframes dans React

Les iframes sont normalement utilisés avec du HTML brut. L'utilisation d'Iframes avec React ne nécessite pas beaucoup de changements, le principal étant de convertir les noms d'attributs en camelcase. Un exemple de ceci est que srcdoc deviendrait srcDoc .

L'avenir des iframes sur le Web

Les iframes continuent d'être très utiles dans le développement Web. Quelque chose que vous voudrez peut-être vérifier est les portails. Comme l'explique Daniel Brain :

« Les portails introduisent un nouvel ensemble puissant de fonctionnalités dans ce mélange. Il est désormais possible de créer quelque chose qui ressemble à un iframe, qui peut animer et transformer de manière transparente et prendre en charge la totalité de la fenêtre du navigateur.

L'une des choses que Portals essaie de résoudre est le problème de la barre d'URL. Lors de l'utilisation d'iframe, les composants rendus dans l'iframe ne portent pas d'URL unique dans la barre d'adresse ; en tant que tel, cela pourrait ne pas être génial pour l'expérience utilisateur, selon le cas d'utilisation. Les portails valent la peine d'être vérifiés, et je vous suggère de le faire, mais comme ce n'est pas l'objet de notre article, c'est tout ce que j'en dirai ici.

Créer l'iframe pour héberger notre résultat

Continuons avec notre tutoriel en créant une iframe pour héberger le résultat de nos éditeurs.

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

Ici, nous avons créé l'iframe et l'avons logé dans une balise de conteneur div . Dans l'iframe, nous avons passé certains attributs dont nous avons besoin :

  • srcDoc
    L'attribut srcDoc est écrit en camelcase car c'est ainsi que l'on écrit les attributs iframe dans React. Lors de l'utilisation d'un iframe, nous pouvons soit intégrer une page Web externe sur la page, soit afficher le contenu HTML spécifié. Pour charger et intégrer une page externe, nous utiliserions plutôt la propriété src . Dans notre cas, nous ne chargeons pas une page externe ; nous voulons plutôt créer un nouveau document HTML interne qui héberge notre résultat ; pour cela, nous avons besoin de l'attribut srcDoc . Cet attribut prend le document HTML que nous voulons intégrer (nous ne l'avons pas encore créé, mais nous le ferons bientôt).
  • title
    L'attribut title est utilisé pour décrire le contenu du cadre en ligne.
  • sandbox
    Cette propriété a plusieurs objectifs. Dans notre cas, nous l'utilisons pour permettre aux scripts de s'exécuter dans notre iframe avec la valeur allow-scripts . Parce que nous travaillons avec un éditeur JavaScript, cela serait utile rapidement.
  • frameBorder
    Cela définit simplement l'épaisseur de la bordure de l'iframe.
  • width et height
    Ceci définit la largeur et la hauteur de l'iframe.

Ces termes devraient maintenant avoir plus de sens pour vous. Passons à autre chose et déclarons l'état qui contiendra le modèle de document HTML pour srcDoc . Si vous regardez attentivement le bloc de code ci-dessus, vous verrez que nous avons passé une valeur à l'attribut srcDoc : srcDoc ={srcDoc} . Utilisons notre useState() React pour déclarer l'état srcDoc . Pour cela, dans le fichier App.js , allez là où nous avons défini les autres états et ajoutez celui-ci :

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

Maintenant que nous avons créé l'état, la prochaine chose à faire est d'afficher le résultat dans l'état chaque fois que nous tapons dans l'éditeur de code. Mais ce que nous ne voulons pas, c'est restituer le composant à chaque pression de touche. Dans cet esprit, continuons.

Configuration de l'iframe pour afficher le résultat

Chaque fois qu'il y a un changement dans l'un des éditeurs pour le HTML, le CSS et le JavaScript, respectivement, nous voulons que useEffect() soit déclenché, et cela rendra le résultat mis à jour dans l'iframe. useEffect() pour ce faire dans le fichier App.js :

Tout d'abord, importez le useEffect() :

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

useEffect() comme ceci :

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

Ici, nous avons écrit un crochet useEffect() qui s'exécutera toujours chaque fois que les états de valeur que nous avons déclarés pour les éditeurs HTML, CSS et JavaScript sont modifiés ou mis à jour.

Pourquoi avons-nous besoin d'utiliser setTimeout() ? Eh bien, si nous écrivions ceci sans cela, alors chaque fois qu'une seule pression de touche est faite dans un éditeur, notre iframe serait mis à jour, et ce n'est pas bon pour les performances en général. Nous utilisons donc setTimeout() pour retarder la mise à jour de 250 millisecondes, ce qui nous laisse suffisamment de temps pour savoir si l'utilisateur est toujours en train de taper. Autrement dit, chaque fois que l'utilisateur appuie sur une touche, le décompte redémarre, de sorte que l'iframe ne sera mis à jour que lorsque l'utilisateur aura été inactif (sans taper) pendant 250 millisecondes. C'est un moyen sympa d'éviter d'avoir à mettre à jour l'iframe à chaque fois qu'une touche est enfoncée.

La prochaine chose que nous avons faite ci-dessus a été de mettre à jour srcDoc avec les nouvelles modifications. Le composant srcDoc , comme nous l'avons expliqué ci-dessus, restitue le contenu HTML spécifié dans l'iframe. Dans notre code, nous avons passé un modèle HTML, en prenant l'état html qui contient le code que l'utilisateur a tapé dans l'éditeur HTML et en le plaçant entre les balises body de notre modèle. Nous avons également pris l'état css qui contient les styles que l'utilisateur a tapé dans l'éditeur CSS, et nous l'avons passé entre les balises de style . Enfin, nous avons pris l'état js qui contient le code JavaScript que l'utilisateur a tapé dans l'éditeur JavaScript, et nous l'avons passé entre les balises script .

Notez qu'en définissant setSrcDoc , nous avons utilisé des backticks ( ` ` ) au lieu de guillemets normaux ( ' ' ). En effet, les backticks nous permettent de transmettre les valeurs d'état correspondantes, comme nous l'avons fait dans le code ci-dessus.

L'instruction return dans le useEffect() est une fonction de nettoyage qui efface setTimeout() lorsqu'elle est terminée, pour éviter les fuites de mémoire. La documentation contient plus d'informations sur useEffect .

Voici à quoi ressemble notre projet pour le moment :

A quoi ressemble notre projet en ce moment
( Grand aperçu )

Modules complémentaires CodeMirror

Avec les modules complémentaires CodeMirror, nous pouvons améliorer notre éditeur avec davantage de fonctionnalités que nous trouverions dans d'autres éditeurs de code. Passons en revue un exemple de balises de fermeture ajoutées automatiquement lorsqu'une balise d'ouverture est saisie, et un autre exemple de crochet se fermant automatiquement lorsque le crochet d'ouverture est saisi :

La première chose à faire est d'importer l'addon pour cela dans notre fichier App.js :

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

Passons-le dans les options ControlledEditorComponent :

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

Voici maintenant ce que nous avons :

A quoi ressemble notre projet
( Grand aperçu )

Vous pouvez ajouter une tonne de ces addons à votre éditeur pour lui donner des fonctionnalités plus riches. Nous ne pourrions pas les parcourir tous ici.

Maintenant que nous en avons terminé, discutons brièvement des choses que nous pourrions faire pour améliorer l'accessibilité et les performances de notre application.

Performances et Accessibilité de la Solution

En regardant notre éditeur de code Web, certaines choses pourraient certainement être améliorées.

Parce que nous avons prêté attention principalement à la fonctionnalité, nous avons peut-être un peu négligé le design. Pour une meilleure accessibilité, voici certaines choses que vous pourriez faire pour améliorer cette solution :

  1. Vous pouvez définir une classe active sur le bouton de l'éditeur actuellement ouvert. La mise en surbrillance du bouton améliorerait l'accessibilité en donnant aux utilisateurs une indication claire de l'éditeur sur lequel ils travaillent actuellement.
  2. Vous voudrez peut-être que l'éditeur occupe plus d'espace à l'écran que ce que nous avons ici. Une autre chose que vous pouvez essayer est de faire apparaître l'iframe en cliquant sur un bouton ancré quelque part sur le côté. Cela donnerait à l'éditeur plus d'espace à l'écran.
  3. Ce type d'éditeur serait utile pour les personnes qui souhaitent exécuter un exercice rapide sur leur appareil mobile, il serait donc nécessaire de l'adapter complètement au mobile (sans parler des deux points ci-dessus concernant le mobile).
  4. Actuellement, nous sommes en mesure de changer le thème du composant éditeur parmi les multiples thèmes que nous avons chargés, mais le thème général de la page reste le même. Vous pouvez permettre à l'utilisateur de basculer entre un thème sombre et clair pour l'ensemble de la mise en page. Ce serait bon pour l'accessibilité, soulageant la fatigue oculaire des gens qui regardent un écran lumineux trop longtemps.
  5. Nous n'avons pas examiné les problèmes de sécurité avec notre iframe, principalement parce que nous chargeions un document HTML interne dans l'iframe, plutôt qu'un document externe. Nous n'avons donc pas besoin d'y réfléchir trop attentivement, car les iframes conviennent parfaitement à notre cas d'utilisation.
  6. Avec les iframes, une autre considération serait le temps de chargement de la page, car le contenu chargé dans l'iframe serait normalement hors de votre contrôle. Dans notre application, ce n'est pas un problème car notre contenu iframe n'est pas externe.

Les performances et l'accessibilité valent la peine d'être prises en compte lorsque vous créez une application, car elles détermineront l'utilité et l'utilité de votre application pour ses utilisateurs.

Shedrack a fait du bon travail en expliquant les méthodes d'amélioration et d'optimisation des performances dans les applications React. Cela vaut la peine de vérifier!

Conclusion

Travailler à travers différents projets nous aide à apprendre sur un large éventail de sujets. Maintenant que vous avez parcouru cet article, n'hésitez pas à développer votre expérience en expérimentant d'autres modules complémentaires pour enrichir l'éditeur de code, réorganiser l'interface utilisateur et résoudre les problèmes d'accessibilité et de performances décrits ci-dessus.

  • L'intégralité de la base de code de ce projet est disponible sur GitHub.

Voici la démo sur Codesandbox :

Liens et matériel

  • "Les portails de Google Chrome : comme les iframes, mais en mieux et en pire", Daniel Brain
  • "Optimisation des performances", documentation React
  • "Manuel de l'utilisateur et guide de référence", documentation CodeMirror