لماذا لا أستخدم مكونات الويب

أكتب هذا بشكل أساسي لنفسي في المستقبل ، حتى يكون لدي مكان للإشارة إليه عندما يسألني أحدهم عن سبب شكوكي في مكونات الويب ولماذا لا يتم تجميع Svelte في مكونات الويب افتراضيًا. (ومع ذلك ، يمكن تجميعها في مكونات الويب ، وكذلك دمجها معهم ، كما يتضح من التقييم الممتاز على Custom Elements Everywhere ).


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


1. التحسين التدريجي


قد يكون هذا اعتقادًا قديمًا ، لكنني أعتقد أن مواقع الويب يجب أن تعمل بدون جافا سكريبت قدر الإمكان. مكونات الويب بدون JS لا تعمل. يعد هذا أمرًا طبيعيًا بالنسبة للأشياء التفاعلية بطبيعتها ، مثل عناصر النموذج المخصص (<cool-datepicker>) ، ولكنها ليست طبيعية للتنقل في الموقع ، على سبيل المثال. أو تخيل أحد مكونات <twitter-share> الذي يحوي منطق إنشاء عنوان URL لإرساله إلى Twitter . يمكنني تطبيقه على Svelte ، والذي يعرض HTML التالي لي على الخادم:


 <a target="_blank" noreferrer href="..." class="svelte-1jnfxx"> Tweet this </a> 

وبعبارة أخرى ، فإن <a> المعتاد في جميع روعته المتاحة.


عند تمكين JavaScript ، يحدث تحسن تدريجي - بدلاً من فتح علامة تبويب جديدة ، يتم فتح نافذة صغيرة منبثقة. ولكن حتى بدون JS ، المكون لا يزال يعمل بشكل جيد.


في حالة مكون ويب HTML ، سيبدو كالتالي:


 <twitter-share text="..." url="..." via="..."/> 

... غير مجدي وغير مناسب للاستخدام إذا تم حظر JS ، أو لسبب ما ، أو إذا كان المستخدم لديه متصفح قديم.


بالإضافة إلى ذلك ، توفر لنا class="svelte-1jnfxx" للنمط بدون Shadow DOM. الذي يقودنا إلى النقطة التالية.


2. CSS في ، اه ... JS


إذا كنت تريد استخدام Shadow DOM لتغليف الأنماط ، فستحتاج إلى إدراج CSS في علامة <style> . الطريقة العملية الوحيدة للقيام بذلك ، إذا كنت ترغب في تجنب تحميل وميض المحتوى (FOUC) ، هي تضمين CSS كسلسلة في JavaScript تحدد باقي منطق مكون الويب الخاص بك.


هذا يتناقض مع نصيحة تحسين الأداء التي تنص على: "أقل جافا سكريبت ، من فضلك." تم انتقاد مجتمع CSS-in-JS ، على وجه الخصوص ، كثيرًا لعدم استخدام ملفات CSS لـ CSS ، وهنا نحن مرة أخرى مع مكونات الويب.


في المستقبل ، سنكون قادرين على استخدام CSS Modules وكذلك Constructable Styless للتعامل مع هذه المشكلة. سيكون لدينا أيضًا فرصة لتصميم ::part الداخلية لـ Shadow DOM من خلال ::theme و ::part . ولكن هنا لم يكن الأمر بدون مشاكل.


3. منصة التعب



هذا التاج مؤلم بالنسبة لي - لقد أعلنت عن هذه الأشياء باسم "المستقبل" لعدة سنوات ، ولكن من أجل مواكبة الوقت الحاضر ، كان علينا ملء المنصة بمجموعة من الميزات المختلفة ، مما أدى إلى تفاقم الفجوة بين المتصفحات.

في وقت كتابة هذا التقرير ، على https://crbug.com ، متتبع علة Chrome ، 61000 خطأ مفتوح يُظهر التعقيد الهائل لكتابة متصفح حديث.


في كل مرة نضيف فيها ميزة جديدة إلى النظام الأساسي ، فإننا نزيد من التعقيد - نخلق احتمالية حدوث أخطاء جديدة وتجعل من المحتمل أن يكون لدى Chrome منافس جديد. كما أنه يخلق صعوبات للمطورين الذين يتم تشجيعهم على تعلم هذه الميزات الجديدة (بعضها ، مثل HTML Imports أو الإصدار الأصلي من Custom Custom standard ، لم يتخذ الجذر خارج Google وهو الآن في مرحلة الإزالة).


4. Polphhiles


حقيقة أنك تحتاج إلى استخدام polyfiles لدعم المتصفحات القديمة لا تسهم في تطور الموقف. وهو لا يساعد على الإطلاق في أن المقالات الموجودة على صفحات الأنماط القابلة للتشكيل المكتوبة في Google (مرحبًا يا جايسون!) لا تذكر أن هذه الميزة متوفرة فقط في Chrome. (يعمل جميع مؤلفي المواصفات الثلاثة لـ Google. يبدو أن Webkit لديهم شكوك حول بعض جوانب هذا المعيار).


5. التكوين


