بناء مكونات ويب قابلة للتشغيل البيني والتي تعمل حتى مع ذكاء بيانات PlatoBlockchain التفاعلي. البحث العمودي. عاي.

بناء مكونات ويب قابلة للتشغيل البيني والتي تعمل حتى مع React

أولئك منا الذين عملوا مطوري ويب لأكثر من بضع سنوات ربما كتبوا تعليمات برمجية باستخدام أكثر من إطار عمل JavaScript واحد. مع كل الخيارات المتاحة - React ، Svelte ، Vue ، Angular ، Solid - كل هذا لا مفر منه. من أكثر الأشياء المحبطة التي يتعين علينا التعامل معها عند العمل عبر أطر العمل إعادة إنشاء جميع مكونات واجهة المستخدم منخفضة المستوى: الأزرار وعلامات التبويب والقوائم المنسدلة وما إلى ذلك. الأمر المحبط بشكل خاص هو أننا سنحددها عادةً في إطار واحد ، على سبيل المثال React ، ولكن بعد ذلك نحتاج إلى إعادة كتابتها إذا أردنا بناء شيء ما في Svelte. أو Vue. أو صلبة. وهلم جرا.

ألن يكون من الأفضل لو تمكنا من تحديد مكونات واجهة المستخدم منخفضة المستوى مرة واحدة ، بطريقة حيادية الإطار ، ثم إعادة استخدامها بين الأطر؟ بالطبع ستفعل! ويمكننا؛ مكونات الويب هي الطريق. هذا المنشور سوف يوضح لك كيف.

اعتبارًا من الآن ، فإن قصة SSR لمكونات الويب مفقودة بعض الشيء. الظل التعريفي DOM (DSD) هو كيفية عرض مكون الويب من جانب الخادم ، ولكن حتى كتابة هذه السطور ، لم يتم دمجه مع أطر التطبيقات المفضلة لديك مثل Next أو Remix أو SvelteKit. إذا كان هذا مطلبًا بالنسبة لك ، فتأكد من التحقق من أحدث حالة DSD. لكن بخلاف ذلك ، إذا لم يكن SSR شيئًا تستخدمه ، فتابع القراءة.

أولاً ، بعض السياق

مكونات الويب هي في الأساس عناصر HTML تحددها بنفسك ، مثل <yummy-pizza> أو أيا كان ، من الألف إلى الياء. يتم تغطيتها في كل مكان هنا في CSS-Tricks (بما في ذلك سلسلة شاملة لكاليب ويليامز و واحد من جون ريا) لكننا سنتابع العملية بإيجاز. بشكل أساسي ، تقوم بتعريف فئة JavaScript ، وترثها من HTMLElement، ثم حدد الخصائص والسمات والأنماط التي يمتلكها مكون الويب ، وبالطبع الترميز الذي سيقدمه للمستخدمين في النهاية.

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

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

مكون الويب الذي نبنيه

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

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

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

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

مكون عداد بسيط

لنقم ببناء "Hello World" الكلاسيكي لمكونات JavaScript: عداد. سنعرض قيمة وزرًا يزيد هذه القيمة. بسيط وممل ، لكنه سيتيح لنا إلقاء نظرة على أبسط مكون ويب ممكن.

من أجل بناء مكون ويب ، فإن الخطوة الأولى هي إنشاء فئة JavaScript ، والتي ترث من HTMLElement:

class Counter extends HTMLElement {}

الخطوة الأخيرة هي تسجيل مكون الويب ، ولكن فقط إذا لم نقم بتسجيله بالفعل:

if (!customElements.get("counter-wc")) { customElements.define("counter-wc", Counter);
}

وبالطبع ، قم بتقديمها:

<counter-wc></counter-wc>

وكل شيء بينهما هو جعل مكون الويب يفعل ما نريده. طريقة دورة الحياة الشائعة هي connectedCallback، والذي يتم تنشيطه عند إضافة مكون الويب الخاص بنا إلى DOM. يمكننا استخدام هذه الطريقة لتقديم أي محتوى نريده. تذكر أن هذه فئة JS موروثة من HTMLElement، وهو ما يعني لدينا this value هي عنصر الويب نفسه ، مع جميع طرق معالجة DOM العادية التي تعرفها وتحبها بالفعل.

