بناء محرر كود الويب

نشرت: 2022-03-10
ملخص سريع ↬ إذا كنت مطورًا تفكر في إنشاء نظام أساسي يتطلب محرر كود بشكل أو بآخر ، فهذه المقالة مناسبة لك. تشرح هذه المقالة كيفية إنشاء محرر رمز ويب يعرض النتيجة في الوقت الفعلي بمساعدة بعض HTML و CSS و JavaScript.

يعد محرر أكواد الويب عبر الإنترنت مفيدًا للغاية عندما لا تتاح لك الفرصة لاستخدام تطبيق محرر التعليمات البرمجية ، أو عندما تريد تجربة شيء ما على الويب بسرعة باستخدام جهاز الكمبيوتر أو حتى الهاتف المحمول. يعد هذا أيضًا مشروعًا مثيرًا للاهتمام للعمل عليه لأن المعرفة بكيفية إنشاء محرر كود سيمنحك أفكارًا حول كيفية التعامل مع المشاريع الأخرى التي تتطلب منك دمج محرر كود لإظهار بعض الوظائف.

إليك بعض مفاهيم React التي ستحتاج إلى معرفتها للمتابعة في هذه المقالة:

  • خطاف
  • هيكل المكون ،
  • المكونات الوظيفية ،
  • الدعائم.

باستخدام CodeMirror

سنستخدم مكتبة تسمى CodeMirror لبناء محررنا. CodeMirror هو محرر نصوص متعدد الاستخدامات يتم تنفيذه في JavaScript للمتصفح. إنه مخصص لتحرير الكود ويأتي مع عدد من أوضاع اللغة والوظائف الإضافية لمزيد من وظائف التحرير المتقدمة.

تتوفر واجهة برمجة تطبيقات برمجة غنية ونظام سمات CSS لتخصيص CodeMirror لتلائم تطبيقك وتوسيعه بوظائف جديدة. يمنحنا وظيفة إنشاء محرر كود غني يعمل على الويب ويظهر لنا نتيجة الكود الخاص بنا في الوقت الفعلي.

في القسم التالي ، سنقوم بإعداد مشروع React الجديد الخاص بنا وتثبيت المكتبات التي نحتاجها لبناء تطبيق الويب الخاص بنا.

إنشاء مشروع 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).

المزيد بعد القفز! أكمل القراءة أدناه ↓

مكون الزر

بدلاً من إنشاء أزرار فردية ، دعنا نجعل الزر مكونًا يمكن إعادة استخدامه. في مشروعنا ، سيكون للزر ثلاث حالات ، وفقًا لعلامات التبويب الثلاث التي نحتاجها.

قم بإنشاء مجلد يسمى 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 ، سنقوم بإعداد الحالة التي ستخزن اسم علامة تبويب المحرر المفتوحة حاليًا عند النقر على زر علامة التبويب تلك.

إليك كيف نفعل ذلك:

 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 وظيفة ، 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 ، مما يجعل علامة التبويب هذه مرئية. إليك ما يبدو عليه تطبيقنا حاليًا:

صورة GIF تعرض علامة التبويب لدينا حاليًا.
صورة GIF تعرض علامة التبويب لدينا حاليًا. (معاينة كبيرة)

دعنا نضيف القليل من CSS إلى حاوية div التي تحتوي على الأزرار. نريد أن يتم عرض الأزرار في شبكة ، بدلاً من تكديسها عموديًا كما في الصورة أعلاه. انتقل إلى ملف App.css وأضف الكود التالي:

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

تذكر أننا أضفنا className="tab-button-container" كسمة في علامة div التي تحمل أزرار علامات التبويب الثلاثة. هنا ، قمنا بتصميم تلك الحاوية ، باستخدام CSS لضبط عرضها على flex . هذه هي النتيجة:

نستخدم CSS لضبط عرضه على الثني
(معاينة كبيرة)

