การสร้างตัวแก้ไขรหัสเว็บ

เผยแพร่แล้ว: 2022-03-10
สรุปโดยย่อ ↬ หากคุณเป็นนักพัฒนาซอฟต์แวร์ที่กำลังคิดจะสร้างแพลตฟอร์มที่ต้องใช้โปรแกรมแก้ไขโค้ดในรูปแบบใดรูปแบบหนึ่ง บทความนี้เหมาะสำหรับคุณ บทความนี้อธิบายวิธีสร้างตัวแก้ไขโค้ดเว็บที่แสดงผลลัพธ์แบบเรียลไทม์โดยใช้ HTML, CSS และ JavaScript บางตัว

โปรแกรมแก้ไขโค้ดเว็บออนไลน์มีประโยชน์มากที่สุดเมื่อคุณไม่มีโอกาสใช้แอปพลิเคชันตัวแก้ไขโค้ด หรือเมื่อคุณต้องการลองใช้บางอย่างบนเว็บอย่างรวดเร็วด้วยคอมพิวเตอร์หรือแม้แต่โทรศัพท์มือถือของคุณ นี่เป็นโครงการที่น่าสนใจในการทำงาน เพราะการมีความรู้เกี่ยวกับวิธีสร้างโปรแกรมแก้ไขโค้ดจะทำให้คุณมีแนวคิดเกี่ยวกับวิธีเข้าถึงโครงการอื่นๆ ที่ต้องการให้คุณผสานรวมโปรแกรมแก้ไขโค้ดเพื่อแสดงฟังก์ชันการทำงานบางอย่าง

ต่อไปนี้เป็นแนวคิดเกี่ยวกับ 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 ทำให้แท็บนั้นมองเห็นได้ นี่คือลักษณะที่แอพของเราในปัจจุบัน:

GIF ที่แสดงการสลับแท็บที่เรามีในปัจจุบัน
GIF ที่แสดงการสลับแท็บที่เรามีในปัจจุบัน (ตัวอย่างขนาดใหญ่)

มาเพิ่ม CSS เล็กน้อยให้กับคอนเทนเนอร์ div ที่ถือปุ่มไว้ เราต้องการให้ปุ่มต่างๆ แสดงในตาราง แทนที่จะวางซ้อนกันในแนวตั้งเหมือนในภาพด้านบน ไปที่ไฟล์ App.css ของคุณและเพิ่มรหัสต่อไปนี้:

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

จำได้ว่าเราได้เพิ่ม className="tab-button-container" เป็นแอตทริบิวต์ในแท็ก div ที่มีปุ่มสามแท็บ ที่นี่ เราจัดรูปแบบคอนเทนเนอร์นั้น โดยใช้ CSS เพื่อตั้งค่าการแสดงผลเป็น flex นี่คือผลลัพธ์:

เราใช้ 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 ระบุภาษาที่โปรแกรมแก้ไขมีไว้สำหรับ เรานำเข้าสามโหมดเนื่องจากเรามีผู้แก้ไขสามคนสำหรับโครงการนี้:

  1. XML: โหมดนี้ใช้สำหรับ HTML มันใช้คำว่า XML
  2. JavaScript: สิ่งนี้ ( codemirror/mode/javascript/javascript ) นำมาสู่โหมด JavaScript
  3. 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, }} />

นี่คือสิ่งที่เรามี:

รูปลักษณ์ของโครงการของเรา
(ตัวอย่างขนาดใหญ่)

คุณสามารถเพิ่มส่วนเสริมเหล่านี้ได้มากมายในโปรแกรมแก้ไขของคุณเพื่อให้มีคุณลักษณะที่สมบูรณ์ยิ่งขึ้น เราไม่สามารถผ่านพวกเขาทั้งหมดได้ที่นี่

เมื่อเสร็จสิ้นแล้ว เรามาพูดคุยสั้นๆ เกี่ยวกับสิ่งที่เราสามารถทำได้เพื่อปรับปรุงการเข้าถึงและประสิทธิภาพของแอปของเรา

ประสิทธิภาพและการเข้าถึงโซลูชัน

เมื่อดูที่ตัวแก้ไขโค้ดเว็บของเรา บางสิ่งสามารถปรับปรุงได้อย่างแน่นอน

