مثال على الاستخدام العملي للوحدات





يوم جيد ، أيها الأصدقاء!



تعد الوحدات النمطية ES6 التي تستخدم صيغة "الاستيراد / التصدير" أدوات قوية جدًا ومنافسة جديرة لمكونات الأطر الشائعة.



اسمحوا لي أن أوضح ذلك من خلال رسم أشكال مختلفة على القماش.



مستوحى من هذا القسم من دليل JavaScript من MDN.



فيما يلي الوظائف التي سيتم تنفيذها في تطبيقنا الصغير:



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


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



من المستحسن أن يكون لديك على الأقل فهم أساسي للعمل مع الفصول واللوحة.



كود المشروع هنا .



يمكن الاطلاع على عرض للتطبيق هنا .



لنبدأ بالدعم.











بشكل عام جيد جدا. في المتوسط ​​، حوالي 93٪.



سيكون هيكل المشروع كما يلي (يمكنك إنشاء جميع الملفات دفعة واحدة أو إنشائها حسب الحاجة):



modules
  helpers
    convert.js
  shapes
    circle.js
    square.js
    triangle.js
  canvas.js
index.html
main.js
main.css


تبدو الترميز كما يلي:



<div>
  <section>
    <h3>Circle</h3>
    <label>
      X:
      <input type="number" value="75" data-prop="x" />
    </label>
    <label>
      Y:
      <input type="number" value="75" data-prop="y" />
    </label>
    <label>
      Radius:
      <input type="number" value="50" data-prop="radius" />
    </label>
    <label>
      Color:
      <input type="color" value="#ff0000" data-prop="color" />
    </label>
    <button data-btn="circle" class="draw_btn">Draw</button>
  </section>

  <section>
    <h3>Square</h3>
    <label>
      X:
      <input type="number" value="275" data-prop="x" />
    </label>
    <label>
      Y:
      <input type="number" value="175" data-prop="y" />
    </label>
    <label>
      Length:
      <input type="number" value="100" data-prop="length" />
    </label>
    <label>
      Color:
      <input type="color" value="#00ff00" data-prop="color" />
    </label>
    <button data-btn="square" class="draw_btn">Draw</button>
  </section>

  <section>
    <h3>Triangle</h3>
    <label>
      X:
      <input type="number" value="150" data-prop="x" />
    </label>
    <label>
      Y:
      <input type="number" value="100" data-prop="y" />
    </label>
    <label>
      Length:
      <input type="number" value="125" data-prop="length" />
    </label>
    <label>
      Color:
      <input type="color" value="#0000ff" data-prop="color" />
    </label>
    <button data-btn="triangle" class="draw_btn">Draw</button>
  </section>
</div>
<button>Clear Canvas</button>

<script src="main.js" type="module"></script>


ما الذي يجب الانتباه إليه هنا؟



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



انتبه إلى كيفية اتصال النص. مطلوب سمة "النوع" مع القيمة "الوحدة". سمة "التأجيل" غير مطلوبة في هذه الحالة ، حيث يتم تأجيل تحميل الوحدات (أي بعد تحميل الصفحة بالكامل) افتراضيًا. لاحظ أيضًا أننا نقوم فقط بتضمين ملف "main.js" في الصفحة. يتم استخدام ملفات أخرى داخل "main.js" كوحدات نمطية.



تتمثل إحدى السمات الرئيسية للوحدات النمطية في أن لكل وحدة نطاقها (سياقها) الخاص ، بما في ذلك "main.js". من ناحية أخرى ، يعد هذا أمرًا جيدًا لأنه يتجنب تلوث مساحة الاسم العالمية ، وبالتالي يمنع التعارض بين المتغيرات والوظائف التي تحمل الاسم نفسه. من ناحية أخرى ، إذا احتاجت وحدات نمطية مختلفة إلى الوصول إلى نفس عناصر DOM ، على سبيل المثال ، عليك إما إنشاء نص برمجي منفصل باستخدام متغيرات عامة وربطه بالصفحة قبل الوحدة النمطية الرئيسية ، أو إنشاء متغيرات عامة بشكل صريح (window.variable = value) ، أو إنشاء نفس المتغيرات داخل كل وحدة ، أو تبادل المتغيرات بين الوحدات (وهو ما سنفعله في الواقع).