كن فخورًا بالمقدار الذي فعلته للوصول إلى هذه النقطة. في القسم التالي ، سننشئ برامج التحرير الخاصة بنا ، مع استبدال علامات 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 لأننا سنحتاجه.
  • لقد قمنا باستيراد ملف CodeMirror CSS (الذي يأتي من مكتبة CodeMirror التي قمنا بتثبيتها ، لذا لن تضطر إلى تثبيته بأي طريقة خاصة).
  • لقد استوردنا Controlled من react-codemirror2 ، وأعدنا تسميته إلى ControlledEditorComponent لجعله أكثر وضوحًا. سوف نستخدم هذا قريبا.
  • بعد ذلك ، أعلنا عن المكون الوظيفي Editor ، ولدينا تعليمة إرجاع تحتوي على div فارغ ، مع اسم className في تعليمة الإرجاع في الوقت الحالي.

في مكوننا الوظيفي ، دمرنا بعض القيم من الخاصيات ، بما في ذلك 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
      هذا يسمح linting.
    • mode: language
      هذا الوضع ، كما نوقش أعلاه ، يأخذ اللغة التي سيتم استخدام المحرر لها. تم استيراد اللغة أعلاه بالفعل ، لكن المحرر سيطبق لغة بناءً على قيمة language المقدمة للمحرر عبر الخاصية.
    • lineNumbers: true
      هذا يحدد أن المحرر يجب أن يكون لديه أرقام أسطر لكل سطر.

بعد ذلك ، يمكننا كتابة دالة handleChange لمعالج onBeforeChange :

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

في الكود أعلاه ، استخدمنا علامة HTML label لإضافة تسمية إلى القائمة المنسدلة ، ثم أضفنا علامة HTML select لإنشاء القائمة المنسدلة الخاصة بنا. تحدد علامة option في عنصر select الخيارات المتاحة في القائمة المنسدلة.

نظرًا لأننا احتجنا إلى ملء القائمة المنسدلة بأسماء السمات في themeArray الذي أنشأناه ، فقد استخدمنا طريقة مصفوفة .map لتعيين themeArray وعرض الأسماء بشكل فردي باستخدام علامة option .

انتظر - لم ننتهي من شرح الكود أعلاه. في علامة select الافتتاحية ، مررنا سمة onChange لتتبع حالة theme وتحديثها كلما تم تحديد قيمة جديدة في القائمة المنسدلة. عندما يتم تحديد خيار جديد في القائمة المنسدلة ، يتم الحصول على القيمة من الكائن الذي يتم إرجاعه إلينا. بعد ذلك ، نستخدم setTheme من خطاف الحالة الخاص بنا لتعيين القيمة الجديدة لتكون القيمة التي تحتفظ بها الحالة.

في هذه المرحلة ، أنشأنا القائمة المنسدلة ، وقمنا بإعداد حالة موضوعنا ، وكتبنا وظيفتنا لضبط الحالة بالقيمة الجديدة. آخر شيء يتعين علينا القيام به لجعل 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 وجافا سكريبت في العروض الشرطية بمكونات المحرر التي أنشأناها للتو ، وسنمرر أيضًا الخاصية المناسبة لكل مثيل من المحرر عنصر:

 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

تُستخدم إطارات Iframe عادةً مع HTML عادي. لا يتطلب استخدام إطارات Iframes مع React العديد من التغييرات ، أهمها تحويل أسماء السمات إلى مجموعة أحرف. مثال على ذلك هو أن srcdoc ستصبح srcDoc .

مستقبل إطارات Iframes على الويب

لا تزال إطارات Iframe مفيدة حقًا في تطوير الويب. شيء ما قد ترغب في التحقق منه هو البوابات. كما يشرح دانيال برين:

"تقدم البوابات مجموعة جديدة قوية من الإمكانات في هذا المزيج. أصبح من الممكن الآن إنشاء شيء يبدو وكأنه إطار 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 في حالة الجمل لأن هذه هي كيفية كتابة سمات 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 للإعلان عن حالة srcDoc . للقيام بذلك ، في ملف App.js ، انتقل إلى حيث حددنا الحالات الأخرى وأضف هذه الحالة:

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

