مكونات الويب بدون Shadow DOM

مرحبا يا هابر!

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

لماذا؟


الفكرة الرئيسية التي أريد أن أنقل هذه المرة هو أن مكونات ويب وDOM الظل هي لا نفس الشيء. عند استخدام Shadow DOM ، تحصل على ميزتين رئيسيتين:

  • قسم معزول من المستند تشعر فيه بأنماطك آمنة من التأثيرات الخارجية و "التسريبات"
  • آلية تركيبية تسمح لك بتقسيم الوثيقة إلى ما هو هيكل المكون نفسه ومحتوياته (أحفاد عنصر DOM في الشجرة)

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

const MY_CSS = {
  title: 'color: #00f; font-size: 2em',
  item: 'color: #f00; font-size: 1.2em',
};

const DATA = [
  {text: 'Text 1'},
  {text: 'Text 2'},
  {text: 'Text 3'},
];

let template = document.createElement('template');
template.innerHTML = /*html*/ `
<div style="${MY_CSS_.title}">List items:</div>
<div class="my-list">
  ${DATA.map(item => /*html*/ `<div style="${MY_CSS.item}">${item.text}</div>`).join('')}
</div>
`;

class ShadowlessComponent extends HTMLElement {
  constructor() {
    super();
    this._contents = new DocumentFragment();
    this._contents.appendChild(template.content.cloneNode(true));
  }
  connectedCallback() {
    this.appendChild(this._contents);
  }
}

window.customElements.define('shadowless-component', ShadowlessComponent);

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

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

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

في المجموع ، باستخدام علامة مخصصة في ترميزنا shadowless-component، نحصل على النتيجة التالية في المتصفح:

<shadowless-component>
  <div id="caption" style="color: #00f; font-size: 2em">List items:</div>
    <div class="my-list">
      <div style="color: #f00; font-size: 1.2em">Text 1</div>
      <div style="color: #f00; font-size: 1.2em">Text 2</div>
      <div style="color: #f00; font-size: 1.2em">Text 3</div>
    </div>
</shadowless-component>

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

العرض: المحتويات


مكونات الويب هي العقد الكاملة لـ DOM الخاص بك. هذا يعني ، بالإضافة إلى حقيقة أن جميع الطرق القياسية لعناصر DOM متاحة لك ، وأن المكون الخاص بك هو دائمًا نوع من الحاويات. بمعنى ، إذا كنت ترغب في إضافة بنية عشوائية للعناصر إلى DOM باستخدام مكون الويب ، فستكون جميعًا أحفادًا للمكون الخاص بك ، وهو ليس مناسبًا دائمًا. في مثل هذه الحالات ، يمكنك استخدام قاعدة CSS الجديدة - عرض: المحتويات . دعم المتصفح: caniuse.com/#feat=css-display-contents

بشكل افتراضي ، تحتوي جميع المكونات على العرض: خاصية مضمنة .

قليلا من الأذى


ولكن ماذا لو كنا لا نريد أي حاويات إضافية وعلامات مخصصة على الإطلاق؟ أعط HTML خالص!

حسنا:

  constructor() {
    super();
    this._contents = new DocumentFragment();
    this._contents.appendChild(template.content.cloneNode(true));
    this._titleEl = this._contents.querySelector('#caption');
    window.setInterval(() => {
      this._titleEl.textContent = Date.now();
    }, 1000);
  }
  connectedCallback() {
    this.parentNode.prepend(this._contents, this);
    this.remove();
  }

ونتيجة لذلك ، نحصل على هذا:

<div id="caption" style="color: #00f; font-size: 2em">1581075598392</div>
<div class="my-list">
  <div style="color: #f00; font-size: 1.2em">Text 1</div>
  <div style="color: #f00; font-size: 1.2em">Text 2</div>
  <div style="color: #f00; font-size: 1.2em">Text 3</div>
</div>

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

مكونات CSS


وفقًا للمعيار ، يجب تسمية العلامات المخصصة بالإضافة الإلزامية لحرف "-". إذا كنت تستخدم علامتك في الترميز ، ولكن في الوقت نفسه لا تنشئ أي مكون في JS وأضف منشئه إلى سجل المكونات ، فإن المستعرض يعتبر علامتك "عنصر غير معروف" ( HTMLUnknownElement ). بشكل افتراضي ، تتشابه هذه العناصر في السلوك مع علامة الامتداد. يمكن استخدام هذا إذا كنت بحاجة إلى إنشاء مكوِّن غبي بسيط ببنية بسيطة تكفي لها تعبيرات CSS Rules :: before و :: after و attr () . مثال:

  my-container {
    display: block;
    padding: 10px;
    border: 1px solid currentColor;
  }
  my-container::before {
    content: attr(caption);
    margin-bottom: .6em;
  }

الاستخدام في الترميز:

<my-container caption=""></my-container>

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


All Articles