構建 Web 代碼編輯器
已發表: 2022-03-10當您沒有機會使用代碼編輯器應用程序,或者當您想使用計算機甚至手機快速嘗試 Web 上的某些內容時,在線 Web 代碼編輯器最有用。 這也是一個有趣的項目,因為了解如何構建代碼編輯器將使您了解如何處理需要您集成代碼編輯器以顯示某些功能的其他項目。
為了在本文中繼續學習,您需要了解以下幾個 React 概念:
- 掛鉤,
- 組件結構,
- 功能組件,
- 道具。
使用代碼鏡像
我們將使用一個名為 CodeMirror 的庫來構建我們的編輯器。 CodeMirror 是一個用 JavaScript 為瀏覽器實現的通用文本編輯器。 它特別適用於編輯代碼,並帶有多種語言模式和附加組件,可實現更高級的編輯功能。
豐富的編程 API 和 CSS 主題系統可用於自定義 CodeMirror 以適應您的應用程序並使用新功能對其進行擴展。 它為我們提供了創建豐富的代碼編輯器的功能,該編輯器可以在 Web 上運行並實時向我們顯示代碼的結果。
在下一節中,我們將設置新的 React 項目並安裝構建 Web 應用程序所需的庫。
創建一個新的 React 項目
讓我們從創建一個新的 React 項目開始。 在命令行界面中,導航到要在其中創建項目的目錄,讓我們創建一個 React 應用程序並將其命名為code_editor
:
npx create-react-app code_editor
創建了新的 React 應用程序後,讓我們在命令行界面中導航到該項目的目錄:
cd code_editor
我們需要在這里安裝兩個庫: codemirror
和react-codemirror2
。
npm install codemirror react-codemirror2
在安裝了這個項目所需的庫之後,讓我們創建我們的選項卡並啟用將出現在我們的編輯器中的三個選項卡之間的選項卡切換(用於 HTML、CSS 和 JavaScript)。
按鈕組件
讓我們讓按鈕成為可重用的組件,而不是創建單獨的按鈕。 在我們的項目中,根據我們需要的三個選項卡,按鈕將具有三個實例。
在src
文件夾中創建一個名為components
的文件夾。 在這個新的components
文件夾中,創建一個名為Button.jsx
的 JSX 文件。
以下是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
以下是我們上面所做的完整解釋:
- 我們創建了一個名為
Button
的功能組件,然後將其導出。 - 我們從進入組件的 props 中解構了
title
和onClick
。 在這裡,title
將是一個文本字符串,onClick
將是一個在單擊按鈕時調用的函數。 - 接下來,我們使用
button
元素來聲明我們的按鈕,並使用style
屬性來設置按鈕的樣式,使其看起來像樣。 - 我們添加了
onClick
屬性並將我們解構的onClick
函數道具傳遞給它。 - 您會注意到我們在此組件中所做的最後一件事是將
{title}
作為button
標籤的內容傳遞。 這允許我們根據調用時傳遞給按鈕組件實例的道具動態顯示標題。
現在我們已經創建了一個可重用的按鈕組件,讓我們繼續並將我們的組件引入App.js.
轉到App.js
並導入新創建的按鈕組件:
import Button from './components/Button';
要跟踪打開的選項卡或編輯器,我們需要一個聲明狀態來保存打開的編輯器的值。 使用useState
React 鉤子,我們將設置狀態,該狀態將存儲單擊該選項卡按鈕時當前打開的編輯器選項卡的名稱。
以下是我們的做法:
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;
在這裡,我們聲明了我們的狀態。 它採用當前打開的編輯器的名稱。 因為值html
作為狀態的默認值傳遞,所以 HTML 編輯器將默認打開選項卡。
讓我們繼續編寫函數,該函數將使用setOpenedEditor
來更改單擊選項卡按鈕時的狀態值。
注意:兩個選項卡可能不會同時打開,所以我們在編寫函數時必須考慮到這一點。
這是我們名為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;
在這裡,我們傳遞了一個函數參數,它是當前選擇的選項卡的名稱。 該參數將在調用函數的任何位置提供,並且將傳入該選項卡的相關名稱。
讓我們為我們需要的三個選項卡創建Button
的三個實例:
<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>
這是我們所做的:
- 我們首先添加了一個
p
標籤,基本上只是為了給我們的應用程序的內容提供一些上下文。 - 我們使用
div
標籤來包裝我們的標籤按鈕。div
標籤帶有一個className
,我們將在本教程後面的 CSS 文件中使用它來將按鈕設置為網格顯示。 - 接下來,我們聲明了
Button
組件的三個實例。 如果你還記得,Button
組件有兩個 props,title
和onClick
。 在Button
組件的每個實例中,都提供了這兩個道具。 -
title
屬性採用選項卡的標題。 -
onClick
屬性接受一個我們剛剛創建的函數onTabClick
,它接受一個參數:所選選項卡的名稱。
根據當前選擇的選項卡,我們將使用 JavaScript 三元運算符有條件地顯示選項卡。 這意味著如果openedEditor
狀態的值設置為html
(即setOpenedEditor('html')
),那麼HTML 部分的選項卡將成為當前可見的選項卡。 當我們在下面這樣做時,您會更好地理解這一點:
... 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> ); ...
讓我們用簡單的英語回顧一下上面的代碼。 如果openedEditor
的值為html
,則顯示 HTML 部分。 否則,如果openedEditor
的值為css
,則顯示 CSS 部分。 否則,如果該值既不是html
也不是css
,那麼這意味著該值必須是js
,因為openedEditor
狀態只有三個可能的值; 所以,然後我們將顯示 JavaScript 的選項卡。
我們對三元運算符條件中的不同部分使用了段落標籤 ( p
)。 隨著我們繼續,我們將創建編輯器組件並用編輯器組件本身替換p
標籤。
我們已經走到這一步了! 單擊按鈕時,它會觸發將其代表的選項卡設置為true
的操作,從而使該選項卡可見。 這是我們的應用程序當前的樣子:
讓我們在包含按鈕的div
容器中添加一點 CSS。 我們希望按鈕顯示在網格中,而不是像上圖那樣垂直堆疊。 轉到您的App.css
文件並添加以下代碼:
.tab-button-container{ display: flex; }
回想一下,我們添加了className="tab-button-container"
作為包含三個選項卡按鈕的div
標記中的屬性。 在這裡,我們為該容器設置樣式,使用 CSS 將其顯示設置為flex
。 這是結果:
為你為達到這一點所做的努力感到自豪。 在下一節中,我們將創建我們的編輯器,用它們替換p
標籤。
創建編輯器
因為我們已經在 CodeMirror 編輯器中安裝了要處理的庫,所以讓我們繼續在components
文件夾中創建Editor.jsx
文件。
組件 > Editor.jsx
創建新文件後,讓我們在其中編寫一些初始代碼:
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
這是我們所做的:
- 我們將 React 與
useState
掛鉤一起導入,因為我們將需要它。 - 我們導入了 CodeMirror CSS 文件(它來自我們安裝的 CodeMirror 庫,因此您不必以任何特殊方式安裝它)。
- 我們從
react-codemirror2
導入Controlled
,將其重命名為ControlledEditorComponent
以使其更清晰。 我們將很快使用它。 - 然後,我們聲明了我們的
Editor
功能組件,我們有一個帶有空div
的 return 語句,現在在 return 語句中有一個className
。
在我們的函數組件中,我們從 props 中解構了一些值,包括language
、 value
和setEditorState
。 當在App.js
中調用編輯器時,這三個道具將在編輯器的任何實例中提供。
讓我們使用ControlledEditorComponent
為我們的編輯器編寫代碼。 這是我們要做的:
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
讓我們來看看我們在這裡做了什麼,解釋一些 CodeMirror 術語。
CodeMirror 模式指定編輯器適用於哪種語言。 我們導入了三種模式,因為我們有這個項目的三個編輯器:
- XML:此模式適用於 HTML。 它使用術語 XML。
- JavaScript:這(
codemirror/mode/javascript/javascript
)引入了 JavaScript 模式。 - CSS:這(
codemirror/mode/css/css
)引入了 CSS 模式。
注意:因為編輯器是作為可重用的組件構建的,所以我們不能在編輯器中放置直接模式。 所以,我們通過我們解構的language
道具來提供模式。 但這並沒有改變需要導入模式才能工作的事實。
接下來,我們來討論一下ControlledEditorComponent
中的東西:
-
onBeforeChange
每當您向編輯器寫入或從編輯器中刪除時,都會調用此方法。 可以將其想像為通常在輸入字段中用於跟踪更改的onChange
處理程序。 使用它,我們將能夠在有新更改的任何時候獲取編輯器的值並將其保存到編輯器的狀態。 我們將繼續編寫{handleChange}
函數。 -
value = {value}
這只是編輯器在任何給定時間的內容。 我們將一個名為value
的解構道具傳遞給該屬性。value
props 是保存該編輯器值的狀態。 這將由編輯器的實例提供。 -
className
="code-mirror-wrapper"
這個類名不是我們自己製作的樣式。 它由我們在上面導入的 CodeMirror 的 CSS 文件提供。 -
options
這是一個具有我們希望編輯器具有的不同功能的對象。 CodeMirror 中有許多令人驚嘆的選項。 讓我們看看我們在這裡使用的那些:-
lineWrapping: true
這意味著當行滿時代碼應該換行到下一行。 -
lint: true
這允許掉毛。 -
mode: language
如上所述,此模式採用編輯器將要使用的語言。 上面已經導入了語言,但是編輯器將根據通過 prop 提供給編輯器的language
值應用一種語言。 -
lineNumbers: true
這指定編輯器應該有每一行的行號。
-
接下來,我們可以為onBeforeChange
處理程序編寫handleChange
函數:
const handleChange = (editor, data, value) => { setEditorState(value); }
onBeforeChange
處理程序讓我們可以訪問三件事: editor, data, value
。
我們只需要這個value
,因為它是我們想要在setEditorState
中傳遞的值。 setEditorState
屬性代表我們在App.js
中聲明的每個狀態的設置值,保存每個編輯器的值。 隨著我們繼續,我們將看看如何將其作為道具傳遞給Editor
組件。
接下來,我們將添加一個下拉列表,允許我們為編輯器選擇不同的主題。 那麼,讓我們看看 CodeMirror 中的主題。
CodeMirror 主題
CodeMirror 有多個主題可供我們選擇。 訪問官方網站以查看可用的不同主題的演示。 讓我們創建一個包含不同主題的下拉列表,用戶可以在我們的編輯器中選擇這些主題。 在本教程中,我們將添加五個主題,但您可以添加任意數量的主題。
首先,讓我們在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';
接下來,創建一個包含我們導入的所有主題的數組:
const themeArray = ['dracula', 'material', 'mdn-like', 'the-matrix', 'night']
讓我們聲明一個useState
掛鉤來保存所選主題的值,並將默認主題設置為dracula
:
const [theme, setTheme] = useState("dracula")
讓我們創建下拉列表:
... 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> ) ...
在上面的代碼中,我們使用label
HTML 標籤向我們的下拉列表添加標籤,然後添加select
HTML 標籤來創建我們的下拉列表。 select
元素中的option
標籤定義下拉菜單中可用的選項。
因為我們需要用我們創建的themeArray
中的主題名稱填充下拉列表,所以我們使用.map
數組方法來映射themeArray
並使用option
標籤單獨顯示名稱。
等等——我們還沒有解釋完上面的代碼。 在開始的select
標籤中,我們傳遞了onChange
屬性來跟踪和更新theme
狀態,只要在下拉列表中選擇了新值。 每當在下拉列表中選擇一個新選項時,該值都是從返回給我們的對像中獲取的。 接下來,我們使用 state hook 中的setTheme
將新值設置為 state 持有的值。
至此,我們已經創建了下拉列表,設置了主題的狀態,並編寫了我們的函數來設置具有新值的狀態。 為了使 CodeMirror 使用我們的主題,我們需要做的最後一件事是將主題傳遞給ControlledEditorComponent
中的options
對象。 在options
對像中,我們添加一個名為theme
的值,並將其值設置為所選主題的狀態值,也名為theme
。
這是ControlledEditorComponent
現在的樣子:
<ControlledEditorComponent onBeforeChange={handleChange} value= {value} className="code-mirror-wrapper" options={{ lineWrapping: true, lint: true, mode: language, lineNumbers: true, theme: theme, }} />
現在,我們製作了一個可以在編輯器中選擇的不同主題的下拉列表。
以下是Editor.js
中的完整代碼目前的樣子:
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
我們只需要設置一個className
樣式。 轉到App.css
並添加以下樣式:
.editor-container{ padding-top: 0.4%; }
現在我們的編輯器已經準備好了,讓我們回到App.js
並在那裡使用它們。
src > App.js
我們需要做的第一件事是在此處導入Editor.js
組件:
import Editor from './components/Editor';
在App.js
中,讓我們分別聲明將保存 HTML、CSS 和 JavaScript 編輯器內容的狀態。
const [html, setHtml] = useState(''); const [css, setCss] = useState(''); const [js, setJs] = useState('');
如果您還記得,我們將需要使用這些狀態來保存和提供我們編輯器的內容。
接下來,讓我們用剛剛創建的編輯器組件替換我們在條件渲染中用於 HTML、CSS 和 JavaScript 的段落 ( p
) 標籤,我們還將向編輯器的每個實例傳遞適當的 prop零件:
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;
如果你一直跟著到現在,你就會明白我們在上面的代碼塊中做了什麼。
這裡是簡單的英語:我們用編輯器組件的實例替換了p
標籤(作為佔位符)。 然後,我們分別提供它們的language
、 value
和setEditorState
來匹配它們對應的狀態。
我們已經走了這麼遠! 這是我們的應用程序現在的樣子:
iframe 簡介
我們將使用內聯框架 (iframe) 來顯示在編輯器中輸入的代碼的結果。
根據 MDN:
HTML 內聯框架元素 (
<iframe>
) 表示嵌套的瀏覽上下文,將另一個 HTML 頁面嵌入到當前頁面中。
iframe 如何在 React 中工作
iframe 通常與純 HTML 一起使用。 將 iframe 與 React 一起使用不需要很多更改,主要是將屬性名稱轉換為駝峰式。 一個例子是srcdoc
會變成srcDoc
。
Web 上 iframe 的未來
iframe 在 Web 開發中仍然非常有用。 您可能想要檢查的是門戶。 正如 Daniel Brain 解釋的那樣:
“門戶網站在這種組合中引入了一組強大的新功能。 現在可以構建一些感覺像 iframe 的東西,它可以無縫地動畫和變形,並接管整個瀏覽器窗口。”
Portals 試圖解決的問題之一是 URL 欄問題。 使用 iframe 時,在 iframe 中渲染的組件不會在地址欄中攜帶唯一的 URL; 因此,這可能對用戶體驗不利,具體取決於用例。 Portals 值得一試,我建議您這樣做,但因為它不是我們文章的重點,所以我將在此僅介紹它。
創建 iframe 來存放我們的結果
讓我們繼續我們的教程,創建一個 iframe 來容納我們的編輯器的結果。
return ( <div className="App"> // ... <div> <iframe srcDoc={srcDoc} title="output" sandbox="allow-scripts" frameBorder="1" width="100%" height="100%" /> </div> </div> );
在這裡,我們創建了 iframe 並將其存放在div
容器標籤中。 在 iframe 中,我們傳遞了一些我們需要的屬性:
-
srcDoc
srcDoc
屬性是用駝峰寫的,因為這是在 React 中編寫 iframe 屬性的方法。 使用 iframe 時,我們可以在頁面上嵌入外部網頁或呈現指定的 HTML 內容。 要加載和嵌入外部頁面,我們將使用src
屬性。 在我們的例子中,我們沒有加載外部頁面; 相反,我們想創建一個新的內部 HTML 文檔來存放我們的結果; 為此,我們需要srcDoc
屬性。 該屬性採用我們想要嵌入的 HTML 文檔(我們還沒有創建它,但很快就會創建)。 -
title
title 屬性用於描述內聯框架的內容。 -
sandbox
這個屬性有很多用途。 在我們的例子中,我們使用它來允許腳本在我們的 iframe 中使用allow-scripts
值運行。 因為我們正在使用 JavaScript 編輯器,所以這會很快派上用場。 -
frameBorder
這僅定義了 iframe 的邊框厚度。 -
width
和height
這定義了 iframe 的寬度和高度。
這些術語現在應該對您更有意義。 讓我們繼續並聲明將保存srcDoc
的 HTML 模板文檔的狀態。 如果您仔細查看上面的代碼塊,您會看到我們向srcDoc
屬性傳遞了一個值: srcDoc
={srcDoc}
。 讓我們使用我們的useState()
React 鉤子來聲明srcDoc
狀態。 為此,在App.js
文件中,轉到我們定義其他狀態的位置並添加以下狀態:
const [srcDoc, setSrcDoc] = useState(` `);
現在我們已經創建了狀態,接下來要做的就是在我們在代碼編輯器中輸入時在狀態中顯示結果。 但是我們不希望在每次按鍵時重新渲染組件。 考慮到這一點,讓我們繼續。
配置 iframe 以顯示結果
每次分別在 HTML、CSS 和 JavaScript 的任何編輯器中發生更改時,我們都希望觸發useEffect()
,這將在 iframe 中呈現更新的結果。 讓我們在App.js
文件中編寫useEffect()
來執行此操作:
首先,導入useEffect()
鉤子:
import React, { useState, useEffect } from 'react';
讓我們這樣寫useEffect()
:
useEffect(() => { const timeOut = setTimeout(() => { setSrcDoc( ` <html> <body>${html}</body> <style>${css}</style> <script>${js}</script> </html> ` ) }, 250); return () => clearTimeout(timeOut) }, [html, css, js])
在這裡,我們編寫了一個useEffect()
掛鉤,只要我們為 HTML、CSS 和 JavaScript 編輯器聲明的值狀態發生更改或更新,該掛鉤就會始終運行。
為什麼我們需要使用setTimeout()
? 好吧,如果我們在沒有它的情況下編寫它,那麼每次在編輯器中按下一個鍵,我們的 iframe 都會更新,這通常不利於性能。 所以我們使用setTimeout()
將更新延遲 250 毫秒,讓我們有足夠的時間知道用戶是否還在打字。 也就是說,每次用戶按下一個鍵時,它都會重新開始計數,因此 iframe 只會在用戶空閒(未鍵入)250 毫秒時更新。 這是避免每次按下鍵時都必須更新 iframe 的一種很酷的方法。
我們在上面做的下一件事是用新的變化來更新srcDoc
。 如上所述, srcDoc
組件在 iframe 中呈現指定的 HTML 內容。 在我們的代碼中,我們傳遞了一個 HTML 模板,獲取包含用戶在 HTML 編輯器中鍵入的代碼的html
狀態,並將其放置在模板的body
標記之間。 我們還獲取了包含用戶在 CSS 編輯器中鍵入的樣式的css
狀態,並在style
標籤之間傳遞了它。 最後,我們獲取包含用戶在 JavaScript 編輯器中鍵入的 JavaScript 代碼的js
狀態,並在script
標籤之間傳遞它。
請注意,在設置setSrcDoc
時,我們使用了反引號 ( ` `
) 而不是普通引號 ( ' '
)。 這是因為反引號允許我們傳入相應的狀態值,就像我們在上面的代碼中所做的那樣。
useEffect()
鉤子中的return
語句是一個清理函數,它在完成時清除setTimeout()
,以避免內存洩漏。 該文檔有更多關於useEffect
的信息。
這是我們的項目目前的樣子:
CodeMirror 插件
使用 CodeMirror 插件,我們可以使用其他代碼編輯器中的更多功能來增強我們的編輯器。 讓我們來看一個在輸入開始標籤時自動添加結束標籤的示例,以及在輸入開始括號時自動關閉括號的另一個示例:
首先要做的是將插件導入到我們的App.js
文件中:
import 'codemirror/addon/edit/closetag'; import 'codemirror/addon/edit/closebrackets';
讓我們在ControlledEditorComponent
選項中傳遞它:
<ControlledEditorComponent ... options={{ ... autoCloseTags: true, autoCloseBrackets: true, }} />
現在這就是我們所擁有的:
您可以將大量這些插件添加到您的編輯器中,以使其具有更豐富的功能。 我們不可能在這裡遍歷所有這些。
現在我們已經完成了這些,讓我們簡要討論一下我們可以做些什麼來提高我們的應用程序的可訪問性和性能。
解決方案的性能和可訪問性
看看我們的網絡代碼編輯器,有些東西肯定可以改進。
因為我們主要關注功能,所以我們可能會稍微忽略設計。 為了獲得更好的可訪問性,您可以採取以下措施來改進此解決方案:
- 您可以在當前打開的編輯器的按鈕上設置一個
active
類。 突出顯示該按鈕將通過讓用戶清楚地指示他們當前正在使用哪個編輯器來提高可訪問性。 - 您可能希望編輯器佔用比我們這裡更多的屏幕空間。 您可以嘗試的另一件事是通過單擊停靠在側面某處的按鈕來彈出 iframe。 這樣做會給編輯器更多的屏幕空間。
- 這種編輯器對於想要在他們的移動設備上進行快速練習的人來說很有用,因此完全適應移動設備是必要的(更不用說上面關於移動設備的兩點了)。
- 目前,我們可以在加載的多個主題中切換編輯器組件的主題,但頁面的總體主題保持不變。 您可以讓用戶在整個佈局的深色和淺色主題之間切換。 這將有利於可訪問性,減輕人們長時間看明亮的屏幕對眼睛的壓力。
- 我們沒有考慮 iframe 的安全問題,主要是因為我們在 iframe 中加載了內部 HTML 文檔,而不是外部文檔。 所以我們不需要考慮太仔細,因為 iframe 非常適合我們的用例。
- 對於 iframe,另一個考慮因素是頁面加載時間,因為 iframe 中加載的內容通常不受您的控制。 在我們的應用程序中,這不是問題,因為我們的 iframe 內容不是外部的。
當您構建任何應用程序時,性能和可訪問性都值得考慮很多,因為它們將決定您的應用程序對其用戶的有用性和可用性。
Shedrack 在解釋改進和優化 React 應用程序性能的方法方面做得很好。 值得一試!
結論
通過不同的項目工作有助於我們了解廣泛的主題。 既然您已經閱讀了這篇文章,請隨意擴展您的體驗,嘗試更多的插件以使代碼編輯器更豐富,改進 UI,並修復上面概述的可訪問性和性能問題。
- 該項目的整個代碼庫可在 GitHub 上找到。
這是 Codesandbox 上的演示:
鏈接和材料
- “谷歌瀏覽器的門戶:像 iframe,但更好,更差”,Daniel Brain
- “優化性能”,React 文檔
- “用戶手冊和參考指南”,CodeMirror 文檔