عندما يتعلق الأمر بمكونات الويب ، غالبًا ما يقولون: "ماذا تريد بدون أطر؟ كل شيء جاهز هناك. " في الواقع ، هناك أطر عمل تم إنشاؤها على أساس تطبيق المعايير المدرجة في مجموعة مكونات الويب. هناك حتى جيدة نسبيا مثل علامة
X- . ولكن اليوم ، ما زلنا نفهم إلى أي مدى أصبحت واجهة برمجة تطبيقات المتصفح بسيطة وأنيقة وقوية لحل مهام التطوير اليومية ، بما في ذلك تنظيم تفاعل المكونات مع بعضها البعض ومع الكائنات الأخرى من سياق تنفيذ المتصفح ، ويمكنك دائمًا استخدام أطر عمل مع مكونات الويب ، حتى تلك التي تم تطويرها عبر المعايير ، بما في ذلك من خلال الآليات التي سنقوم بتحليلها اليوم ، على الأقل كما هو موضح
في الموقع .
علمتنا قرارات مثل الكاشف أنه يجب أن يكون هناك جانب واحد كبير للبيانات والمكونات الموقعة من أجل تغييراته ، وفي مكونات الويب ، يحاولون أيضًا تمييز عدم وجود مثل هذا النظام الفرعي ، في الواقع ، مما يعني ضمنيًا عدم التراجع العكسي وهو موجود بالفعل في مكونات الويب.
كل عنصر يحتوي على سمات القيمة التي يمكننا تغييرها. وإذا أدرجت الأسماء في خطاف obsAttributes ، عند تغييرها ، سيتم استدعاء
attributeChangedCallback () تلقائيًا بحيث يمكننا تحديد سلوك المكون عند تغيير السمة. باستخدام getter magic ، من السهل إجراء الربط العكسي بطريقة مماثلة.
لقد قمنا بالفعل برسم بعض المشاريع في
الجزء الأول واليوم سنواصل خفضه أكثر.
تجدر الإشارة على الفور إلى أحد القيود ، على الأقل في التنفيذ الحالي ، أن قيمة السمة يمكن أن تكون فقط قيمة بدائية محددة بواسطة حرفي وقابل للاختزال لسلسلة ، ولكن بشكل عام يمكن نقل "الكائنات" بهذه الطريقة باستخدام علامات اقتباس مفردة من الخارج ومضاعفة التحديد قيم وحقول المشروع.
<my-component my='{"foo": "bar"}'></my-component>
لاستخدام هذه القيمة في التعليمات البرمجية ، يمكنك تطبيق أداة الحصول على
السحر التلقائي التي
ستستدعي JSON.parse () عليها .
لكن في الوقت الحالي ، القيمة العددية للعداد تكفي لنا.
نضيف عددًا جديدًا من السمات
إلى العنصر الخاص بنا ، ونشير إليه كما هو ملاحظ ، وانقر فوق معالج النقر لزيادة هذا العداد ، ونضيف تحديث القيمة المعروضة عبر رابط مباشر إلى رابط التغيير ، حيث يتم تطبيق منطقه في طريقة تحديث
قابلة لإعادة الاستخدام منفصلة
(LabLable) .
export class MyWebComp extends HTMLElement { constructor() { super(); } connectedCallback() { let html = document.importNode(myWebCompTemplate.content, true); this.attachShadow({mode: 'open'}); this.shadowRoot.appendChild(html); this.updateLabel(); } updateLabel() { this.shadowRoot.querySelector('#helloLabel').textContent = 'Hello ' + this.getAttribute('greet-name') + ' ' + this.getAttribute('count'); } static get observedAttributes() { return ['count']; } attributeChangedCallback(name, oldValue, newValue) { if (name === 'count') { this.updateLabel(); } } showMessage(event) { this.setAttribute('count', this.getAttribute('count') + 1); } }

