การสร้างตัวแก้ไขรหัสเว็บ
เผยแพร่แล้ว: 2022-03-10โปรแกรมแก้ไขโค้ดเว็บออนไลน์มีประโยชน์มากที่สุดเมื่อคุณไม่มีโอกาสใช้แอปพลิเคชันตัวแก้ไขโค้ด หรือเมื่อคุณต้องการลองใช้บางอย่างบนเว็บอย่างรวดเร็วด้วยคอมพิวเตอร์หรือแม้แต่โทรศัพท์มือถือของคุณ นี่เป็นโครงการที่น่าสนใจในการทำงาน เพราะการมีความรู้เกี่ยวกับวิธีสร้างโปรแกรมแก้ไขโค้ดจะทำให้คุณมีแนวคิดเกี่ยวกับวิธีเข้าถึงโครงการอื่นๆ ที่ต้องการให้คุณผสานรวมโปรแกรมแก้ไขโค้ดเพื่อแสดงฟังก์ชันการทำงานบางอย่าง
ต่อไปนี้เป็นแนวคิดเกี่ยวกับ React บางส่วนที่คุณจำเป็นต้องรู้เพื่อปฏิบัติตามในบทความนี้:
- ตะขอ
- โครงสร้างส่วนประกอบ
- องค์ประกอบการทำงาน
- อุปกรณ์ประกอบฉาก
การใช้ CodeMirror
เราจะใช้ห้องสมุดชื่อ CodeMirror เพื่อสร้างตัวแก้ไขของเรา CodeMirror เป็นโปรแกรมแก้ไขข้อความอเนกประสงค์ที่ใช้ใน JavaScript สำหรับเบราว์เซอร์ โดยเฉพาะอย่างยิ่งสำหรับการแก้ไขโค้ดและมาพร้อมกับโหมดภาษาและส่วนเสริมจำนวนมากสำหรับฟังก์ชันการแก้ไขขั้นสูง
API การเขียนโปรแกรมที่หลากหลายและระบบชุดรูปแบบ CSS พร้อมใช้งานสำหรับการปรับแต่ง CodeMirror เพื่อให้เหมาะสมกับแอปพลิเคชันของคุณและขยายด้วยฟังก์ชันการทำงานใหม่ มันทำให้เรามีฟังก์ชันในการสร้างตัวแก้ไขโค้ดที่สมบูรณ์ซึ่งทำงานบนเว็บและแสดงผลลัพธ์ของโค้ดของเราแบบเรียลไทม์
ในส่วนถัดไป เราจะตั้งค่าโครงการ React ใหม่และติดตั้งไลบรารีที่เราต้องการเพื่อสร้างเว็บแอปของเรา
การสร้างโครงการตอบโต้ใหม่
เริ่มต้นด้วยการสร้างโครงการ React ใหม่ ในอินเทอร์เฟซ commandline ของคุณ นำทางไปยังไดเร็กทอรีที่คุณต้องการสร้างโครงการของคุณ และสร้างแอปพลิเคชัน React และตั้งชื่อมันว่า code_editor
:
npx create-react-app code_editor
เมื่อสร้างแอปพลิเคชัน React ใหม่แล้ว ให้ไปที่ไดเร็กทอรีของโครงการนั้นในอินเทอร์เฟซบรรทัดคำสั่ง:
cd code_editor
มีสองไลบรารีที่เราจำเป็นต้องติดตั้งที่นี่: codemirror
และ react-codemirror2
npm install codemirror react-codemirror2
หลังจากติดตั้งไลบรารีที่เราต้องการสำหรับโปรเจ็กต์นี้แล้ว มาสร้างแท็บของเราและเปิดใช้งานการสลับแท็บระหว่างสามแท็บที่จะปรากฏในโปรแกรมแก้ไขของเรา (สำหรับ HTML, CSS และ JavaScript)
ส่วนประกอบปุ่ม
แทนที่จะสร้างปุ่มทีละปุ่ม เรามาทำให้ปุ่มนั้นเป็นส่วนประกอบที่นำกลับมาใช้ใหม่ได้ ในโครงการของเรา ปุ่มจะมีสามอินสแตนซ์ ตามสามแท็บที่เราต้องการ
สร้างโฟลเดอร์ชื่อ components
ในโฟลเดอร์ src
ในโฟลเดอร์ components
ใหม่นี้ ให้สร้างไฟล์ JSX ชื่อ Button.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
ซึ่งเราส่งออกแล้ว - เราทำลายโครงสร้าง
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
จะใช้อุปกรณ์ประกอบฉากสองอย่างtitle
และonClick
อุปกรณ์ประกอบฉากทั้งสองนี้มีให้ในส่วนประกอบButton
ทุกอินสแตนซ์ - พร็อพ
title
ใช้ชื่อของแท็บ -
onClick
prop ใช้ฟังก์ชันonTabClick
ซึ่งเราเพิ่งสร้างขึ้นและใช้อาร์กิวเมนต์เดียว: ชื่อของแท็บที่เลือก
ตามแท็บที่เลือกในปัจจุบัน เราจะใช้ตัวดำเนินการ ternary 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
ทำให้แท็บนั้นมองเห็นได้ นี่คือลักษณะที่แอพของเราในปัจจุบัน:
มาเพิ่ม CSS เล็กน้อยให้กับคอนเทนเนอร์ div
ที่ถือปุ่มไว้ เราต้องการให้ปุ่มต่างๆ แสดงในตาราง แทนที่จะวางซ้อนกันในแนวตั้งเหมือนในภาพด้านบน ไปที่ไฟล์ App.css
ของคุณและเพิ่มรหัสต่อไปนี้:
.tab-button-container{ display: flex; }
จำได้ว่าเราได้เพิ่ม className="tab-button-container"
เป็นแอตทริบิวต์ในแท็ก div
ที่มีปุ่มสามแท็บ ที่นี่ เราจัดรูปแบบคอนเทนเนอร์นั้น โดยใช้ CSS เพื่อตั้งค่าการแสดงผลเป็น flex
นี่คือผลลัพธ์:
จงภูมิใจในสิ่งที่คุณทำเพื่อมาถึงจุดนี้ ในส่วนถัดไป เราจะสร้างตัวแก้ไขของเรา โดยแทนที่แท็ก p
ด้วย
การสร้างบรรณาธิการ
เนื่องจากเราได้ติดตั้งไลบรารี่ที่เราจะดำเนินการภายในตัวแก้ไข CodeMirror ของเราแล้ว ให้สร้างไฟล์ Editor.jsx
ของเราในโฟลเดอร์ components
ส่วนประกอบ > 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
hook เพราะเราต้องการมัน - เรานำเข้าไฟล์ CodeMirror CSS (ซึ่งมาจากไลบรารี CodeMirror ที่เราติดตั้ง ดังนั้นคุณจึงไม่ต้องติดตั้งด้วยวิธีพิเศษใดๆ)
- เรานำเข้า
Controlled
จากreact-codemirror2
โดยเปลี่ยนชื่อเป็นControlledEditorComponent
เพื่อให้ชัดเจนยิ่งขึ้น เราจะใช้สิ่งนี้ในไม่ช้า - จากนั้น เราประกาศองค์ประกอบการทำงานของ
Editor
และเรามีคำสั่ง return ที่มีdiv
ว่างเปล่า โดยมีclassName
ในคำสั่ง return สำหรับตอนนี้
ในองค์ประกอบการทำงานของเรา เราทำลายโครงสร้างค่าบางส่วนจากอุปกรณ์ประกอบฉาก รวมถึง 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
คือสถานะที่ถือคุณค่าของเอดิเตอร์นั้น สิ่งนี้จะได้รับจากอินสแตนซ์ของบรรณาธิการ -
className
="code-mirror-wrapper"
ชื่อคลาสนี้ไม่ใช่สไตล์ที่เราทำขึ้นเอง มาจากไฟล์ CSS ของ CodeMirror ซึ่งเรานำเข้าด้านบน -
options
นี่คือออบเจ็กต์ที่ใช้ฟังก์ชันต่างๆ ที่เราต้องการให้เอดิเตอร์ของเรามี มีตัวเลือกที่น่าทึ่งมากมายใน CodeMirror ลองดูที่เราใช้ที่นี่:-
lineWrapping: true
ซึ่งหมายความว่าโค้ดควรตัดไปยังบรรทัดถัดไปเมื่อบรรทัดเต็ม -
lint: true
นี้จะช่วยให้ผ้าสำลี -
mode: language
โหมดนี้ ตามที่กล่าวไว้ข้างต้น ใช้ภาษาที่ตัวแก้ไขจะใช้สำหรับ ภาษาถูกนำเข้ามาข้างต้นแล้ว แต่ตัวแก้ไขกำลังจะใช้ภาษาตามค่าlanguage
ที่ส่งให้กับตัวแก้ไขผ่านพร็อพ -
lineNumbers: true
ระบุว่าเอดิเตอร์ควรมีหมายเลขบรรทัดสำหรับแต่ละบรรทัด
-
ต่อไป เราสามารถเขียนฟังก์ชัน handleChange
สำหรับตัวจัดการ onBeforeChange
:
const handleChange = (editor, data, value) => { setEditorState(value); }
ตัวจัดการ onBeforeChange
ทำให้เราสามารถเข้าถึงสามสิ่ง: editor, data, value
เราต้องการ value
เท่านั้นเพราะเป็นสิ่งที่เราต้องการส่งผ่านใน setEditorState
prop ของเรา พร็ setEditorState
แสดงถึงค่าที่ตั้งไว้สำหรับแต่ละสถานะที่เราประกาศใน App.js
โดยเก็บค่าไว้สำหรับเอดิเตอร์แต่ละคน เมื่อเราดำเนินการต่อไป เราจะดูวิธีการส่งสิ่งนี้เป็นพร็อพไปยังองค์ประกอบ Editor
ต่อไป เราจะเพิ่มดรอปดาวน์ที่ช่วยให้เราเลือกธีมต่างๆ สำหรับตัวแก้ไขได้ มาดูธีมใน CodeMirror กัน
ธีม CodeMirror
CodeMirror มีหลายธีมที่เราสามารถเลือกได้ เยี่ยมชมเว็บไซต์อย่างเป็นทางการเพื่อดูการสาธิตของธีมต่างๆ ที่มี มาทำเมนูดรอปดาวน์ด้วยธีมต่างๆ ที่ผู้ใช้เลือกได้ในเครื่องมือแก้ไขของเรา สำหรับบทช่วยสอนนี้ เราจะเพิ่มธีม 5 ธีม แต่คุณสามารถเพิ่มได้มากเท่าที่ต้องการ
อันดับแรก ให้นำเข้าธีมของเราในองค์ประกอบ 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
hook เพื่อเก็บค่าของธีมที่เลือกไว้ และตั้งค่าธีมเริ่มต้นเป็น 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> ) ...
ในโค้ดด้านบน เราใช้แท็ก HTML ของ label
เพื่อเพิ่มป้ายกำกับลงในรายการแบบเลื่อนลง จากนั้นจึงเพิ่มแท็ก HTML ที่ select
เพื่อสร้างรายการแบบเลื่อนลง แท็ก option
ในองค์ประกอบที่ select
จะกำหนดตัวเลือกที่มีอยู่ในรายการแบบเลื่อนลง
เนื่องจากเราจำเป็นต้องกรอกชื่อธีมใน themeArray
ที่เราสร้างขึ้นในดรอปดาวน์ เราจึงใช้เมธอด .map array เพื่อจับ themeArray
.map
แสดงชื่อทีละรายการโดยใช้แท็ก option
เดี๋ยวก่อน เรายังอธิบายโค้ดด้านบนไม่เสร็จ ในแท็กเปิดการ select
เราได้ส่งแอตทริบิวต์ onChange
เพื่อติดตามและอัปเดตสถานะของ theme
ทุกครั้งที่มีการเลือกค่าใหม่ในเมนูแบบเลื่อนลง เมื่อใดก็ตามที่เลือกตัวเลือกใหม่ในดรอปดาวน์ ค่าจะได้รับจากออบเจกต์ที่ส่งคืนให้เรา ต่อไป เราใช้ setTheme
จาก state hook เพื่อตั้งค่าใหม่ให้เป็นค่าที่รัฐเก็บไว้
ณ จุดนี้ เราได้สร้างดรอปดาวน์ ตั้งค่าสถานะของธีม และเขียนฟังก์ชันของเราเพื่อตั้งค่าสถานะด้วยค่าใหม่ สิ่งสุดท้ายที่เราต้องทำเพื่อให้ CodeMirror ใช้ธีมของเราคือส่งธีมไปที่อ็อบเจกต์ options
ใน ControlledEditorComponent
ในอ็อบเจ็กต์ 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('');
หากคุณจำได้ เราจะต้องใช้สถานะเหล่านี้เพื่อจัดเก็บและจัดหาเนื้อหาของบรรณาธิการของเรา
ต่อไป มาแทนที่แท็กย่อหน้า ( p
) ที่เราใช้สำหรับ HTML, CSS และ JavaScript ในการเรนเดอร์แบบมีเงื่อนไขด้วยองค์ประกอบตัวแก้ไขที่เราเพิ่งสร้างขึ้น และเราจะส่งต่อในอุปกรณ์ที่เหมาะสมไปยังแต่ละอินสแตนซ์ของตัวแก้ไข ส่วนประกอบ:
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
ตามลำดับ เพื่อให้ตรงกับสถานะที่เกี่ยวข้อง
เรามาไกลมากแล้ว! นี่คือสิ่งที่แอพของเราดูเหมือนตอนนี้:
ข้อมูลเบื้องต้นเกี่ยวกับ Iframes
เราจะใช้เฟรมอินไลน์ (iframes) เพื่อแสดงผลลัพธ์ของโค้ดที่ป้อนในตัวแก้ไข
ตาม MDN:
องค์ประกอบ HTML Inline Frame (
<iframe>
) แสดงถึงบริบทการสืบค้นที่ซ้อนกัน โดยฝังหน้า HTML อื่นลงในหน้าปัจจุบัน
Iframes ทำงานอย่างไรใน React
โดยปกติแล้ว Iframes จะใช้กับ HTML ธรรมดา การใช้ Iframes กับ React ไม่ต้องการการเปลี่ยนแปลงมากมาย สิ่งสำคัญคือการแปลงชื่อแอตทริบิวต์เป็น camelcase ตัวอย่างนี้คือ srcdoc
จะกลายเป็น srcDoc
อนาคตของ Iframes บนเว็บ
Iframes ยังคงมีประโยชน์อย่างมากในการพัฒนาเว็บ สิ่งที่คุณอาจต้องการตรวจสอบคือพอร์ทัล ดังที่ Daniel Brain อธิบายว่า:
“พอร์ทัลแนะนำชุดความสามารถใหม่อันทรงพลังในการผสมผสานนี้ ตอนนี้ เป็นไปได้ที่จะสร้างบางสิ่งที่ให้ความรู้สึกเหมือนเป็น iframe ที่สามารถเคลื่อนไหวและปรับเปลี่ยนอย่างราบรื่นและเข้าควบคุมหน้าต่างเบราว์เซอร์แบบเต็มได้”
สิ่งหนึ่งที่พอร์ทัลพยายามแก้ไขคือปัญหาแถบ URL เมื่อใช้ iframe ส่วนประกอบที่แสดงใน iframe จะไม่มี URL เฉพาะในแถบที่อยู่ เช่นนี้อาจไม่ดีสำหรับประสบการณ์ผู้ใช้ ขึ้นอยู่กับกรณีการใช้งาน พอร์ทัลเป็นสิ่งที่ควรค่าแก่การตรวจสอบ และฉันขอแนะนำให้คุณทำอย่างนั้น แต่เนื่องจากไม่ใช่จุดสนใจของบทความของเรา ฉันจึงจะพูดเกี่ยวกับเรื่องนี้ทั้งหมดที่นี่
การสร้าง 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
เขียนด้วย camelcase เนื่องจากเป็นวิธีการเขียนแอตทริบิวต์ iframe ใน React เมื่อใช้ iframe เราสามารถฝังหน้าเว็บภายนอกบนหน้าเว็บหรือแสดงเนื้อหา HTML ที่ระบุได้ ในการโหลดและฝังเพจภายนอก เราจะใช้คุณสมบัติsrc
แทน ในกรณีของเรา เราไม่ได้โหลดหน้าภายนอก เราต้องการสร้างเอกสาร HTML ภายในใหม่ซึ่งเป็นที่เก็บผลลัพธ์ของเรา สำหรับสิ่งนี้ เราต้องการแอตทริบิวต์srcDoc
คุณลักษณะนี้ใช้เอกสาร HTML ที่เราต้องการฝัง (เรายังไม่ได้สร้างสิ่งนั้น แต่เราจะสร้างในไม่ช้า) -
title
แอตทริบิวต์ชื่อใช้เพื่ออธิบายเนื้อหาของเฟรมแบบอินไลน์ -
sandbox
ทรัพย์สินนี้มีวัตถุประสงค์หลายประการ ในกรณีของเรา เราใช้มันเพื่อให้สคริปต์ทำงานใน iframe ของเราด้วยค่าallow-scripts
เนื่องจากเรากำลังทำงานกับตัวแก้ไข JavaScript สิ่งนี้จะสะดวกรวดเร็ว -
frameBorder
นี่เป็นเพียงการกำหนดความหนาของเส้นขอบของ iframe -
width
และheight
กำหนดความกว้างและความสูงของ iframe
เงื่อนไขเหล่านี้ควรเหมาะสมสำหรับคุณมากขึ้น ไปต่อและประกาศสถานะที่จะเก็บเอกสารเทมเพลต HTML สำหรับ srcDoc
หากคุณดูบล็อคโค้ดด้านบนอย่างใกล้ชิด คุณจะเห็นว่าเราส่งค่าไปยังแอตทริบิวต์ srcDoc
: srcDoc
={srcDoc}
ลองใช้ useState()
React hook เพื่อประกาศสถานะ srcDoc
ในการดำเนินการนี้ ในไฟล์ App.js
ให้ไปที่ตำแหน่งที่เรากำหนดสถานะอื่นๆ และเพิ่มสถานะนี้:
const [srcDoc, setSrcDoc] = useState(` `);
ตอนนี้เราได้สร้างสถานะแล้ว สิ่งต่อไปที่ต้องทำคือแสดงผลในสถานะทุกครั้งที่เราพิมพ์ในโปรแกรมแก้ไขโค้ด แต่สิ่งที่เราไม่ต้องการคือการแสดงผลคอมโพเนนต์ใหม่ทุกครั้งที่กดปุ่ม โดยที่ในใจให้ดำเนินการต่อไป
การกำหนดค่า Iframe เพื่อแสดงผล
ทุกครั้งที่มีการเปลี่ยนแปลงในตัวแก้ไขใดๆ สำหรับ HTML, CSS และ JavaScript ตามลำดับ เราต้องการให้ useEffect()
ทำงาน และนั่นจะแสดงผลลัพธ์ที่อัปเดตใน iframe มาเขียน useEffect()
เพื่อทำสิ่งนี้ในไฟล์ App.js
:
ขั้นแรก นำเข้า useEffect()
hook:
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
ตามที่เราอธิบายไว้ข้างต้น แสดงเนื้อหา HTML ที่ระบุใน iframe ในโค้ดของเรา เราได้ส่งเทมเพลต HTML โดยใช้สถานะ html
ที่มีโค้ดที่ผู้ใช้พิมพ์ลงในโปรแกรมแก้ไข HTML และวางไว้ระหว่างแท็ก body
ของเทมเพลตของเรา เรายังใช้สถานะ css
ที่มีสไตล์ที่ผู้ใช้พิมพ์ในตัวแก้ไข CSS และเราส่งต่อสิ่งนี้ระหว่างแท็ก style
สุดท้าย เราใช้สถานะ js
ที่มีโค้ด JavaScript ที่ผู้ใช้พิมพ์ในตัวแก้ไข JavaScript และเราส่งผ่านระหว่างแท็ก script
สังเกตว่าในการตั้งค่า setSrcDoc
เราใช้ backticks ( ` `
) แทนเครื่องหมายคำพูดปกติ ( ' '
) เนื่องจาก backticks ช่วยให้เราสามารถส่งผ่านค่าสถานะที่สอดคล้องกัน ดังที่เราทำในโค้ดด้านบน
คำสั่ง return
ใน useEffect()
เป็นฟังก์ชันการล้างข้อมูลที่ล้าง setTimeout()
เมื่อเสร็จสิ้น เพื่อหลีกเลี่ยงการรั่วไหลของหน่วยความจำ เอกสารประกอบมีข้อมูลเพิ่มเติมเกี่ยวกับ useEffect
นี่คือสิ่งที่โครงการของเราดูเหมือนในขณะนี้:
CodeMirror Addons
ด้วยส่วนเสริมของ CodeMirror เราสามารถปรับปรุงตัวแก้ไขของเราด้วยฟังก์ชันการทำงานประเภทอื่นๆ ที่เราพบในโปรแกรมแก้ไขโค้ดอื่นๆ มาดูตัวอย่างของการปิดแท็กที่เพิ่มโดยอัตโนมัติเมื่อพิมพ์แท็กเปิด และตัวอย่างอื่นของวงเล็บปิดโดยอัตโนมัติเมื่อป้อนวงเล็บเปิด:
สิ่งแรกที่ต้องทำคือนำเข้าส่วนเสริมสำหรับสิ่งนี้ลงในไฟล์ App.js
ของเรา:
import 'codemirror/addon/edit/closetag'; import 'codemirror/addon/edit/closebrackets';
ส่งต่อในตัวเลือก ControlledEditorComponent
:
<ControlledEditorComponent ... options={{ ... autoCloseTags: true, autoCloseBrackets: true, }} />
นี่คือสิ่งที่เรามี:
คุณสามารถเพิ่มส่วนเสริมเหล่านี้ได้มากมายในโปรแกรมแก้ไขของคุณเพื่อให้มีคุณลักษณะที่สมบูรณ์ยิ่งขึ้น เราไม่สามารถผ่านพวกเขาทั้งหมดได้ที่นี่
เมื่อเสร็จสิ้นแล้ว เรามาพูดคุยสั้นๆ เกี่ยวกับสิ่งที่เราสามารถทำได้เพื่อปรับปรุงการเข้าถึงและประสิทธิภาพของแอปของเรา
ประสิทธิภาพและการเข้าถึงโซลูชัน
เมื่อดูที่ตัวแก้ไขโค้ดเว็บของเรา บางสิ่งสามารถปรับปรุงได้อย่างแน่นอน
เนื่องจากเราให้ความสำคัญกับการใช้งานเป็นหลัก เราจึงอาจมองข้ามการออกแบบไปบ้าง เพื่อให้สามารถเข้าถึงได้ง่ายยิ่งขึ้น นี่คือบางสิ่งที่คุณสามารถทำได้เพื่อปรับปรุงโซลูชันนี้:
- คุณสามารถตั้งค่าคลาสที่
active
บนปุ่มสำหรับตัวแก้ไขที่เปิดอยู่ในปัจจุบัน การเน้นที่ปุ่มจะช่วยปรับปรุงการเข้าถึงโดยให้ผู้ใช้ระบุได้ชัดเจนว่ากำลังแก้ไขตัวแก้ไขใดอยู่ - คุณอาจต้องการให้โปรแกรมแก้ไขใช้พื้นที่หน้าจอมากกว่าที่เรามีอยู่ อีกสิ่งหนึ่งที่คุณสามารถลองได้คือการทำให้ iframe ปรากฏขึ้นด้วยการคลิกปุ่มที่วางอยู่ด้านข้าง การทำเช่นนี้จะทำให้เอดิเตอร์มีพื้นที่หน้าจอมากขึ้น
- เครื่องมือแก้ไขประเภทนี้จะเป็นประโยชน์สำหรับผู้ที่ต้องการทำแบบฝึกหัดสั้นๆ บนอุปกรณ์พกพา ดังนั้นจำเป็นต้องปรับให้เข้ากับอุปกรณ์เคลื่อนที่อย่างเต็มที่ (ไม่ต้องพูดถึงทั้งสองประเด็นเกี่ยวกับอุปกรณ์เคลื่อนที่ด้านบน)
- ในปัจจุบัน เราสามารถสลับธีมขององค์ประกอบตัวแก้ไขจากหลาย ๆ ธีมที่เราโหลดเข้าไป แต่ธีมทั่วไปของหน้ายังคงเหมือนเดิม คุณสามารถให้ผู้ใช้สลับระหว่างธีมสีเข้มและสีอ่อนสำหรับเค้าโครงทั้งหมดได้ ซึ่งจะเป็นการดีสำหรับการช่วยสำหรับการเข้าถึง ช่วยลดความตึงเครียดในสายตาของผู้คนจากการมองหน้าจอที่สว่างนานเกินไป
- เราไม่ได้พิจารณาปัญหาด้านความปลอดภัยกับ iframe ของเรา สาเหตุหลักมาจากการโหลดเอกสาร HTML ภายในใน iframe แทนที่จะเป็นเอกสารภายนอก เราจึงไม่ต้องพิจารณาเรื่องนี้ให้รอบคอบเกินไปเพราะ iframes เหมาะสมกับกรณีการใช้งานของเรา
- สำหรับ iframes ข้อควรพิจารณาอีกอย่างหนึ่งคือเวลาในการโหลดหน้าเว็บ เนื่องจากเนื้อหาที่โหลดใน iframe มักจะอยู่นอกเหนือการควบคุมของคุณ ในแอปของเรา นี่ไม่ใช่ปัญหาเพราะเนื้อหา iframe ของเราไม่ได้มาจากภายนอก
ประสิทธิภาพและความสามารถในการเข้าถึงเป็นสิ่งที่ควรค่าแก่การพิจารณาเมื่อคุณสร้างแอปพลิเคชันใดๆ เนื่องจากแอปพลิเคชันเหล่านี้จะกำหนดว่าแอปพลิเคชันของคุณมีประโยชน์และมีประโยชน์ต่อผู้ใช้มากน้อยเพียงใด
Shedrack อธิบายวิธีการปรับปรุงและเพิ่มประสิทธิภาพในแอป React ได้เป็นอย่างดี มันคุ้มค่าที่จะเช็คเอาท์!
บทสรุป
การทำงานในโครงการต่างๆ ช่วยให้เราเรียนรู้เกี่ยวกับวิชาต่างๆ มากมาย เมื่อคุณได้อ่านบทความนี้แล้ว อย่าลังเลที่จะขยายประสบการณ์ของคุณด้วยการทดลองกับส่วนเสริมอื่นๆ เพื่อทำให้ตัวแก้ไขโค้ดสมบูรณ์ยิ่งขึ้น ปรับปรุง UI และแก้ไขข้อกังวลด้านการเข้าถึงและประสิทธิภาพตามที่อธิบายไว้ข้างต้น
- ฐานรหัสทั้งหมดสำหรับโครงการนี้มีอยู่ใน GitHub
นี่คือตัวอย่างใน Codesandbox:
ลิงค์และวัสดุ
- “พอร์ทัลของ Google Chrome: เช่นเดียวกับ Iframes แต่ดีกว่าและแย่กว่านั้น” Daniel Brain
- “การเพิ่มประสิทธิภาพ”, เอกสารประกอบการตอบสนอง
- “คู่มือการใช้งานและคู่มืออ้างอิง”, เอกสาร CodeMirror