في أبسط ما يمكن ، يمكننا القيام بذلك:

class Counter extends HTMLElement { connectedCallback() { this.innerHTML = "<div style='color: green'>Hey</div>"; }
} if (!customElements.get("counter-wc")) { customElements.define("counter-wc", Counter);
}

... التي ستعمل بشكل جيد.

كلمة "مرحبًا" باللون الأخضر.
بناء مكونات ويب قابلة للتشغيل البيني والتي تعمل حتى مع React

إضافة محتوى حقيقي

دعنا نضيف بعض المحتويات المفيدة والتفاعلية. نحتاج الى <span> للاحتفاظ بقيمة الرقم الحالي و <button> لزيادة العداد. في الوقت الحالي ، سننشئ هذا المحتوى في المنشئ الخاص بنا ونلحقه عندما يكون مكون الويب بالفعل في DOM:

constructor() { super(); const container = document.createElement('div'); this.valSpan = document.createElement('span'); const increment = document.createElement('button'); increment.innerText = 'Increment'; increment.addEventListener('click', () => { this.#value = this.#currentValue + 1; }); container.appendChild(this.valSpan); container.appendChild(document.createElement('br')); container.appendChild(increment); this.container = container;
} connectedCallback() { this.appendChild(this.container); this.update();
}

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

للمضي قدمًا ، نحتاج إلى خاصية فئة JavaScript قابلة للتعيين تسمى value

#currentValue = 0; set #value(val) { this.#currentValue = val; this.update();
}

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

أو لا تتردد في الاتصال بها _value إذا كنت تفضل. وأخيرًا ، لدينا update الأسلوب:

update() { this.valSpan.innerText = this.#currentValue;
}

إنها تعمل!

مكون الويب المضاد.
بناء مكونات ويب قابلة للتشغيل البيني والتي تعمل حتى مع React

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

إضافة بعض الوظائف

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

لتحقيق هذه الغاية ، دعنا نحسن مكون الويب المضاد قليلاً. دعونا نجعلها تقبل أ color للتحكم في لون القيمة المعروضة. ودعنا نجعلها أيضًا تقبل increment حتى يتمكن مستهلكو مكون الويب هذا من زيادته بمقدار 2 ، 3 ، 4 في كل مرة. ولإحداث تغييرات في الحالة هذه ، دعنا نستخدم العداد الجديد في صندوق الحماية Svelte - سنصل إلى React بعد قليل.

سنبدأ بمكون الويب نفسه كما كان من قبل ونضيف سمة اللون. لتكوين مكون الويب الخاص بنا لقبول سمة والرد عليها ، نقوم بإضافة ثابت observedAttributes الخاصية التي تُرجع السمات التي يستمع إليها مكون الويب الخاص بنا.

static observedAttributes = ["color"];

مع ذلك ، يمكننا إضافة attributeChangedCallback طريقة دورة الحياة ، والتي سيتم تشغيلها كلما تم سرد أي من السمات في observedAttributes تم تعيينها أو تحديثها.

attributeChangedCallback(name, oldValue, newValue) { if (name === "color") { this.update(); }
}

الآن نقوم بتحديث ملف update طريقة استخدامه في الواقع:

update() { this.valSpan.innerText = this._currentValue; this.valSpan.style.color = this.getAttribute("color") || "black";
}

أخيرًا ، دعنا نضيف increment خاصية:

increment = 1;

بسيط ومتواضع.

استخدام مكون العداد في Svelte

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

<script> let color = "red";
</script> <style> main { text-align: center; }
</style> <main> <select bind:value={color}> <option value="red">Red</option> <option value="green">Green</option> <option value="blue">Blue</option> </select> <counter-wc color={color}></counter-wc>
</main>

ويعمل! يقوم العداد ، والزيادات ، والقائمة المنسدلة بتحديث اللون. كما ترى ، نعرض سمة اللون في قالب Svelte الخاص بنا ، وعندما تتغير القيمة ، يتعامل Svelte مع إجراءات الاتصال setAttribute على مثيل مكون الويب الأساسي لدينا. لا يوجد شيء مميز هنا: هذا هو نفس الشيء الذي يفعله بالفعل لسمات أي وقت عنصر HTML.

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

أولاً ، سنضيف بعض المتغيرات إلى مكون Svelte الخاص بنا:

let increment = 1;
let wcInstance;

ستتيح لك قوتنا المكونة العداد الزيادة بمقدار 1 أو 2:

<button on:click={() => increment = 1}>Increment 1</button>
<button on:click={() => increment = 2}>Increment 2</button>

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

<counter-wc bind:this={wcInstance} color={color}></counter-wc>

الآن ، في قالب Svelte الخاص بنا ، نستمع إلى التغييرات التي تطرأ على متغير زيادة المكون الخاص بنا وقمنا بتعيين خاصية مكون الويب الأساسية.

$: { if (wcInstance) { wcInstance.increment = increment; }
}

يمكنك اختبارها انتهى في هذا العرض الحي.

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

<counter-wc increment={increment} color={color}></counter-wc>

اتضح أننا نستطيع. هذا الرمز يعمل ؛ Svelte يتولى كل هذا العمل من أجلنا. تحقق من ذلك في هذا العرض. هذا هو السلوك القياسي لجميع أطر عمل JavaScript تقريبًا.

فلماذا أوضحت لك الطريقة اليدوية لتعيين خاصية مكون الويب؟ سببان: من المفيد فهم كيفية عمل هذه الأشياء ، وقلت منذ لحظة أن هذا يعمل "إلى حد كبير" مع جميع أطر عمل JavaScript. ولكن هناك إطار عمل واحد ، بشكل مجنون ، لا يدعم إعداد عنصر الويب كما رأينا للتو.

رد الفعل هو وحش مختلف

بناء مكونات ويب قابلة للتشغيل البيني والتي تعمل حتى مع ذكاء بيانات PlatoBlockchain التفاعلي. البحث العمودي. عاي.
بناء مكونات ويب قابلة للتشغيل البيني والتي تعمل حتى مع React

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

الحل ، بالطبع ، هو استخدام ref، والاستيلاء على مثيل مكون الويب ، وتعيينه يدويًا increment عندما تتغير هذه القيمة. تبدو هكذا:

import React, { useState, useRef, useEffect } from 'react';
import './counter-wc'; export default function App() { const [increment, setIncrement] = useState(1); const [color, setColor] = useState('red'); const wcRef = useRef(null); useEffect(() => { wcRef.current.increment = increment; }, [increment]); return ( <div> <div className="increment-container"> <button onClick={() => setIncrement(1)}>Increment by 1</button> <button onClick={() => setIncrement(2)}>Increment by 2</button> </div> <select value={color} onChange={(e) => setColor(e.target.value)}> <option value="red">Red</option> <option value="green">Green</option> <option value="blue">Blue</option> </select> <counter-wc ref={wcRef} increment={increment} color={color}></counter-wc> </div> );
}

كما ناقشنا ، فإن ترميز هذا يدويًا لكل خاصية مكون ويب ليس قابلاً للتطوير. لكن لم نفقد كل شيء لأن لدينا خيارين.

الخيار 1: استخدم السمات في كل مكان

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

الخيار 2: لفها

هناك قول مأثور مفاده أنه يمكنك حل أي مشكلة في علوم الكمبيوتر عن طريق إضافة مستوى من المراوغة (باستثناء مشكلة مستويات كثيرة جدًا من المراوغة). الكود الخاص بضبط هذه الدعائم يمكن التنبؤ به وبسيط. ماذا لو أخفناها في مكتبة؟ الأشخاص الأذكياء وراء Lit حل واحد. تنشئ هذه المكتبة مكوّن React جديدًا لك بعد أن تعطيه مكوّن ويب ، وسرد الخصائص التي تحتاجها. بينما ذكي ، أنا لست من المعجبين بهذا النهج.

بدلاً من تعيين مكونات الويب بشكل فردي لمكونات React التي تم إنشاؤها يدويًا ، ما أفضله هو صورة واحدة؟ يتفاعل المكون الذي نجتازه مكون الويب الخاص بنا اسم العلامة إلى (counter-wc في حالتنا) - جنبًا إلى جنب مع جميع السمات والخصائص - ولكي يعرض هذا المكون مكون الويب الخاص بنا ، أضف ref، ثم اكتشف ما هي الدعامة وما هي السمة. هذا هو الحل المثالي في رأيي. لا أعرف مكتبة تقوم بذلك ، لكن يجب أن يكون إنشاءها أمرًا سهلاً. هيا نحاول!

هذا هو استعمال نحن نبحث عن:

<WcWrapper wcTag="counter-wc" increment={increment} color={color} />

wcTag هو اسم علامة مكون الويب ؛ الباقي هي الخصائص والسمات التي نريد تمريرها.

إليك ما يبدو عليه التنفيذ الخاص بي:

import React, { createElement, useRef, useLayoutEffect, memo } from 'react'; const _WcWrapper = (props) => { const { wcTag, children, ...restProps } = props; const wcRef = useRef(null); useLayoutEffect(() => { const wc = wcRef.current; for (const [key, value] of Object.entries(restProps)) { if (key in wc) { if (wc[key] !== value) { wc[key] = value; } } else { if (wc.getAttribute(key) !== value) { wc.setAttribute(key, value); } } } }); return createElement(wcTag, { ref: wcRef });
}; export const WcWrapper = memo(_WcWrapper);

السطر الأكثر إثارة للاهتمام هو في النهاية:

return createElement(wcTag, { ref: wcRef });

هذه هي الطريقة التي ننشئ بها عنصرًا في React باسم ديناميكي. في الواقع ، هذا ما تقوم به React عادةً بنقل JSX إليه. يتم تحويل جميع divs لدينا إلى createElement("div") المكالمات. لا نحتاج عادةً إلى استدعاء واجهة برمجة التطبيقات هذه مباشرةً ولكنها موجودة عندما نحتاج إليها.

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

إذا كنت تتساءل لماذا نستخدم useLayoutEffect بدلا من useEffect، لأننا نريد تشغيل هذه التحديثات على الفور قبل عرض المحتوى الخاص بنا. لاحظ أيضًا أنه ليس لدينا مجموعة تبعية لملف useLayoutEffect؛ هذا يعني أننا نريد تشغيل هذا التحديث على كل تصيير. قد يكون هذا محفوفًا بالمخاطر لأن React تميل إلى إعادة التصيير كثير. أقوم بتحسين هذا من خلال لف كل شيء فيه React.memo. هذا هو في الأساس الإصدار الحديث من React.PureComponent، مما يعني أن المكون لن يعيد التصيير إلا إذا تغير أي من دعائمه الفعلية - ويتحقق مما إذا كان ذلك قد حدث عبر فحص بسيط للمساواة.

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

قبل الانتقال ، أود أن أستدعي شيئًا أخيرًا. قد لا تكون سعيدًا بمظهر الاستخدام. مرة أخرى ، يتم استخدام هذا المكون على النحو التالي:

<WcWrapper wcTag="counter-wc" increment={increment} color={color} />

على وجه التحديد ، قد لا ترغب في تمرير اسم علامة مكون الويب إلى <WcWrapper> المكون ويفضل بدلاً من ذلك @lit-labs/react package أعلاه ، مما يؤدي إلى إنشاء مكون React فردي جديد لكل مكون ويب. هذا عادل تمامًا وأنا أشجعك على استخدام كل ما يناسبك. لكن بالنسبة لي ، فإن إحدى ميزات هذا النهج هي أنه من السهل القيام بذلك حذف. إذا قامت React ، ببعض المعجزة ، بدمج التعامل الصحيح مع مكون الويب من فرعها التجريبي إلى main غدًا ، يمكنك تغيير الرمز أعلاه من هذا:

<WcWrapper wcTag="counter-wc" increment={increment} color={color} />

…الى هذا:

<counter-wc ref={wcRef} increment={increment} color={color} />

يمكنك على الأرجح كتابة كودمود واحد للقيام بذلك في كل مكان ، ثم حذفه <WcWrapper> كليا. في الواقع ، اخدش ذلك: من المحتمل أن يعمل البحث الشامل والاستبدال بـ RegEx.

التطبيق

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

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

اعرض الكود بالكامل
class BookCover extends HTMLElement { static observedAttributes = ['url']; attributeChangedCallback(name, oldValue, newValue) { if (name === 'url') { this.createMainImage(newValue); } } set preview(val) { this.previewEl = this.createPreview(val); this.render(); } createPreview(val) { if (typeof val === 'string') { return base64Preview(val); } else { return blurHashPreview(val); } } createMainImage(url) { this.loaded = false; const img = document.createElement('img'); img.alt = 'Book cover'; img.addEventListener('load', () =&gt; { if (img === this.imageEl) { this.loaded = true; this.render(); } }); img.src = url; this.imageEl = img; } connectedCallback() { this.render(); } render() { const elementMaybe = this.loaded ? this.imageEl : this.previewEl; syncSingleChild(this, elementMaybe); }
}

أولاً ، نسجل السمة التي نهتم بها ونتفاعل عندما تتغير:

static observedAttributes = ['url']; attributeChangedCallback(name, oldValue, newValue) { if (name === 'url') { this.createMainImage(newValue); }
}

يؤدي هذا إلى إنشاء مكون الصورة الخاص بنا ، والذي سيظهر فقط عند التحميل:

createMainImage(url) { this.loaded = false; const img = document.createElement('img'); img.alt = 'Book cover'; img.addEventListener('load', () => { if (img === this.imageEl) { this.loaded = true; this.render(); } }); img.src = url; this.imageEl = img;
}

بعد ذلك ، لدينا خاصية المعاينة الخاصة بنا ، والتي يمكن أن تكون إما سلسلة معاينة base64 الخاصة بنا ، أو الخاصة بنا blurhash رزمة:

set preview(val) { this.previewEl = this.createPreview(val); this.render();
} createPreview(val) { if (typeof val === 'string') { return base64Preview(val); } else { return blurHashPreview(val); }
}

هذا ينسجم مع الوظيفة المساعدة التي نحتاجها:

function base64Preview(val) { const img = document.createElement('img'); img.src = val; return img;
} function blurHashPreview(preview) { const canvasEl = document.createElement('canvas'); const { w: width, h: height } = preview; canvasEl.width = width; canvasEl.height = height; const pixels = decode(preview.blurhash, width, height); const ctx = canvasEl.getContext('2d'); const imageData = ctx.createImageData(width, height); imageData.data.set(pixels); ctx.putImageData(imageData, 0, 0); return canvasEl;
}

وأخيرًا ، لدينا render الأسلوب:

connectedCallback() { this.render();
} render() { const elementMaybe = this.loaded ? this.imageEl : this.previewEl; syncSingleChild(this, elementMaybe);
}

وبعض طرق المساعدين لربط كل شيء معًا:

export function syncSingleChild(container, child) { const currentChild = container.firstElementChild; if (currentChild !== child) { clearContainer(container); if (child) { container.appendChild(child); } }
} export function clearContainer(el) { let child; while ((child = el.firstElementChild)) { el.removeChild(child); }
}

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

الاحتمالات وينتهي

لقد ذكرت بالفعل غلاف Lit's React. ولكن إذا وجدت نفسك تستخدم Stencil ، فهي تدعم بالفعل ملف منفصل الإخراج فقط من أجل React. والناس الطيبون في Microsoft لديهم أيضًا صنع شيئًا مشابهًا لملف Lit، مرفق بمكتبة مكون الويب السريع.

كما ذكرت ، فإن جميع الأطر التي لم تسمى React ستتعامل مع تعيين خصائص مكون الويب نيابة عنك. فقط لاحظ أن البعض لديه بعض النكهات الخاصة في بناء الجملة. على سبيل المثال ، مع Solid.js ، <your-wc value={12}> دائما يفترض ذلك value هي خاصية يمكنك تجاوزها بامتداد attr البادئة ، مثل <your-wc attr:value={12}>.

اختتام

تعد مكونات الويب جزءًا مثيرًا للاهتمام ، وغالبًا ما يتم استخدامه بشكل غير كافٍ في مشهد تطوير الويب. يمكن أن تساعد في تقليل اعتمادك على أي إطار عمل JavaScript واحد عن طريق إدارة واجهة المستخدم الخاصة بك ، أو مكونات "طرفية". في حين أن إنشاء هذه المكونات كمكونات ويب - على عكس مكونات Svelte أو React - لن يكون مريحًا ، إلا أن الجانب الإيجابي هو أنها ستكون قابلة لإعادة الاستخدام على نطاق واسع.


بناء مكونات ويب قابلة للتشغيل البيني والتي تعمل حتى مع React نشرت أصلا في حيل CSS. يجب احصل على النشرة الإخبارية.

الطابع الزمني:

اكثر من الخدع المغلق