الآن وقد أنشأنا الحالة ، فإن الشيء التالي الذي يجب فعله هو عرض النتيجة في الحالة كلما نكتب في محرر الكود. ولكن ما لا نريده هو إعادة تصيير المكون في كل ضغطة مفتاح. مع وضع ذلك في الاعتبار ، دعنا ننتقل.

تكوين إطار Iframe لعرض النتيجة

في كل مرة يحدث تغيير في أي من محرري HTML و CSS و JavaScript ، على التوالي ، نريد useEffect() ، وسيؤدي ذلك إلى عرض النتيجة المحدثة في iframe. useEffect() للقيام بذلك في ملف App.js :

أولاً ، قم باستيراد 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 ، كما أوضحنا أعلاه ، محتوى HTML محددًا في iframe. في الكود الخاص بنا ، مررنا نموذج HTML ، مع أخذ حالة html التي تحتوي على الكود الذي كتبه المستخدم في محرر HTML ووضعه بين علامات body في القالب الخاص بنا. أخذنا أيضًا حالة css التي تحتوي على الأنماط التي كتبها المستخدم في محرر CSS ، وقمنا بتمريرها بين علامات style . أخيرًا ، أخذنا الحالة js التي تحتوي على كود JavaScript الذي كتبه المستخدم في محرر JavaScript ، وقمنا بتمريره بين علامات script .

لاحظ أنه في إعداد setSrcDoc ، استخدمنا backticks ( ` ` ) بدلاً من علامات الاقتباس العادية ( ' ' ). هذا لأن backticks تسمح لنا بتمرير قيم الحالة المقابلة ، كما فعلنا في الكود أعلاه.

عبارة return في useEffect() هي دالة تنظيف تقوم بمسح setTimeout() عند اكتمالها ، لتجنب تسرب الذاكرة. تحتوي الوثائق على المزيد حول useEffect .

إليك ما يبدو عليه مشروعنا في الوقت الحالي:

كيف يبدو مشروعنا في الوقت الحالي
(معاينة كبيرة)

ملحقات CodeMirror

باستخدام الوظائف الإضافية 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 ، وليس مستندًا خارجيًا. لذلك لا نحتاج إلى التفكير في هذا الأمر بعناية شديدة لأن إطارات iframe مناسبة لحالة الاستخدام لدينا.
  6. مع إطارات iframe ، هناك اعتبار آخر يتمثل في وقت تحميل الصفحة ، لأن المحتوى الذي يتم تحميله في إطار iframe يكون عادةً خارج نطاق سيطرتك. في تطبيقنا ، هذه ليست مشكلة لأن محتوى iframe الخاص بنا ليس خارجيًا.

يستحق الأداء وإمكانية الوصول قدرًا كبيرًا من الاهتمام عند إنشاء أي تطبيق لأنهما سيحددان مدى فائدة تطبيقك وقابليته للاستخدام لمستخدميه.

قام Shedrack بعمل جيد في شرح طرق تحسين الأداء وتحسينه في تطبيقات React. يستحق التدقيق!

خاتمة

يساعدنا العمل من خلال مشاريع مختلفة على التعرف على مجموعة واسعة من الموضوعات. الآن بعد مراجعة هذه المقالة ، لا تتردد في توسيع نطاق تجربتك من خلال تجربة المزيد من الوظائف الإضافية لجعل محرر الكود أكثر ثراءً ، وتجديد واجهة المستخدم ، وإصلاح مشكلات الوصول والأداء الموضحة أعلاه.

  • قاعدة التعليمات البرمجية الكاملة لهذا المشروع متاحة على GitHub.

هذا هو العرض التوضيحي على Codesandbox:

الروابط والمواد

  • "بوابات Google Chrome: مثل Iframes ، ولكن أفضل ، وأسوأ" ، دانيال برين
  • "تحسين الأداء" ، وثائق رد الفعل
  • "دليل المستخدم والدليل المرجعي" ، وثائق CodeMirror