تلقى كل عنصر عداد مستقل يتم تحديثه تلقائيًا.
الواجب المنزلي: تنفيذ تحويل العداد إلى رقم واستخدامه من خلال الحصول على السيارات ؛)
بالطبع ، خيارات أكثر تقدما ممكنة. على سبيل المثال ، إذا لم تأخذ بعين الاعتبار عمليات الاسترجاعات والأحداث البسيطة ، باستخدام الوكيل الأصلي ونفس الحروف والأدوات ، فيمكنك تنفيذ وظيفة الربط العكسي.
أضف ملف
my-counter.js بفئة من هذا النوع
export class MyCounter extends EventTarget { constructor() { super(); this.count = 0; } increment() { this.count++; this.dispatchEvent(new CustomEvent('countChanged', { detail: { count: this.count } })); } }
لقد ورثنا فئة من
EventTarget بحيث يمكن للفئات الأخرى الاشتراك في الأحداث التي
تطرحها كائنات هذه الفئة وتحديد خاصية العد التي ستخزن قيمة العداد.
الآن قم بإضافة مثيل هذه الفئة كخاصية ثابتة للمكون.
<script type="module"> import { MyWebComp } from "./my-webcomp.js"; import { MyCounter } from "./my-counter.js"; let counter = new MyCounter(); Object.defineProperty(MyWebComp.prototype, 'counter', { value: counter }); customElements.define('my-webcomp', MyWebComp); </script>
وفي رمز المكون ،
سنشترك في تغيير قيمة طريقة تحديث التسمية
updateLabel () ، والتي نضيف إليها عرض القيمة من العداد المشترك العالمي. وفي معالج النقر ، مكالمة مباشرة إلى الطريقة الإضافية.
export class MyWebComp extends HTMLElement { constructor() { super(); } connectedCallback() { let html = document.importNode(myWebCompTemplate.content, true); this.attachShadow({mode: 'open'}); this.shadowRoot.appendChild(html); this.updateLabel(); this.counter.addEventListener('countChanged', this.updateLabel.bind(this)); } updateLabel() { this.shadowRoot.querySelector('#helloLabel').textContent = 'Hello ' + this.getAttribute('greet-name') + ' ' + this.getAttribute('count') + ' ' + this.counter.count; } static get observedAttributes() { return ['count']; } attributeChangedCallback(name, oldValue, newValue) { if (name === 'count') { if (this.shadowRoot) { this.updateLabel(); } } } showMessage(event) { this.setAttribute('count', parseInt(this.getAttribute('count')) + 1); this.counter.increment(); } }
على الرغم من أن كل عنصر حصل على عدادين ، سيتم زيادة كل قيمة من قيمته بشكل مستقل ، لكن قيمة العداد المشترك ستكون هي نفسها لكلا العنصرين ، وسيتم تحديد القيم الفردية بعدد النقرات عليها فقط.