هناك أيضًا طريقة رابعة: الوصول إلى عناصر DOM عن طريق المعرّف مباشرةً. هل تعلم عن هذا الاحتمال؟ على سبيل المثال ، إذا كان لدينا عنصر بالمعرف "main" في ترميزنا ، فيمكننا الإشارة إليه ببساطة على أنه main (main.innerHTML = "<p> بعض المحتوى الرائع <p />") بدون تحديد (البحث) أولاً عن العنصر باستخدام باستخدام "document.getElementById ()" أو طرق مشابهة. ومع ذلك ، فإن هذا النهج غير قياسي ولا ينصح باستخدامه ، لأنه من غير المعروف ما إذا كان سيتم دعمه في المستقبل ، على الرغم من أنني شخصياً أجد هذه الفرصة مريحة للغاية.



ميزة أخرى للوحدات النمطية الثابتة هي أنه لا يمكن استيرادها إلا مرة واحدة. سيتم تجاهل إعادة الاستيراد.



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



المضي قدما. دعنا نضيف بعض الأساليب البسيطة:



body {
  max-width: 768px;
  margin: 0 auto;
  color: #222;
  text-align: center;
}
canvas {
  display: block;
  margin: 1rem auto;
  border: 1px dashed #222;
  border-radius: 4px;
}
div {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}
section {
  padding: 1rem;
}
label {
  display: block;
}
input {
  margin: 0.25rem 0;
}
input:not([type="color"]) {
  width: 50px;
}
button {
  margin: 0.25rem auto;
  cursor: pointer;
}
ul {
  list-style: none;
}
li {
  margin: 0.5rem auto;
  width: 320px;
  border-bottom: 1px dotted #222;
}
p {
  margin: 0.25rem 0;
}


لا يوجد شيء مميز هنا. يمكنك إضافة الجمال إلى ذوقك.



دعنا ننتقل إلى الوحدات.



يحتوي ملف "canvas.js" على رمز الفصل لإنشاء لوحة الرسم وعرضها ، بالإضافة إلى قائمة بالرسائل المعروضة عند إنشاء شكل معين (تمثل هذه الرسائل معلومات حول منطقة ومحيط الشكل (التقليدي)):



// export default      
//           ,  IIFE (   ,    )
//   , ..  class ClassName...,  export default ClassName
export default class Canvas {
  //     :  ,   
  constructor(parent, width, height) {
    this.parent = parent;
    this.width = width;
    this.height = height;
    //   
    this.ctx = null;
    //   
    this.listEl = null;
    //        ,          
    this.clearCanvas = this.clearCanvas.bind(this);
  }

  //     
  createCanvas() {
    //      
    //  ,       
    //        
    if (this.ctx !== null) {
      console.log("Canvas already created!");
      return;
    } else {
      //   "canvas"
      const canvasEl = document.createElement("canvas");

      //         
      //        
      canvasEl.setAttribute("width", this.width);
      canvasEl.setAttribute("height", this.height);

      //     
      this.parent.append(canvasEl);

      //     
      this.ctx = canvasEl.getContext("2d");
    }

    //          
    return this;
  }

  //     
  //        
  createReportList() {
    if (this.listEl !== null) {
      console.log("Report list already created!");
      return;
    } else {
      const listEl = document.createElement("ul");
      this.parent.append(listEl);

      this.listEl = listEl;
    }

    return this;
  }

  //       
  clearCanvas() {
    this.ctx.clearRect(0, 0, this.width, this.height);
    this.listEl.innerHTML = "";
  }
}


