في مرحلة ما كان عليّ أن أتعرف بشكل عاجل على مكونات الويب وإيجاد طريقة لتطوير استخدامها بسهولة. أخطط لكتابة سلسلة من المقالات التي من شأنها أن
تنظيم بطريقة ما المعرفة بمكونات الويب ، وإضاءة العنصر وإعطاء مقدمة موجزة لهذه التكنولوجيا للآخرين. أنا لست خبيراً في هذه التكنولوجيا وسأقبل بكل سرور أي ملاحظات.
العنصر المضاء عبارة عن غلاف (قالب أساسي) لمكونات الويب الأصلية. انها تنفذ العديد من الطرق المريحة التي ليست في المواصفات. نظرًا لقربها من التطبيق الأصلي ، يُظهر العنصر المضاء نتائج جيدة جدًا في معايير مختلفة مقارنةً بالطرق الأخرى (اعتبارًا من 02/06/2019).
المكافآت التي أراها من استخدام عنصر الإضاءة كفئة أساسية لمكونات الويب:
- تطبق هذه التقنية بالفعل الإصدار الثاني و "مرضت بأمراض الطفولة" ، والتي تعتبر غريبة على الأدوات التي ظهرت للتو.
- يمكن تنفيذ التجميع باستخدام كل من polymer ، و webpack ، و typescript ، و rollup ، وما إلى ذلك ، مما يتيح لك تضمين العنصر المضاء في أي مشروع حديث دون أي مشاكل.
- يحتوي العنصر المضاء على نظام ملائم للغاية للعمل مع الخاصية من حيث كتابة القيم وبدءها وتحويلها.
- ينفذ العنصر المضاء نفس منطق رد الفعل تقريبًا ، أي أنه يوفر الحد الأدنى - قالب واحد لبناء المكونات وتقديمها ولا يحد المطور في اختيار نظام بيئي ومكتبات إضافية.
إنشاء مكون ويب بسيط على عنصر مضاءة. دعنا ننتقل إلى الوثائق. نحن بحاجة إلى ما يلي:
- أضف حزمة npm مع العنصر المضاء إلى مجموعتنا
npm install --save lit-element
- إنشاء مكون لدينا.
على سبيل المثال ، نحتاج إلى إنشاء مكون ويب تمت تهيئته في علامة my-component
. للقيام بذلك ، قم بإنشاء ملف js my-component.js
وحدد القالب الأساسي الخاص به:
أولاً ، نستورد قالبنا الأساسي:
import { LitElement, html } from 'lit-element';
ثانياً ، قم بإنشاء مكون الويب نفسه باستخدام LitElement
وآخر شيء هو تسجيل مكون الويب في المتصفح
customElements.define('my-component', MyComponent);
نتيجة لذلك ، حصلنا على ما يلي:
import { LitElement, html } from 'lit-element'; class MyComponent extends LitElement { render() { return html`<p>Hello World!</p>` } } customElements.define('my-component', MyComponent);
إذا استبعدت الحاجة إلى توصيل my-component.js
بـ html ، فهذا كل شيء. أبسط عنصر جاهز.
أقترح عدم إعادة اختراع العجلة وأخذ التجميع النهائي للإضاءة المتراكمة. اتبع التعليمات:
git clone https://github.com/PolymerLabs/lit-element-build-rollup.git cd lit-element-build-rollup npm install npm run build npm run start
بعد الانتهاء من جميع الأوامر ، نذهب إلى الصفحة في المتصفح http: // localhost: 5000 / .
إذا ألقينا نظرة على html ، فسنرى أن webcomponents-loader.js أمام العلامة الختامية. هذه هي مجموعة من polyfills لمكونات الويب ، ومن أجل التشغيل عبر متصفح مكون الويب ، من المستحسن وجود هذا polyfill. دعونا نلقي نظرة على جدول المتصفحات التي تنفذ جميع معايير العمل مع مكونات الويب ، فهي تقول أن EDGE لا تزال لا تنفذ المعايير بالكامل (أنا صامت حول IE11 ، الذي لا يزال مطلوبًا لدعمه).