เนื่องจากเราให้ความสำคัญกับการใช้งานเป็นหลัก เราจึงอาจมองข้ามการออกแบบไปบ้าง เพื่อให้สามารถเข้าถึงได้ง่ายยิ่งขึ้น นี่คือบางสิ่งที่คุณสามารถทำได้เพื่อปรับปรุงโซลูชันนี้:

  1. คุณสามารถตั้งค่าคลาสที่ active บนปุ่มสำหรับตัวแก้ไขที่เปิดอยู่ในปัจจุบัน การเน้นที่ปุ่มจะช่วยปรับปรุงการเข้าถึงโดยให้ผู้ใช้ระบุได้ชัดเจนว่ากำลังแก้ไขตัวแก้ไขใดอยู่
  2. คุณอาจต้องการให้โปรแกรมแก้ไขใช้พื้นที่หน้าจอมากกว่าที่เรามีอยู่ อีกสิ่งหนึ่งที่คุณสามารถลองได้คือการทำให้ iframe ปรากฏขึ้นด้วยการคลิกปุ่มที่วางอยู่ด้านข้าง การทำเช่นนี้จะทำให้เอดิเตอร์มีพื้นที่หน้าจอมากขึ้น
  3. เครื่องมือแก้ไขประเภทนี้จะเป็นประโยชน์สำหรับผู้ที่ต้องการทำแบบฝึกหัดสั้นๆ บนอุปกรณ์พกพา ดังนั้นจำเป็นต้องปรับให้เข้ากับอุปกรณ์เคลื่อนที่อย่างเต็มที่ (ไม่ต้องพูดถึงทั้งสองประเด็นเกี่ยวกับอุปกรณ์เคลื่อนที่ด้านบน)
  4. ในปัจจุบัน เราสามารถสลับธีมขององค์ประกอบตัวแก้ไขจากหลาย ๆ ธีมที่เราโหลดเข้าไป แต่ธีมทั่วไปของหน้ายังคงเหมือนเดิม คุณสามารถให้ผู้ใช้สลับระหว่างธีมสีเข้มและสีอ่อนสำหรับเค้าโครงทั้งหมดได้ ซึ่งจะเป็นการดีสำหรับการช่วยสำหรับการเข้าถึง ช่วยลดความตึงเครียดในสายตาของผู้คนจากการมองหน้าจอที่สว่างนานเกินไป
  5. เราไม่ได้พิจารณาปัญหาด้านความปลอดภัยกับ iframe ของเรา สาเหตุหลักมาจากการโหลดเอกสาร HTML ภายในใน iframe แทนที่จะเป็นเอกสารภายนอก เราจึงไม่ต้องพิจารณาเรื่องนี้ให้รอบคอบเกินไปเพราะ iframes เหมาะสมกับกรณีการใช้งานของเรา
  6. สำหรับ iframes ข้อควรพิจารณาอีกอย่างหนึ่งคือเวลาในการโหลดหน้าเว็บ เนื่องจากเนื้อหาที่โหลดใน iframe มักจะอยู่นอกเหนือการควบคุมของคุณ ในแอปของเรา นี่ไม่ใช่ปัญหาเพราะเนื้อหา iframe ของเราไม่ได้มาจากภายนอก

ประสิทธิภาพและความสามารถในการเข้าถึงเป็นสิ่งที่ควรค่าแก่การพิจารณาเมื่อคุณสร้างแอปพลิเคชันใดๆ เนื่องจากแอปพลิเคชันเหล่านี้จะกำหนดว่าแอปพลิเคชันของคุณมีประโยชน์และมีประโยชน์ต่อผู้ใช้มากน้อยเพียงใด

Shedrack อธิบายวิธีการปรับปรุงและเพิ่มประสิทธิภาพในแอป React ได้เป็นอย่างดี มันคุ้มค่าที่จะเช็คเอาท์!

บทสรุป

การทำงานในโครงการต่างๆ ช่วยให้เราเรียนรู้เกี่ยวกับวิชาต่างๆ มากมาย เมื่อคุณได้อ่านบทความนี้แล้ว อย่าลังเลที่จะขยายประสบการณ์ของคุณด้วยการทดลองกับส่วนเสริมอื่นๆ เพื่อทำให้ตัวแก้ไขโค้ดสมบูรณ์ยิ่งขึ้น ปรับปรุง UI และแก้ไขข้อกังวลด้านการเข้าถึงและประสิทธิภาพตามที่อธิบายไว้ข้างต้น

  • ฐานรหัสทั้งหมดสำหรับโครงการนี้มีอยู่ใน GitHub

นี่คือตัวอย่างใน Codesandbox:

ลิงค์และวัสดุ

  • “พอร์ทัลของ Google Chrome: เช่นเดียวกับ Iframes แต่ดีกว่าและแย่กว่านั้น” Daniel Brain
  • “การเพิ่มประสิทธิภาพ”, เอกสารประกอบการตอบสนอง
  • “คู่มือการใช้งานและคู่มืออ้างอิง”, เอกสาร CodeMirror