يحتوي ملف "convert.js" على وظيفة لتحويل الدرجات إلى راديان:



//  
export const convert = (degrees) => (degrees * Math.PI) / 180;


كل ملف في دليل الأشكال هو وحدة نمطية لشكل معين. بشكل عام ، رمز هذه الوحدات متطابق ، باستثناء طرق الرسم ، وكذلك الصيغ لحساب مساحة الشكل ومحيطه. ضع في اعتبارك وحدة تحتوي على رمز لرسم دائرة (دائرة. js):



//    
//     
//       -      "Module"
import { convert } from "../helpers/convert.js";

//  
//         
export class Circle {
  //     "" 
  //     
  //    ,    ""  ctx  listEl
  constructor({ ctx, listEl, radius, x, y, color }) {
    this.ctx = ctx;
    this.listEl = listEl;
    this.radius = radius;
    this.x = x;
    this.y = y;
    this.color = color;
    //  
    this.name = "Circle";
    //   
    this.listItemEl = document.createElement("li");
  }

  //    
  draw() {
    //   
    this.ctx.fillStyle = this.color;
    //  
    this.ctx.beginPath();
    //  arc  6 :
    //     "x",     "y", ,  ,  
    // (      "0, 2 * Math.PI")
    //   ,    :     
    this.ctx.arc(this.x, this.y, this.radius, convert(0), convert(360));
    //  
    this.ctx.fill();
  }

  //        
  report() {
    // 
    this.listItemEl.innerHTML = `<p>${this.name} area is ${Math.round(Math.PI * (this.radius * this.radius))}px squared.</p>`;
    // 
    this.listItemEl.innerHTML += `<p>${this.name} circumference is ${Math.round(2 * Math.PI * this.radius)}px.</p>`;

    this.listEl.append(this.listItemEl);
  }
}


أخيرًا ، في ملف "main.js" ، يتم تنفيذ استيراد افتراضي ثابت لفئة الوحدة النمطية "Canvas" ، ويتم إنشاء مثيل من هذه الفئة ويتم التعامل مع ضغطات الأزرار ، والتي تتكون من الاستيراد الديناميكي لوحدة فئة الشكل المقابلة واستدعاء طرقها:



//   
//      
import Canvas from "./modules/canvas.js";

//   ,         
//     :
//   ,       
const { ctx, listEl, clearCanvas } = new Canvas(document.body, 400, 300).createCanvas().createReportList();

//    "" 
//    ,      
//      "async"
document.addEventListener("click", async (e) => {
  //     
  if (e.target.tagName !== "BUTTON") return;

  //     
  if (e.target.className === "draw_btn") {
    //   
    //  ,     
    //     
    const { btn: btnName } = e.target.dataset;

    //      
    //      -      
    const shapeName = `${btnName[0].toUpperCase()}${btnName.slice(1)}`;

    //     
    const shapeParams = {};

    //     
    const inputsEl = e.target.parentElement.querySelectorAll("input");

    //  
    inputsEl.forEach((input) => {
      //   
      //   
      const { prop } = input.dataset;
      //   
      //  ,   ,   
      const value = !isNaN(input.value) ? input.valueAsNumber : input.value;
      //      
      shapeParams[prop] = value;
    });
    //          
    shapeParams.ctx = ctx;
    shapeParams.listEl = listEl;

    console.log(shapeParams);

    //  
    //   "Module"
    const ShapeModule = await import(`./modules/shapes/${btnName}.js`);
    //      -  
    //    
    //             "Module" (  )       
    const shape = new ShapeModule[shapeName](shapeParams);

    //   
    shape.draw();
    //         
    shape.report();
  } else {
    //     
    //     "Canvas"
    clearCanvas();
  }
});


يمكنك اللعب بالرمز هنا .



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



أتمنى أن تجد شيئًا ممتعًا لنفسك. شكرآ لك على أهتمامك.



All Articles