نفذت 2 خيارات لهذا polyfill:
- webcomponents-bundle.js - يحتوي هذا الإصدار على جميع الخيارات الممكنة لـ polyfill ، وكلها بدأت ، ولكن كل polyfill سيعمل فقط على أساس العلامات المكتشفة.
- webcomponents-loader.js هو الحد الأدنى من أداة تحميل التشغيل التي تقوم ، بناءً على الأعراض المكتشفة ، بتحميل ملفات polyfills اللازمة
أطلب منك أيضًا الانتباه إلى ملف polyfill آخر - custom-elements-es5-adapter.js . وفقًا للمواصفات ، يمكن إضافة فئات ES6 فقط إلى customElements.define الأصلي. للحصول على أفضل أداء ، يجب تمرير رمز ES6 فقط إلى المتصفحات التي تدعمه ، و ES5 إلى أي شخص آخر. لا يمكن القيام بذلك دائمًا ، لذلك ، من أجل توافق أفضل بين المستعرضات ، يوصى بتحويل كل شفرة ES6 إلى ES5. ولكن في هذه الحالة ، لن تتمكن مكونات الويب على ES5 من العمل في المتصفحات. لحل هذه المشكلة ، هناك custom-elements-es5-adapter.js.
الآن ./src/my-element.js
ملف ./src/my-element.js
import {html, LitElement, property} from 'lit-element'; class MyElement extends LitElement {
يمكن لمشغل القالب lit-html معالجة سلسلة مختلفة. سأقدم لك عدة خيارات:
نصائح لتحسين وظيفة التجسيد ():
- يجب ألا يغير حالة العنصر ،
- لا ينبغي أن يكون لها آثار جانبية ،
- يجب أن يعتمد فقط على خصائص العنصر ،
- يجب أن ترجع نفس النتيجة عند إرسال نفس القيم.
لا تقم بتحديث DOM خارج وظيفة التقديم ().
Lit-html مسؤول عن تقديم العنصر المضاء - هذه طريقة تعريفية لوصف كيفية عرض مكون الويب. تضمن lit-html التحديثات السريعة من خلال تغيير أجزاء DOM التي تحتاج إلى تغيير فقط.
كانت كل هذه الشفرة تقريبًا في مثال بسيط ، ولكن تمت إضافةproperty myProp
للخاصية myProp
. يشير هذا الديكور إلى أننا نتوقع سمة تسمى myprop
في myprop
. إذا لم يتم تعيين مثل هذه السمة ، يتم ضبط قيمة السلسلة على العناصر افتراضيًا.
<my-element></my-element> <my-element myprop="else"></my-element>
يوفر العنصر المضاء طريقتين للعمل مع property
:
- من خلال الديكور.
- عبر
properties
جامع ثابت.
يتيح الخيار الأول تحديد كل خاصية على حدة:
@property({type: String}) prop1 = ''; @property({type: Number}) prop2 = 0; @property({type: Boolean}) prop3 = false; @property({type: Array}) prop4 = []; @property({type: Object}) prop5 = {};
والثاني هو تحديد كل شيء في مكان واحد ، ولكن في هذه الحالة ، إذا كانت الخاصية لها قيمة افتراضية ، فيجب كتابتها في طريقة مُنشئ الفصل:
static get properties() { return { prop1: {type: String}, prop2: {type: Number}, prop3: {type: Boolean}, prop4: {type: Array}, prop5: {type: Object} }; } constructor() { this.prop1 = ''; this.prop2 = 0; this.prop3 = false; this.prop4 = []; this.prop5 = {}; }
واجهة برمجة التطبيقات (API) للتعامل مع الخصائص في العنصر المضاء واسعة جدًا:
- السمة : ما إذا كانت الخاصية يمكن أن تصبح سمة يمكن ملاحظتها. إذا كانت
false
، فسيتم استبعاد السمة من الملاحظة ، ولن يتم إنشاء أي إحضار لها. إذا كان true
أو attribute
مفقودة ، فإن الخاصية المحددة في getter بتنسيق lowerCamelCase ستتوافق مع السمة بتنسيق السلسلة. إذا تم تحديد سلسلة ، على سبيل المثال my-prop
، فسوف تتوافق مع نفس الاسم في السمات. - محول : يحتوي على وصف لكيفية تحويل قيمة من / إلى سمة / خاصية. يمكن أن تكون القيمة دالة تعمل على إجراء تسلسل للقيمة
fromAttribute
تسلسلها ، أو يمكن أن تكون كائنًا fromAttribute
و toAttribute
، وتحتوي هذه المفاتيح على وظائف منفصلة لتحويل القيم. بشكل افتراضي ، تحتوي الخاصية على تحويل لأنواع الأساس Boolean
و String
و Number
و Object
و Array
. قواعد التحويل مدرجة هنا . - النوع : يشير إلى أحد الأنواع الأساسية التي ستحتوي عليها هذه الخاصية. يتم استخدامه كـ "تلميح" للمحول حول نوع الخاصية التي يجب أن تحتوي عليها.
- انعكاس : يشير إلى ما إذا كان يجب ربط السمة بالخاصية (
true
) وتغييرها وفقًا للقواعد من type
converter
. - hasChanged : تحتوي كل خاصية على ، تحتوي على دالة تحدد ما إذا كان هناك تغيير بين القيمة القديمة والقيمة الجديدة ، على التوالي تقوم بإرجاع قيمة
Boolean
. إذا كان هذا true
، يبدأ في تحديث العنصر. - noAccessor : تقبل هذه الخاصية
Boolean
وتفترض أنها false
. يحظر إنشاء مجموعات من المستوطنين والأكشاك لكل خاصية للوصول إليها من الفصل. هذا لا يلغي التحويل.
فلنقدم مثالًا افتراضيًا: اكتب أحد مكونات الويب التي تحتوي على معلمة تحتوي على سلسلة ، ويجب رسم هذه الكلمة على الشاشة ، حيث يكون كل حرف أكبر من الحرف السابق.
<ladder-of-letters letters=""></ladder-of-letters>
في النهاية نحصل على:

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

واستخدام reflect
يمكننا أن نرى أيضا تغييرات HTML

إذا غيرت هذه السمة برمز خارج مكون الويب هذا ، فسوف نتسبب أيضًا في إعادة رسم مكون الويب.
الآن النظر في تصميم المكون. لدينا طريقتان لنمط عنصر الإضاءة:
- التصميم عن طريق إضافة علامة نمط إلى طريقة التجسيد
render() { return html` <style> p { color: green; } </style> <p>Hello World</p> `; }

- عبر
styles
جامع ثابت
import {html, LitElement, css} from 'lit-element'; class MyElement extends LitElement { static get styles() { return [ css` p { color: red; } ` ]; } render() { return html` <p>Hello World</p> `; } } customElements.define('my-element', MyElement);
نتيجة لذلك ، حصلنا على أنه لم يتم إنشاء علامة مع الأنماط ، ولكن تمت كتابتها ( >= Chrome 73
) في Shadow DOM
للعنصر وفقًا للمواصفات . هذا يحسن الأداء مع عدد كبير من العناصر ، لأن عند تسجيل مكوّن جديد ، يعرف بالفعل الخصائص التي تحددها أنماطه ؛ فهي لا تحتاج إلى التسجيل في كل مرة وإعادة فرز الأصوات.

علاوة على ذلك ، إذا كانت هذه المواصفات غير مدعومة ، فسيتم إنشاء علامة style
عادية في المكون.

بالإضافة إلى ذلك ، لا تنس أنه بهذه الطريقة يمكننا أيضًا فصل أي الأنماط سيتم إضافتها وحسابها على الصفحة. على سبيل المثال ، لاستخدام استعلامات الوسائط ليس في css ، ولكن في JS وتنفيذ النمط المرغوب فقط ، على سبيل المثال (هذا غريب ، ولكن يجب أن يكون):
static get styles() { const mobileStyle = css`p { color: red; }`; const desktopStyle = css`p { color: green; }`; return [ window.matchMedia("(min-width: 400px)").matches ? desktopStyle : mobileStyle ]; }
وفقًا لذلك ، سنرى هذا إذا قام المستخدم بتسجيل الدخول إلى جهاز بعرض شاشة أكثر من 400 بكسل.

وهذا إذا زار المستخدم الموقع من جهاز بعرض أقل من 400 بكسل.

رأيي: لا يوجد عملياً أي حالة كافية عندما يواجه المستخدم ، الذي يعمل على جهاز محمول ، فجأة شاشة كاملة العرض بعرض 1920 بكسل. أضف إلى هذا التحميل البطيء للمكونات. نتيجة لذلك ، حصلنا على واجهة مُحسّنة للغاية مع تقديم مكونات سريع. المشكلة الوحيدة هي صعوبة الدعم.
أقترح الآن التعرف على أساليب دورة حياة العنصر المضاء:
- render () : ينفذ وصفًا لعنصر DOM باستخدام
lit-html
. من الناحية المثالية ، وظيفة render
هي وظيفة خالصة تستخدم فقط الخصائص الحالية للعنصر. يتم استدعاء الأسلوب render()
بواسطة الدالة update()
. - shouldUpdate (changeProperties) : يتم تنفيذه إذا كان من الضروري التحكم في التحديث
requestUpdate()
، عندما تم تغيير الخصائص أو requestUpdate()
استدعاء requestUpdate()
. الوسيطة إلى الدالة changedProperties
هي Map
تحتوي على مفاتيح للخصائص التي تم تغييرها. تبعًا للإعدادات الافتراضية ، تُرجع هذه الطريقة دائمًا true
، ولكن يمكن تغيير منطق الطريقة للتحكم في تحديث المكون. - performUpdate () : مطبق للتحكم في وقت التحديث ، على سبيل المثال ، للتكامل مع المجدول.
- تحديث (changeProperties) : هذه الطريقة تستدعي
render()
. يقوم أيضًا بتحديث سمات عنصر وفقًا لقيمة الخاصية. لن يؤدي تعيين الخصائص داخل هذه الطريقة إلى تحديث آخر. - firstUpdated (changeProperties) : يسمى بعد التحديث الأول لعنصر DOM مباشرة قبل استدعاء
updated()
. يمكن أن تكون هذه الطريقة مفيدة لالتقاط الارتباطات إلى العقد الثابتة المرئية التي تحتاج إلى التعامل معها مباشرة ، على سبيل المثال ، في updated()
. - المحدثة (changeProperties) : تسمى كلما تم تحديث DOM للعنصر وعرضه . تطبيق لتنفيذ المهام بعد التحديث من خلال DOM API ، على سبيل المثال ، مع التركيز على عنصر.
- requestUpdate (الاسم ، oldValue) : يستدعي طلب تحديث غير متزامن لأحد العناصر. يجب أن يسمى هذا عندما يحتاج العنصر إلى تحديث بناءً على حالة ما لا ينتج عن ضبط الخاصية.
- createRenderRoot () : افتراضيًا يقوم بإنشاء جذر الظل للعنصر. إذا لم يكن استخدام Shadow DOM ضروريًا ، فيجب أن تعيد الطريقة
this
.
كيف يتم تحديث العنصر:
- يتم إعطاء الخاصية قيمة جديدة.
- إذا كانت
hasChanged(value, oldValue)
بإرجاع false
، فلن يتم تحديث العنصر. خلاف ذلك ، يتم التخطيط لتحديث عن طريق استدعاء requestUpdate()
. - requestUpdate () : يقوم بتحديث العنصر بعد microtask (في نهاية حلقة الحدث وقبل إعادة الرسم التالي).
- performUpdate () : التحديث قيد التقدم ، ويستمر مع بقية واجهة برمجة التطبيقات للتحديث.
- shouldUpdate (changeProperties) : يستمر التحديث إذا
true
إرجاع true
. - firstUpdated (changeProperties) : يتم استدعاؤه عند تحديث العنصر لأول مرة ، مباشرة قبل الاتصال
updated()
. - تحديث (changeProperties) : تحديث العنصر. لا يؤدي تغيير الخصائص في هذه الطريقة إلى تحديث آخر.
- render () : إرجاع قالب
lit-html
لتقديم عنصر في DOM. لا يؤدي تغيير الخصائص في هذه الطريقة إلى تحديث آخر.
- المحدثة (changeProperties) : تسمى كلما تم تحديث عنصر.
لفهم جميع الفروق الدقيقة في دورة حياة المكون ، أنصحك باستشارة الوثائق .
في العمل ، لدي مشروع على مدير تجربة أدوبي (AEM) ، في تأليفه ، يمكن للمستخدم سحب وإسقاط المكونات على الصفحة ، ووفقًا لإيديولوجية AEM ، يحتوي هذا المكون على علامة script
تحتوي على كل ما هو مطلوب لتنفيذ منطق هذا المكون. ولكن في الواقع ، أدى هذا النهج إلى العديد من الموارد المعيقة والصعوبات في تنفيذ الجبهة في هذا النظام. لتنفيذ المقدمة ، تم اختيار مكونات الويب كوسيلة لعدم تغيير العرض من جانب الخادم (وهو ما قام به بشكل جيد للغاية) ، وكذلك لإثراء التطبيق القديم مع نهج جديد بلطف ، في اتجاه معاكس. في رأيي ، هناك العديد من الخيارات لتنفيذ تحميل مكونات الويب لهذا النظام: جمع حزمة (يمكن أن تكون كبيرة جدًا) أو تقسيمها إلى مجموعات (هناك الكثير من الملفات الصغيرة ، يلزم التحميل الديناميكي) ، أو استخدم النهج الحالي مع تضمين برنامج نصي في كل مكون الذي يتم تقديمه على جانب الخادم (أنا حقا لا أريد العودة إلى هذا). في رأيي ، الخيار الأول والثالث ليس خيارًا. للمرة الثانية ، تحتاج إلى أداة تحميل ديناميكية ، كما في الاستنسل. ولكن بالنسبة للعنصر المضاء في "المربع" ، لم يتم توفير ذلك. كانت هناك محاولة من جانب مطوري العناصر المضاءة لإنشاء محمل ديناميكي ، لكنها تجربة ، ولا يوصى باستخدامها في الإنتاج. أيضًا من مطوري العناصر المضاءة ، توجد مشكلة في مستودع مواصفات مكونات الويب مع اقتراح لإضافة المواصفات إلى القدرة على تحميل js اللازمة لمكون الويب ديناميكيًا استنادًا إلى ترميز HTML على الصفحة. وفي رأيي ، هذه الأداة الأصلية هي فكرة جيدة جدًا تتيح لك إنشاء نقطة واحدة لتهيئة مكونات الويب وإضافتها ببساطة إلى جميع صفحات الموقع.
لتحميل عناصر الويب المضاءة بشكل حيوي ديناميكيًا مع اللاعبين PolymerLabs ، تم تطوير عنصر الانقسام . هذا هو الحل التجريبي. يعمل بالطريقة التالية:
- لإنشاء SplitElement ، تكتب تعريفين للعنصر في وحدتين.
- واحد منهم هو كعب ، والذي يحدد الأجزاء المحملة من عنصر: عادة ما يكون هذا هو الاسم والخصائص. يجب أن يتم تعريف الخصائص باستخدام كعب روتين بحيث يمكن لعنصر الإضاءة إنشاء سمات يمكن ملاحظتها في الوقت المناسب لاستدعاء
customElements.define()
. - يجب أن يحتوي كعب الروتين أيضًا على طريقة تحميل غير متزامن ثابتة تُرجع فئة تنفيذ.
- فئة أخرى هي "التنفيذ" ، والذي يحتوي على كل شيء آخر.
- يقوم مُنشئ
SplitElement
بتحميل فئة التنفيذ ويقوم بتشغيل upgrade()
.
سبيل المثال كعب:
import {SplitElement, property} from '../split-element.js'; export class MyElement extends SplitElement {
مثال التنفيذ:
import {MyElement} from './my-element.js'; import {html} from '../split-element.js';
مثال SplitElement على ES6:
import {LitElement, html} from 'lit-element'; export * from 'lit-element';
إذا كنت لا تزال تستخدم التجميع المقترح أعلاه على Rollup ، فتأكد من تعيين بابل لتكون قادرة على التعامل مع الواردات الحيوية
npm install @babel/plugin-syntax-dynamic-import
وفي إعدادات .babelrc إضافة
{ "plugins": ["@babel/plugin-syntax-dynamic-import"] }
قدمت هنا مثالًا صغيرًا على تنفيذ مكونات الويب مع تأخير التحميل: https://github.com/malay76a/elbrus-split-litelement-web-components
حاولت تطبيق طريقة التحميل الديناميكي لمكونات الويب ، توصلت إلى الاستنتاج التالي: الأداة تعمل بشكل جيد ، تحتاج إلى جمع كل تعريفات مكونات الويب في ملف واحد ، وربط وصف المكون نفسه من خلال قطع منفصلة. بدون http2 ، هذا النهج لا يعمل ، لأن يتم تكوين مجموعة كبيرة جدًا من الملفات الصغيرة التي تصف المكونات. بناءً على مبدأ التصميم الذري ، يجب تحديد استيراد الذرات في الجسم ، ولكن يجب أن يكون الجسم مرتبطًا بالفعل كمكون منفصل. تتمثل إحدى الاختناقات في أن المستخدم سيتلقى الكثير من تعريفات عناصر المستخدم في المتصفح والتي سيتم تهيئتها بطريقة أو بأخرى في المتصفح وسيتم تحديد الحالة الأولية. مثل هذا الحل هو زائدة عن الحاجة. أحد الخيارات لحل بسيط لمحمل المكونات هي الخوارزمية التالية:
- تحميل المرافق المطلوبة ،
- تحميل polyfills ،
- تجميع العناصر المخصصة من ضوء DOM:
- يتم تحديد جميع عناصر DOM التي تحتوي على واصلة في اسم العلامة
- يتم تصفية القائمة وتشكيل قائمة من العناصر الأولى.
- :
- Intersection Observer,
- +- 100px import.
- 3 shadowDOM,
- , shadowDOM , , import JS.
- lit-element open-wc.org . webpack rollup, - storybook, IDE.
:
- Let's Build Web Components! Part 5: LitElement
- Web Component Essentials
- A night experimenting with Lit-HTML…
- LitElement To Do App
- LitElement app tutorial part 1: Getting started
- LitElement tutorial part 2: Templating, properties, and events
- LitElement tutorial part 3: State management with Redux
- LitElement tutorial part 4: Navigation and code splitting
- LitElement tutorial part 5: PWA and offline
- Lit-html workshop
- Awesome lit-html