قد يكون من المفيد التحكم في وقت الحاجة إلى عرض محتويات الفتحة. تخيل أن لديك <html-include> لتحميل بعض المحتويات الإضافية عندما يكون مرئيًا:


 <p>Toggle the section for more info:</p> <toggled-section> <html-include src="./more-info.html"/> </toggled-section> 

فجأة! حتى لو لم toggled-section حتى الآن ، إلا أن المستعرض طلب بالفعل more-info.html ، جنبًا إلى جنب مع جميع الصور والموارد الأخرى الموجودة هناك.


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


لسوء الحظ ، كان هذا أحد الخصائص الأساسية لـ DOM. الذي يقودنا إلى ...


6. الخلط بين الخصائص والسمات


الخصائص والسمات هي في الأساس نفس الشيء ، أليس كذلك؟


 const button = document.createElement('button'); button.hasAttribute('disabled'); // false button.disabled = true; button.hasAttribute('disabled'); // true button.removeAttribute('disabled'); button.disabled; // false 

حسنا ، تقريبا:


 typeof button.disabled; // 'boolean' typeof button.getAttribute('disabled'); // 'object' button.disabled = true; typeof button.getAttribute('disabled'); // 'string' 

هناك أسماء لا تتطابق:


 div = document.createElement('div'); div.setAttribute('class', 'one'); div.className; // 'one' div.className = 'two'; div.getAttribute('class'); // 'two' 

... وهناك تلك التي لم يتم الاتفاق عليها على الإطلاق:


 input = document.createElement('input'); input.getAttribute('value'); // null input.value = 'one'; input.getAttribute('value'); // null input.setAttribute('value', 'two'); input.value; // 'one' 

ولكن يمكننا التعامل مع هذه المراوغات وتفاعل تنسيق السلسلة (HTML) و DOM. يوجد عدد محدود من هذه الميزات ، وهي موثقة ، حتى يمكننا التعرف عليها على الأقل ، إذا كان لدينا الوقت والصبر.


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


 class MyThing extends HTMLElement { static get observedAttributes() { return ['foo', 'bar', 'baz']; } get foo() { return this.getAttribute('foo'); } set foo(value) { this.setAttribute('foo', value); } get bar() { return this.getAttribute('bar'); } set bar(value) { this.setAttribute('bar', value); } get baz() { return this.hasAttribute('baz'); } set baz(value) { if (value) { this.setAttribute('baz', ''); } else { this.removeAttribute('baz'); } } attributeChangedCallback(name, oldValue, newValue) { if (name === 'foo') { // ... } if (name === 'bar') { // ... } if (name === 'baz') { // ... } } } 

يمكنك القيام بالعكس - استدعاء attributeChangedCallback للألعاب والمستوطنين. في أي حال ، فإن الراحة في العمل معها محبطة للغاية. في الوقت نفسه ، هناك طريقة بسيطة لا لبس فيها في الأطر لنقل البيانات إلى أحد المكونات.


7. تسرب التصميم


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


 const element = document.querySelector('my-thing'); element.attributeChangedCallback('w', 't', 'f'); 

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


8. سيئة دوم


حسنا ، لقد أثبتنا بالفعل أن DOM سيئة. ولكن لا يزال من الصعب المبالغة في مدى عدم ملائمة إنشاء تطبيقات تفاعلية.


قبل بضعة أشهر ، كتبت مقالًا ، "اكتب رمزًا أقل" ، لتوضيح كيف يمكن لـ Svelte كتابة المكونات بشكل أكثر كفاءة من الأطر مثل React و Vue. لم يكن هناك مقارنة مع الفانيليا دوم ، ولكن ينبغي. باختصار ، لدينا مكون بسيط <Adder a={1} b={2}/> :


 <script> export let a; export let b; </script> <input type="number" bind:value={a}> <input type="number" bind:value={b}> <p>{a} + {b} = {a + b}</p> 

هذا كل شيء. اكتب الآن نفس الشيء من خلال مكون الويب:


 class Adder extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }); this.shadowRoot.innerHTML = ` <input type="number"> <input type="number"> <p></p> `; this.inputs = this.shadowRoot.querySelectorAll('input'); this.p = this.shadowRoot.querySelector('p'); this.update(); this.inputs[0].addEventListener('input', e => { this.a = +e.target.value; }); this.inputs[1].addEventListener('input', e => { this.b = +e.target.value; }); } static get observedAttributes() { return ['a', 'b']; } get a() { return +this.getAttribute('a'); } set a(value) { this.setAttribute('a', value); } get b() { return +this.getAttribute('b'); } set b(value) { this.setAttribute('b', value); } attributeChangedCallback() { this.update(); } update() { this.inputs[0].value = this.a; this.inputs[1].value = this.b; this.p.textContent = `${this.a} + ${this.b} = ${this.a + this.b}`; } } customElements.define('my-adder', Adder); 

نعم.


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


9. الأسماء العالمية


لن أركز على هذا لفترة طويلة ؛ يكفي أن أقول إن مخاطر العمل في مساحة اسم واحدة مشتركة معروفة منذ وقت طويل وتم تفكيكها.


10. كل هذه المشاكل تم حلها بالفعل.


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


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

Source: https://habr.com/ru/post/ar457010/


All Articles