وبالتالي ، حصلنا على ارتباط مباشر بتجليد مباشر ، وبسبب استخدام الأحداث ، هذه القيمة ضعيفة في التحديث. بالطبع ، لا يوجد شيء يمنع تنفيذ الزيادة من خلال الأحداث ، من خلال ربط طريقة
الزيادة () بمنظر الحدث:
export class MyCounter extends EventTarget { constructor() { super(); this.count = 0; this.addEventListener('increment', this.increment.bind(this)); } increment() { this.count++; this.dispatchEvent(new CustomEvent('countChanged', { detail: { count: this.count } })); } }
واستبدال استدعاء الأسلوب برمي الحدث:
export class MyWebComp extends HTMLElement { ... showMessage(event) { this.setAttribute('count', parseInt(this.getAttribute('count')) + 1); this.counter.dispatchEvent(new CustomEvent('increment')); } }
ماذا هذا التغيير؟ الآن ، إذا تمت إزالة أو تغيير طريقة
الزيادة () أثناء التطوير ، فسيتم انتهاك صحة التعليمات البرمجية الخاصة بنا ، ولكن لن تكون هناك أخطاء في الترجمة الشفهية ، أي سوف تبقى قابلية التشغيل. هذه الخاصية تسمى ضعف الاتصال.
في التطوير ، يلزم الربط المباشر والضعيف ، عادة ما يكون من المعتاد تنفيذ الربط المباشر داخل منطق وحدة المكون الواحد ، والاقتران الضعيف بين الوحدات النمطية والمكونات المختلفة من أجل توفير المرونة وقابلية التمدد للنظام بأكمله. تشير دلالات HTML إلى مستوى عالٍ من هذه المرونة ، أي التفضيل لضعف الاتصال ، ولكن التطبيق سيعمل بشكل أكثر موثوقية إذا كانت الاتصالات داخل المكونات مباشرة.
يمكننا تعليق المعالجات بالطريقة القديمة واستدعاء أحداث على كائن
المستند العمومي.
export class MyWebComp extends HTMLElement { constructor() { super(); } connectedCallback() { let html = document.importNode(myWebCompTemplate.content, true); this.attachShadow({mode: 'open'}); this.shadowRoot.appendChild(html); this.updateLabel(); this.counter.addEventListener('countChanged', this.updateLabel.bind(this)); document.addEventListener('countChanged', this.updateLabel.bind(this)); } disconnectedCallback() { document.removeEventListener(new CustomEvent('increment')); document.removeEventListener(new CustomEvent('countChanged')); } ... showMessage(event) { this.setAttribute('count', parseInt(this.getAttribute('count')) + 1); this.counter.dispatchEvent(new CustomEvent('increment')); document.dispatchEvent(new CustomEvent('increment')); } }
export class MyCounter extends EventTarget { constructor() { super(); this.count = 0; this.addEventListener('increment', this.increment.bind(this)); document.addEventListener('increment', this.increment.bind(this)); } increment() { this.count++; this.dispatchEvent(new CustomEvent('countChanged', { detail: { count: this.count } })); document.dispatchEvent(new CustomEvent('countChanged', { detail: { count: this.count } })); } }
لا يختلف السلوك عن ذلك ، لكننا نحتاج الآن إلى التفكير في إزالة معالج الأحداث من الكائن العمومي إذا تمت إزالة العنصر نفسه من الشجرة. لحسن الحظ ، لا تزودنا مكونات الويب بتصميم الخطافات فحسب ، بل توفر أيضًا الدمار ، وتجنب تسرب الذاكرة.
هناك أيضًا نقطة واحدة مهمة: يمكن لأحداث عناصر الشجرة أن تتفاعل مع بعضها البعض من خلال آلية تسمى "الفقاعات".
عندما ينقر المستخدم على عنصرنا ، يبدأ الحدث ، بعد أن عمل على العنصر نفسه ، في استدعاء الوالد مثل الدوائر على الماء ، ثم على الوالد من هذا الوالد ، وهكذا إلى عنصر الجذر.
في أي نقطة في سلسلة الاتصال هذه ، يمكن اعتراض الحدث ومعالجته.
هذا ، بالطبع ، ليس هو نفس الحدث تمامًا ، ومشتقاته وسياقاته ، على الرغم من أنها ستحتوي على روابط مع بعضها كما هو الحال في
event.path ، لن تتزامن تمامًا.
ومع ذلك ، تسمح لك هذه الآلية بربط العناصر الفرعية بوالديهم بطريقة لا تنتهك هذا الرابط الأنماط الهندسية ، أي دون روابط مباشرة ، ولكن فقط بسبب سلوكهم.
في العصور القديمة ، تسبب هذا أيضًا في الكثير من المتاعب ، عندما تسببت أحداث بعض العناصر في "دوائر" أو أصداء في بعض أقسام المستند كانت غير مرغوب فيها لردود الفعل.
طوال سنوات ، ناضل مطورو الويب مع "الدعاية" (نشر أو تطفو على السطح) للأحداث وأوقفوها وقلبوها قدر استطاعتهم ، ولكن لاستخدامهم المريح ، كما هو الحال مع المعرفات ، كان هناك فقط عزل لشجرة الظل.
السحر هنا هو أن الأحداث لا تنهب خارج شجرة الظل. ولكن مع ذلك ، يمكنك التقاط الحدث الذي يرميه أطفاله في المكون المضيف وطرحه في الشجرة في شكل معنى حدث آخر ، أو معالجته تمامًا.
لفهم كيف يعمل كل شيء ، نحن نلف عناصر
Webcomp الخاصة بنا في حاوية مثل هذا:
<my-webcont count=0> <my-webcomp id="myWebComp" greet-name="John" count=0 onclick="this.showMessage(event)"></my-webcomp> <my-webcomp id="myWebComp2" greet-name="Josh" count=0 onclick="this.showMessage(event)"></my-webcomp> </my-webcont>
سيكون للحاوية هذا الرمز:
export class MyWebCont extends HTMLElement { constructor() { super(); } connectedCallback() { this.addEventListener('increment', this.updateCount.bind(this)); } updateCount(event) { this.setAttribute('count', parseInt(this.getAttribute('count')) + 1); } static get observedAttributes() { return ['count']; } attributeChangedCallback(name, oldValue, newValue) { if (name === 'count') { this.querySelectorAll('my-webcomp').forEach((el) => { el.setAttribute('count', newValue); }); } } }
في
connectCallback () ، سنعلق المعالج على حدث
الزيادة ، والذي سوف يرمي الأطفال. سيزيد المعالج العداد الخاص بالعنصر ، وسيستمر رد الاتصال لتغيير قيمته من خلال جميع العناصر الفرعية وزيادة العدادات الخاصة بهم ، والتي يتعطل عليها المعالجون الذين سبق أن طورناهم.
سيتغير رمز العناصر الفرعية بشكل طفيف ، في الواقع ، كل ما نحتاجه هو أن يقوم الحدث الزائد برمي العنصر نفسه وليس مجاميعه ، والقيام بذلك باستخدام
الفقاعات: السمة
الحقيقية .
export class MyWebComp extends HTMLElement { ... showMessage(event) { this.setAttribute('count', parseInt(this.getAttribute('count')) + 1); this.counter.dispatchEvent(new CustomEvent('increment')); this.dispatchEvent(new CustomEvent('increment', { bubbles: true })); document.dispatchEvent(new CustomEvent('increment')); } }

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