בניית רכיבי אינטרנט הניתנים להפעלה הדדית שאפילו עובדים עם אינטליגנציה נתונים של React PlatoBlockchain. חיפוש אנכי. איי.

בניית רכיבי אינטרנט הניתנים להפעלה הדדית שאפילו עובדים עם React

אלה מאיתנו שהיו מפתחי אתרים יותר מכמה שנים, כנראה כתבו קוד באמצעות יותר ממסגרת JavaScript אחת. עם כל האפשרויות שיש בחוץ - React, Svelte, Vue, Angular, Solid - זה כמעט בלתי נמנע. אחד הדברים היותר מתסכלים שאנחנו צריכים להתמודד איתם כשעובדים בין מסגרות הוא יצירה מחדש של כל אותם רכיבי ממשק משתמש ברמה נמוכה: כפתורים, כרטיסיות, תפריטים נפתחים וכו'. מה שמתסכל במיוחד הוא שבדרך כלל יגדיר אותם במסגרת אחת , נגיד React, אבל אז צריך לשכתב אותם אם אנחנו רוצים לבנות משהו ב-Svelte. או Vue. או מוצק. וכן הלאה.

האם לא היה עדיף אם נוכל להגדיר את רכיבי המשתמש ברמה הנמוכה האלה פעם אחת, בצורה אגנוסטית למסגרת, ואז לעשות בהם שימוש חוזר בין מסגרות? ברור שכן! ואנחנו יכולים; רכיבי אינטרנט הם הדרך. הפוסט הזה יראה לכם איך.

נכון לעכשיו, הסיפור של SSR עבור רכיבי אינטרנט קצת חסר. Shadow DOM (DSD) הצהרתי הוא האופן שבו רכיב אינטרנט מעובד בצד השרת, אך נכון לכתיבת שורות אלה, הוא אינו משולב עם מסגרות היישום המועדפות עליך כמו Next, Remix או SvelteKit. אם זו דרישה עבורך, הקפד לבדוק את הסטטוס העדכני ביותר של DSD. אבל אחרת, אם SSR הוא לא משהו שאתה משתמש בו, המשך לקרוא.

ראשית, הקשר כלשהו

רכיבי אינטרנט הם בעצם רכיבי HTML שאתה מגדיר בעצמך, כמו <yummy-pizza> או מה שלא יהיה, מהיסוד. הם מכוסים כאן ב-CSS-Tricks (כולל סדרה נרחבת מאת קיילב וויליאמס ו אחד מאת ג'ון ריאה) אבל נעבור בקצרה על התהליך. בעיקרו של דבר, אתה מגדיר מחלקה של JavaScript, יורש אותה ממנה HTMLElement, ולאחר מכן הגדירו את המאפיינים, המאפיינים והסגנונות שיש לרכיב האינטרנט וכמובן, הסימון שהוא בסופו של דבר יציג למשתמשים שלכם.

היכולת להגדיר רכיבי HTML מותאמים אישית שאינם קשורים לאף רכיב מסוים היא מרגשת. אבל החופש הזה הוא גם מגבלה. קיים ללא תלות בכל מסגרת JavaScript פירושה שאתה לא באמת יכול לקיים אינטראקציה עם מסגרות JavaScript אלה. תחשוב על רכיב React שמביא חלק מהנתונים ואז מעבד חלק אַחֵר רכיב תגובה, העברת הנתונים. זה לא באמת יעבוד בתור רכיב אינטרנט, מכיוון שרכיב אינטרנט לא יודע איך לעבד רכיב React.

רכיבי אינטרנט מצטיינים במיוחד רכיבי עלים. רכיבי עלים הם הדבר האחרון שיוצג בעץ רכיבים. אלו הם הרכיבים שמקבלים כמה אביזרים, ומעבדים כמה UI. אלו הם לֹא הרכיבים היושבים באמצע עץ הרכיבים שלך, מעבירים נתונים, מגדירים הקשר וכו' - רק חלקים טהורים של UI זה ייראה אותו הדבר, לא משנה איזו מסגרת JavaScript מפעילה את שאר האפליקציה.

רכיב האינטרנט שאנו בונים

במקום לבנות משהו משעמם (ונפוץ), כמו כפתור, בואו נבנה משהו קצת שונה. ב שלי הודעה אחרונה בדקנו שימוש בתצוגות מקדימות מטושטשות כדי למנוע זרימת תוכן מחדש, ולספק ממשק משתמש הגון למשתמשים בזמן שהתמונות שלנו נטענות. הסתכלנו על קידוד base64 של גרסאות מטושטשות ומושפלות של התמונות שלנו, והראנו את זה בממשק המשתמש שלנו בזמן שהתמונה האמיתית נטענת. בדקנו גם יצירת תצוגה מקדימה קומפקטית ומטושטשת להפליא באמצעות כלי שנקרא Blurhash.

הפוסט הזה הראה לך איך ליצור את התצוגות המקדימות האלה ולהשתמש בהן בפרויקט React. פוסט זה יראה לך כיצד להשתמש בתצוגות מקדימות אלה מרכיב אינטרנט כדי שתוכל להשתמש בהן כל מסגרת JavaScript.

אבל אנחנו צריכים ללכת לפני שנוכל לרוץ, אז נעבור קודם על משהו טריוויאלי ומטופש כדי לראות בדיוק איך רכיבי אינטרנט עובדים.

הכל בפוסט הזה יבנה רכיבי אינטרנט וניל ללא כל כלי עבודה. זה אומר שלקוד יהיה מעט מדבקות, אבל אמור להיות קל יחסית לעקוב. כלים כמו מוּאָר or סטנסיל מיועדים לבניית רכיבי אינטרנט וניתן להשתמש בהם כדי להסיר חלק גדול מהלוח הזה. אני קורא לך לבדוק אותם! אבל עבור הפוסט הזה, אני אעדיף קצת יותר בוילפלייס בתמורה לכך שלא אצטרך להציג וללמד תלות נוספת.

רכיב דלפק פשוט

בואו נבנה את "הלו עולם" הקלאסי של רכיבי 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 נועדו להפוך את זה לפשוט יותר.

הוספת עוד קצת פונקציונליות

פוסט זה אינו צלילה עמוקה לתוך רכיבי אינטרנט. לא נסקור את כל ממשקי ה-API ומחזורי החיים; אפילו לא נכסה שורשי צל או חריצים. יש אינסוף תוכן בנושאים האלה. המטרה שלי כאן היא לספק מבוא הגון מספיק כדי לעורר קצת עניין, יחד עם כמה הדרכה שימושית לגבי המציאות באמצעות רכיבי אינטרנט עם מסגרות 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 ממש על רכיב האינטרנט שלנו, ב-markup, כמו שאנחנו עושים בדרך כלל עבור אביזרי רכיבים, ויש את זה, אתה יודע, פשוט לעבוד? במילים אחרות, זה יהיה נחמד אם נוכל למחוק את כל השימושים של wcInstance והשתמש בקוד הפשוט יותר הזה במקום זאת:

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

מסתבר שאנחנו יכולים. הקוד הזה עובד; סוולטה מטפלת בכל עבודת הרגליים הזו עבורנו. בדוק את זה בהדגמה זו. זוהי התנהגות סטנדרטית כמעט לכל מסגרות JavaScript.

אז למה הראיתי לך את הדרך הידנית להגדיר את האביזר של רכיב האינטרנט? שתי סיבות: כדאי להבין איך הדברים האלה עובדים ולפני רגע אמרתי שזה עובד "בערך" לכל מסגרות JavaScript. אבל יש מסגרת אחת שבאופן מטורף, לא תומכת בהגדרת אבזרי אינטרנט כמו שראינו זה עתה.

React היא חיה אחרת

בניית רכיבי אינטרנט הניתנים להפעלה הדדית שאפילו עובדים עם אינטליגנציה נתונים של React PlatoBlockchain. חיפוש אנכי. איי.
בניית רכיבי אינטרנט הניתנים להפעלה הדדית שאפילו עובדים עם React

לְהָגִיב. מסגרת ה-JavaScript הפופולרית ביותר בעולם אינה תומכת באינטראפ בסיסי עם רכיבי אינטרנט. זו בעיה ידועה שייחודית ל-React. מעניין שזה למעשה תוקן בענף הניסיוני של React, אבל משום מה לא מוזג לגרסה 18. עם זאת, אנחנו עדיין יכולים לעקוב אחר ההתקדמות שלו. ואתה יכול לנסות את זה בעצמך עם א דמו ישיר.

הפתרון, כמובן, הוא להשתמש ב-a 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: לעטוף אותו

יש פתגם ישן שאומר שאפשר לפתור כל בעיה במדעי המחשב על ידי הוספת רמה של עקיפה (חוץ מהבעיה של יותר מדי רמות של עקיפה). הקוד להגדיר את האביזרים האלה די צפוי ופשוט. מה אם נסתיר אותו בספרייה? האנשים החכמים מאחורי ליט יש פתרון אחד. ספרייה זו יוצרת עבורך רכיב 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") שיחות. בדרך כלל איננו צריכים להתקשר ישירות ל-API הזה, אבל הוא נמצא שם כשאנחנו צריכים אותו.

מעבר לזה, אנחנו רוצים להפעיל אפקט פריסה ולולאה דרך כל אביזר שהעברנו לרכיב שלנו. אנחנו עוברים בלולאה על כולם ובודקים אם זה נכס עם in check כי בודק את אובייקט המופע של רכיב הרשת וכן את שרשרת האב-טיפוס שלו, אשר יתפוס את כל ה-getters/seters שייכנסו לאב-טיפוס המחלקה. אם לא קיים מאפיין כזה, ההנחה היא שזהו תכונה. בכל מקרה, אנו מגדירים אותו רק אם הערך השתנה בפועל.

אם אתה תוהה למה אנחנו משתמשים useLayoutEffect במקום useEffect, זה בגלל שאנחנו רוצים להפעיל את העדכונים האלה באופן מיידי לפני שהתוכן שלנו יוצג. כמו כן, שימו לב שאין לנו מערך תלות שלנו useLayoutEffect; זה אומר שאנחנו רוצים להפעיל את העדכון הזה כל עיבוד. זה יכול להיות מסוכן מכיוון ש-React נוטה לעיבוד מחדש הרבה. אני משפר את זה על ידי לעטוף את כל העניין React.memo. זוהי בעצם הגרסה המודרנית של React.PureComponent, כלומר הרכיב יעבד מחדש רק אם אחד מהאביזרים שלו בפועל השתנה - והוא בודק אם זה קרה באמצעות בדיקת שוויון פשוטה.

הסיכון היחיד כאן הוא שאם אתה מעביר אבזר אובייקט שאתה עושה מוטציה ישירות מבלי להקצות מחדש, אז לא תראה את העדכונים. אבל זה מאוד מיואש, במיוחד בקהילת React, אז לא הייתי דואג בקשר לזה.

לפני שממשיכים הלאה, אני רוצה לומר דבר אחרון. אולי לא תהיה מרוצה מאיך שהשימוש נראה. שוב, רכיב זה משמש כך:

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

באופן ספציפי, ייתכן שלא תאהב להעביר את שם תג רכיב האינטרנט ל- <WcWrapper> רכיב ולהעדיף במקום את @lit-labs/react חבילה למעלה, שיוצרת רכיב 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 Network שלך כדי לראות באמת דברים מתרחשים.

הצג את כל הקוד
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 תצטרך עטיפה לעת עתה, כפי שדיברנו .

הסיכויים והסיומות

כבר הזכרתי את מעטפת React של Lit. אבל אם אתה מוצא את עצמך משתמש בסטנסיל, הוא למעשה תומך ב-a צינור פלט נפרד רק עבור React. ולאנשים הטובים במיקרוסופט יש גם יצר משהו דומה לעטיפה של ליט, מצורף לספריית רכיבי האינטרנט המהיר.

כפי שציינתי, כל המסגרות שאינן נקראות React יטפלו בהגדרת מאפייני רכיבי האינטרנט עבורך. רק שימו לב שלחלקם יש כמה טעמים מיוחדים של תחביר. לדוגמה, עם Solid.js, <your-wc value={12}> תמיד מניח את זה value הוא נכס, שאתה יכול לעקוף אותו עם א attr קידומת, כמו <your-wc attr:value={12}>.

גלישה את

רכיבי אינטרנט הם חלק מעניין, לעתים קרובות פחות בשימוש בנוף פיתוח האינטרנט. הם יכולים לעזור להפחית את התלות שלך בכל מסגרת JavaScript בודדת על ידי ניהול ממשק המשתמש שלך, או רכיבי "עלים". בעוד שיצירת אלה כרכיבי אינטרנט - בניגוד לרכיבי Svelte או React - לא תהיה ארגונומית כל כך, הצד החיובי הוא שהם יהיו ניתנים לשימוש חוזר נרחב.


בניית רכיבי אינטרנט הניתנים להפעלה הדדית שאפילו עובדים עם React פורסם במקור ב CSS-טריקים. אתה צריך לקבל את הניוזלטר.

בול זמן:

עוד מ טריקים של CSS