1. مقدمه
چه چیزی روشن است
Lit یک کتابخانه ساده برای ساخت کامپوننت های وب سریع و سبک است که در هر چارچوبی یا اصلاً بدون فریمورک کار می کنند. با Lit می توانید اجزای قابل اشتراک گذاری، برنامه های کاربردی، سیستم های طراحی و موارد دیگر را بسازید.
چیزی که یاد خواهید گرفت
چگونه چندین مفهوم React را به Lit ترجمه کنیم مانند:
- JSX و الگوسازی
- قطعات و لوازم جانبی
- وضعیت و چرخه حیات
- قلاب
- بچه ها
- مراجع
- دولت میانجی
چیزی که خواهی ساخت
در پایان این کد لبه قادر خواهد بود مفاهیم اجزای React را به آنالوگ های Lit خود تبدیل کند.
آنچه شما نیاز دارید
- آخرین نسخه کروم، سافاری، فایرفاکس یا اج.
- آشنایی با HTML، CSS، جاوا اسکریپت و ابزار توسعه کروم .
- دانش React
- (پیشرفته) اگر می خواهید بهترین تجربه توسعه را داشته باشید VS Code را دانلود کنید. همچنین برای VS Code و NPM به پلاگین روشن نیاز دارید.
2. Lit vs React
مفاهیم و قابلیتهای اصلی Lit از بسیاری جهات شبیه React است، اما Lit همچنین دارای برخی تفاوتها و تمایزات اصلی است:
کوچک است
Lit بسیار کوچک است: در مقایسه با React + ReactDOM 40+ kb به حدود 5 کیلو بایت کوچک و gzip می شود.
سریع است
در معیارهای عمومی که سیستم قالبسازی Lit، lit-html، را با VDOM React مقایسه میکنند، lit-html در بدترین حالت 8 تا 10 درصد سریعتر از React و در موارد استفاده رایجتر 50 درصد سریعتر ظاهر میشود.
LitElement (کلاس پایه کامپوننت Lit) حداقل سربار را به lit-html اضافه می کند، اما عملکرد React را در مقایسه با ویژگی های مؤلفه مانند استفاده از حافظه و تعامل و زمان راه اندازی 16 تا 30 درصد شکست می دهد .
نیازی به ساخت ندارد
با ویژگیهای جدید مرورگر مانند ماژولهای ES و برچسبگذاریشده الگو، Lit برای اجرا نیازی به کامپایل ندارد . این بدان معنی است که محیط های توسعه دهنده را می توان با یک برچسب اسکریپت + یک مرورگر + یک سرور تنظیم کرد و شما در حال اجرا هستید.
با ماژولهای ES و CDNهای مدرن امروزی مانند Skypack یا UNPKG ، ممکن است حتی برای شروع به NPM نیاز نداشته باشید!
اگرچه، اگر بخواهید، همچنان میتوانید کد Lit را بسازید و بهینه کنید . ادغام اخیر توسعه دهندگان پیرامون ماژول های بومی ES برای Lit خوب بوده است – Lit فقط جاوا اسکریپت معمولی است و نیازی به CLIهای فریمورک خاص یا مدیریت ساخت وجود ندارد .
چارچوب آگنوستیک
اجزای Lit از مجموعه ای از استانداردهای وب به نام مؤلفه های وب ساخته شده اند. این به این معنی است که ساخت یک کامپوننت در Lit در چارچوب های فعلی و آینده کار خواهد کرد . اگر از عناصر HTML پشتیبانی می کند، از Web Components نیز پشتیبانی می کند.
تنها مشکلی که در ارتباط با چارچوب ها وجود دارد زمانی است که چارچوب ها از DOM پشتیبانی محدودی دارند. React یکی از این فریمورکها است، اما اجازه میدهد دریچههای فرار از طریق Refs انجام شود، و Refs در React یک تجربه توسعهدهنده خوب نیست.
تیم Lit روی یک پروژه آزمایشی به نام @lit-labs/react
کار می کند که به طور خودکار اجزای Lit شما را تجزیه می کند و یک پوشش React ایجاد می کند تا شما مجبور به استفاده از refs نباشید.
علاوه بر این، Custom Elements Everywhere به شما نشان می دهد که کدام چارچوب ها و کتابخانه ها به خوبی با عناصر سفارشی کار می کنند!
پشتیبانی از نوع اسکریپت درجه یک
اگرچه امکان نوشتن تمام کدهای Lit در جاوا اسکریپت وجود دارد، اما Lit در TypeScript نوشته می شود و تیم Lit توصیه می کند که توسعه دهندگان از TypeScript نیز استفاده کنند!
تیم Lit برای کمک به حفظ پروژههایی که چک کردن نوع TypeScript و هوشمندی را به قالبهای Lit در زمان توسعه و ساخت با lit-analyzer
و lit-plugin
میآورند، با جامعه Lit همکاری میکند.
ابزارهای توسعه دهنده در مرورگر تعبیه شده اند
اجزای روشن فقط عناصر HTML در DOM هستند . این بدان معناست که برای بازرسی اجزای خود، نیازی به نصب ابزار یا افزونه برای مرورگر خود ندارید .
شما به سادگی می توانید ابزارهای توسعه دهنده را باز کنید، یک عنصر را انتخاب کنید و ویژگی ها یا وضعیت آن را بررسی کنید.
این با در نظر گرفتن رندر سمت سرور (SSR) ساخته شده است
Lit 2 با در نظر گرفتن پشتیبانی SSR ساخته شده است. در زمان نگارش این کد لبه، تیم Lit هنوز ابزارهای SSR را به شکل پایدار منتشر نکرده است، اما تیم Lit قبلاً اجزای رندر شده سمت سرور را در محصولات Google مستقر کرده و SSR را در برنامههای React آزمایش کرده است. تیم Lit انتظار دارد به زودی این ابزارها را به صورت خارجی در GitHub منتشر کند.
در ضمن میتوانید پیشرفت تیم Lit را در اینجا دنبال کنید.
خرید کم است
Lit به تعهد قابل توجهی برای استفاده نیاز ندارد! می توانید اجزای سازنده را در Lit بسازید و آنها را به پروژه موجود خود اضافه کنید. اگر آنها را دوست ندارید، پس لازم نیست کل برنامه را یکجا تبدیل کنید زیرا اجزای وب در چارچوب های دیگر کار می کنند!
آیا یک برنامه کامل در Lit ساخته اید و می خواهید به برنامه دیگری تغییر دهید؟ خوب، پس می توانید برنامه Lit فعلی خود را در چارچوب جدید خود قرار دهید و هر آنچه را که می خواهید به اجزای فریم ورک جدید منتقل کنید.
علاوه بر این، بسیاری از فریمورکهای مدرن از خروجی در کامپوننتهای وب پشتیبانی میکنند ، به این معنی که آنها معمولاً میتوانند درون یک عنصر Lit قرار بگیرند.
3. راه اندازی و کاوش در زمین بازی
دو راه برای انجام این کد لبه وجود دارد:
- شما می توانید آن را به طور کامل آنلاین، در مرورگر انجام دهید
- (پیشرفته) می توانید این کار را در دستگاه محلی خود با استفاده از VS Code انجام دهید
دسترسی به کد
در سرتاسر Codelab پیوندهایی به زمین بازی Lit وجود دارد که به شرح زیر است:
زمین بازی یک جعبه ماسه ای کد است که به طور کامل در مرورگر شما اجرا می شود. میتواند فایلهای TypeScript و JavaScript را کامپایل و اجرا کند، و همچنین میتواند به طور خودکار واردات به ماژولهای گره را حل کند. به عنوان مثال
// before
import './my-file.js';
import 'lit';
// after
import './my-file.js';
import 'https://cdn.skypack.dev/lit';
شما می توانید کل آموزش را در زمین بازی Lit با استفاده از این نقاط بازرسی به عنوان نقطه شروع انجام دهید. اگر از VS Code استفاده می کنید، می توانید از این نقاط بازرسی برای دانلود کد شروع برای هر مرحله و همچنین برای بررسی کار خود استفاده کنید.
کاوش در رابط کاربری روشنایی زمین بازی
اسکرین شات رابط کاربری زمین بازی Lit بخش هایی را که در این کد لبه استفاده خواهید کرد برجسته می کند.
- انتخابگر فایل به دکمه پلاس توجه کنید...
- ویرایشگر فایل.
- پیش نمایش کد.
- دکمه بارگذاری مجدد
- دکمه دانلود.
تنظیم VS Code (پیشرفته)
در اینجا مزایای استفاده از این تنظیمات VS Code وجود دارد:
- بررسی نوع الگو
- هوشمندسازی الگو و تکمیل خودکار
اگر NPM، VS Code (با افزونه lit-plugin ) را قبلاً نصب کردهاید و میدانید چگونه از آن محیط استفاده کنید، میتوانید به سادگی این پروژهها را با انجام کارهای زیر دانلود و شروع کنید:
- دکمه دانلود را فشار دهید
- محتویات فایل tar را در یک دایرکتوری استخراج کنید
- (اگر TS) یک tsconfig سریع راه اندازی کنید که ماژول های es و es2015+ را خروجی می دهد
- سرور توسعه دهنده ای را نصب کنید که بتواند مشخصه های ماژول خالی را حل کند (تیم Lit @web/dev-server را توصیه می کند)
- در اینجا یک نمونه
package.json
است
- در اینجا یک نمونه
- سرور توسعه دهنده را اجرا کرده و مرورگر خود را باز کنید (اگر از @web/dev-server استفاده می کنید، می توانید از
npx web-dev-server --node-resolve --watch --open
استفاده کنید)- اگر از نمونه
package.json
استفاده می کنید ازnpm run dev
استفاده کنید
- اگر از نمونه
4. JSX & Templating
در این قسمت با اصول قالب بندی در Lit آشنا می شوید.
قالب های JSX & Lit
JSX یک پسوند نحوی برای جاوا اسکریپت است که به کاربران React اجازه می دهد به راحتی قالب ها را در کد جاوا اسکریپت خود بنویسند. الگوهای روشن هدفی مشابه دارند: بیان رابط کاربری یک جزء به عنوان تابعی از وضعیت آن.
نحو پایه
در React شما میتوانید یک Hello World JSX را به این صورت ارائه کنید:
import 'react';
import ReactDOM from 'react-dom';
const name = 'Josh Perez';
const element = (
<>
<h1>Hello, {name}</h1>
<div>How are you?</div>
</>
);
ReactDOM.render(
element,
mountNode
);
در مثال بالا، دو عنصر و یک متغیر "name" وجود دارد. در Lit کارهای زیر را انجام می دهید:
import {html, render} from 'lit';
const name = 'Josh Perez';
const element = html`
<h1>Hello, ${name}</h1>
<div>How are you?</div>`;
render(
element,
mountNode
);
توجه داشته باشید که قالب های Lit برای گروه بندی چندین عنصر در قالب های خود نیازی به React Fragment ندارند.
در Lit، الگوها با یک الگوی برچسبگذاری شده با html
، LIT eral پیچیده میشوند، که اتفاقاً جایی است که Lit نام خود را میگیرد!
مقادیر الگو
الگوهای Lit میتوانند سایر الگوهای Lit را بپذیرند که به عنوان TemplateResult
شناخته میشوند. به عنوان مثال، name
در تگ های مورب ( <i>
) بپیچید و آن را با یک الگوی برچسب گذاری شده بپیچید NB به معنای واقعی کلمه مطمئن شوید که از کاراکتر بک تیک ( `
) نه از کاراکتر نقل قول واحد ( '
) استفاده کنید.
import {html, render} from 'lit';
const name = html`<i>Josh Perez</i>`;
const element = html`
<h1>Hello, ${name}</h1>
<div>How are you?</div>`;
render(
element,
mountNode
);
TemplateResult
های روشن می توانند آرایه ها، رشته ها، سایر قالب های TemplateResult
و همچنین دستورالعمل ها را بپذیرند.
برای تمرین، کد React زیر را به Lit تبدیل کنید:
const itemsToBuy = [
<li>Bananas</li>,
<li>oranges</li>,
<li>apples</li>,
<li>grapes</li>
];
const element = (
<>
<h1>Things to buy:</h1>
<ol>
{itemsToBuy}
</ol>
</>);
ReactDOM.render(
element,
mountNode
);
پاسخ:
import {html, render} from 'lit';
const itemsToBuy = [
html`<li>Bananas</li>`,
html`<li>oranges</li>`,
html`<li>apples</li>`,
html`<li>grapes</li>`
];
const element = html`
<h1>Things to buy:</h1>
<ol>
${itemsToBuy}
</ol>`;
render(
element,
mountNode
);
پاس دادن و تنظیم لوازم
یکی از بزرگترین تفاوتهای میان دستورهای JSX و Lit، نحو اتصال دادهها است. به عنوان مثال، این ورودی React را با bindings بگیرید:
const disabled = false;
const label = 'my label';
const myClass = 'my-class';
const value = 'my value';
const element =
<input
disabled={disabled}
className={`static-class ${myClass}`}
defaultValue={value}/>;
ReactDOM.render(
element,
mountNode
);
در مثال بالا یک ورودی تعریف شده است که موارد زیر را انجام می دهد:
- بر روی یک متغیر تعریف شده غیرفعال می شود (در این مورد نادرست)
- کلاس را روی
static-class
به اضافه یک متغیر تنظیم می کند (در این مورد"static-class my-class"
) - یک مقدار پیش فرض را تنظیم می کند
در Lit کارهای زیر را انجام می دهید:
import {html, render} from 'lit';
const disabled = false;
const label = 'my label';
const myClass = 'my-class';
const value = 'my value';
const element = html`
<input
?disabled=${disabled}
class="static-class ${myClass}"
.value=${value}>`;
render(
element,
mountNode
);
در مثال Lit یک binding boolean برای تغییر ویژگی disabled
اضافه شده است.
در مرحله بعد، به جای className
، یک اتصال مستقیم به ویژگی class
وجود دارد. اتصالات متعددی را می توان به ویژگی class
اضافه کرد، مگر اینکه از دستورالعمل classMap
استفاده کنید که یک کمک کننده اعلامی برای تغییر کلاس ها است.
در نهایت، ویژگی value
روی ورودی تنظیم می شود. برخلاف React، این عنصر ورودی را فقط خواندنی تنظیم نمی کند زیرا از پیاده سازی و رفتار اصلی ورودی پیروی می کند.
دستور صحافی روشن
html`<my-element ?attribute-name=${booleanVar}>`;
-
?
پیشوند، نحو اتصال برای جابجایی یک ویژگی روی یک عنصر است - معادل
inputRef.toggleAttribute('attribute-name', booleanVar)
- مفید برای عناصری که
disabled
به عنوانdisabled="false"
استفاده می کنند، همچنان توسط DOM به عنوان درست خوانده می شود زیراinputElement.hasAttribute('disabled') === true
html`<my-element .property-name=${anyVar}>`;
-
.
پیشوند نحو اتصال برای تنظیم ویژگی یک عنصر است - معادل
inputRef.propertyName = anyVar
- برای انتقال داده های پیچیده مانند اشیا، آرایه ها یا کلاس ها خوب است
html`<my-element attribute-name=${stringVar}>`;
- به ویژگی یک عنصر متصل می شود
- معادل
inputRef.setAttribute('attribute-name', stringVar)
- برای مقادیر پایه، انتخابگرهای قانون سبک و querySelectors خوب است
گردانندگان پاس
const disabled = false;
const label = 'my label';
const myClass = 'my-class';
const value = 'my value';
const element =
<input
onClick={() => console.log('click')}
onChange={e => console.log(e.target.value)} />;
ReactDOM.render(
element,
mountNode
);
در مثال بالا یک ورودی تعریف شده است که موارد زیر را انجام می دهد:
- هنگامی که ورودی کلیک می شود، کلمه "کلیک" را ثبت کنید
- هنگام تایپ یک کاراکتر، مقدار ورودی را ثبت کنید
در Lit کارهای زیر را انجام می دهید:
import {html, render} from 'lit';
const disabled = false;
const label = 'my label';
const myClass = 'my-class';
const value = 'my value';
const element = html`
<input
@click=${() => console.log('click')}
@input=${e => console.log(e.target.value)}>`;
render(
element,
mountNode
);
در مثال Lit، شنونده ای با @click
به رویداد click
اضافه شده است.
در مرحله بعد، به جای استفاده از onChange
، به رویداد input
بومی <input>
متصل میشود، زیرا رویداد change
بومی فقط روی blur
میشود (React Abstracts بر روی این رویدادها).
نحو کنترل کننده رویداد روشن
html`<my-element @event-name=${() => {...}}></my-element>`;
- پیشوند
@
نحو الزام آور برای شنونده رویداد است - معادل
inputRef.addEventListener('event-name', ...)
- از نامهای رویداد DOM بومی استفاده میکند
5. قطعات و لوازم
در این بخش با اجزا و توابع کلاس Lit آشنا می شوید. State و Hooks در بخشهای بعدی با جزئیات بیشتری پوشش داده میشوند.
اجزای کلاس و LitElement
معادل Lit یک جزء کلاس React LitElement است و مفهوم Lit از "ویژگی های واکنشی" ترکیبی از props و حالت React است. به عنوان مثال:
import React from 'react';
import ReactDOM from 'react-dom';
class Welcome extends React.Component {
constructor(props) {
super(props);
this.state = {name: ''};
}
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
const element = <Welcome name="Elliott"/>
ReactDOM.render(
element,
mountNode
);
در مثال بالا یک جزء React وجود دارد که:
- یک
name
ارائه می دهد - مقدار پیشفرض
name
روی رشته خالی تنظیم میکند (""
) - دوباره
name
به"Elliott"
اختصاص می دهد
این روشی است که در LitElement این کار را انجام می دهید
در TypeScript:
import {LitElement, html} from 'lit';
import {customElement, property} from 'lit/decorators.js';
@customElement('welcome-banner')
class WelcomeBanner extends LitElement {
@property({type: String})
name = '';
render() {
return html`<h1>Hello, ${this.name}</h1>`
}
}
در جاوا اسکریپت:
import {LitElement, html} from 'lit';
class WelcomeBanner extends LitElement {
static get properties() {
return {
name: {type: String}
}
}
constructor() {
super();
this.name = '';
}
render() {
return html`<h1>Hello, ${this.name}</h1>`
}
}
customElements.define('welcome-banner', WelcomeBanner);
و در فایل HTML:
<!-- index.html -->
<head>
<script type="module" src="./index.js"></script>
</head>
<body>
<welcome-banner name="Elliott"></welcome-banner>
</body>
مروری بر آنچه در مثال بالا اتفاق می افتد:
@property({type: String})
name = '';
- یک ویژگی واکنش پذیر عمومی را تعریف می کند - بخشی از API عمومی مؤلفه شما
- یک ویژگی (به طور پیش فرض) و همچنین یک ویژگی در کامپوننت شما را نشان می دهد
- نحوه ترجمه خصیصه جزء (که رشته ها هستند) را به مقدار تعریف می کند
static get properties() {
return {
name: {type: String}
}
}
- این همان عملکرد TS decorator
@property
را انجام می دهد اما به صورت بومی در جاوا اسکریپت اجرا می شود
render() {
return html`<h1>Hello, ${this.name}</h1>`
}
- هر زمان که هر خاصیت واکنشی تغییر کند این نام خوانده می شود
@customElement('welcome-banner')
class WelcomeBanner extends LitElement {
...
}
- این یک نام تگ عنصر HTML را با تعریف کلاس مرتبط می کند
- با توجه به استاندارد Custom Elements، نام تگ باید دارای خط فاصله (-) باشد.
-
this
در یک LitElement به نمونه عنصر سفارشی اشاره دارد (<welcome-banner>
در این مورد)
customElements.define('welcome-banner', WelcomeBanner);
- این معادل جاوا اسکریپت دکوراتور
@customElement
TS است
<head>
<script type="module" src="./index.js"></script>
</head>
- تعریف عنصر سفارشی را وارد می کند
<body>
<welcome-banner name="Elliott"></welcome-banner>
</body>
- عنصر سفارشی را به صفحه اضافه می کند
- ویژگی
name
را روی'Elliott'
تنظیم می کند
اجزای تابع
Lit تفسیر 1:1 از یک جزء تابع را ندارد زیرا از JSX یا پیش پردازنده استفاده نمی کند. اگرچه، نوشتن تابعی که ویژگی ها را می گیرد و DOM را بر اساس آن ویژگی ها رندر می کند، بسیار ساده است. به عنوان مثال:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Elliott"/>
ReactDOM.render(
element,
mountNode
);
در Lit این خواهد بود:
import {html, render} from 'lit';
function Welcome(props) {
return html`<h1>Hello, ${props.name}</h1>`;
}
render(
Welcome({name: 'Elliott'}),
document.body.querySelector('#root')
);
6. حالت و چرخه حیات
در این بخش با وضعیت و چرخه حیات Lit آشنا خواهید شد.
ایالت
مفهوم Lit از «ویژگیهای واکنشپذیر» ترکیبی از حالت و ویژگیهای React است. ویژگی های واکنشی، زمانی که تغییر می کنند، می توانند چرخه عمر کامپوننت را فعال کنند. خواص واکنشی در دو نوع وجود دارد:
خواص واکنش عمومی
// React
import React from 'react';
class MyEl extends React.Component {
constructor(props) {
super(props)
this.state = {name: 'there'}
}
componentWillReceiveProps(nextProps) {
if (this.props.name !== nextProps.name) {
this.setState({name: nextProps.name})
}
}
}
// Lit (TS)
import {LitElement} from 'lit';
import {property} from 'lit/decorators.js';
class MyEl extends LitElement {
@property() name = 'there';
}
- تعریف شده توسط
@property
- مشابه ویژگیها و حالتهای React اما قابل تغییر است
- API عمومی که توسط مصرف کنندگان کامپوننت قابل دسترسی و تنظیم است
حالت واکنشی داخلی
// React
import React from 'react';
class MyEl extends React.Component {
constructor(props) {
super(props)
this.state = {name: 'there'}
}
}
// Lit (TS)
import {LitElement} from 'lit';
import {state} from 'lit/decorators.js';
class MyEl extends LitElement {
@state() name = 'there';
}
- توسط
@state
تعریف شده است - مشابه حالت React اما قابل تغییر است
- حالت داخلی خصوصی که معمولاً از داخل مؤلفه یا زیر کلاس ها قابل دسترسی است
چرخه زندگی
چرخه عمر Lit کاملاً شبیه به React است اما تفاوت های قابل توجهی وجود دارد.
constructor
// React (js)
import React from 'react';
class MyEl extends React.Component {
constructor(props) {
super(props);
this.state = { counter: 0 };
this._privateProp = 'private';
}
}
// Lit (ts)
class MyEl extends LitElement {
@property({type: Number}) counter = 0;
private _privateProp = 'private';
}
// Lit (js)
class MyEl extends LitElement {
static get properties() {
return { counter: {type: Number} }
}
constructor() {
this.counter = 0;
this._privateProp = 'private';
}
}
- معادل نور نیز
constructor
است - نیازی به انتقال چیزی به فراخوانی نیست
- فراخوان شده توسط (نه کاملاً شامل):
-
document.createElement
-
document.innerHTML
-
new ComponentClass()
- اگر یک نام تگ ارتقا نیافته در صفحه باشد و تعریف با
@customElement
یاcustomElements.define
بارگیری و ثبت شده باشد.
-
- از نظر عملکرد مشابه
constructor
React است
render
// React
render() {
return <div>Hello World</div>
}
// Lit
render() {
return html`<div>Hello World</div>`;
}
- معادل نور نیز
render
است - می تواند هر نتیجه قابل رندر را برگرداند، به عنوان مثال
TemplateResult
یاstring
و غیره. - مشابه React،
render()
باید یک تابع خالص باشد - به هر گره ای که
createRenderRoot()
برگرداند رندر می شود (به طور پیش فرضShadowRoot
)
componentDidMount
componentDidMount
شبیه به ترکیبی از هر دو تماسهای چرخه حیات firstUpdated
و connectedCallback
Lit است.
firstUpdated
import Chart from 'chart.js';
// React
componentDidMount() {
this._chart = new Chart(this.chartElRef.current, {...});
}
// Lit
firstUpdated() {
this._chart = new Chart(this.chartEl, {...});
}
- اولین باری که قالب کامپوننت در ریشه کامپوننت رندر می شود فراخوانی می شود
- فقط زمانی فراخوانی می شود که عنصر متصل باشد، مثلاً از طریق
document.createElement('my-component')
فراخوانی نشود تا زمانی که آن گره به درخت DOM اضافه شود. - این مکان مناسبی برای انجام تنظیمات کامپوننت است که به DOM ارائه شده توسط کامپوننت نیاز دارد
- برخلاف React's
componentDidMount
، تغییرات به ویژگیهای واکنشی درfirstUpdated
باعث رندر مجدد میشود، اگرچه مرورگر معمولاً تغییرات را در همان قاب دستهبندی میکند. اگر این تغییرات نیازی به دسترسی به DOM ریشه ندارند، معمولاً باید بهwillUpdate
بروند
connectedCallback
// React
componentDidMount() {
this.window.addEventListener('resize', this.boundOnResize);
}
// Lit
connectedCallback() {
super.connectedCallback();
this.window.addEventListener('resize', this.boundOnResize);
}
- هر زمان که عنصر سفارشی در درخت DOM درج شود، فراخوانی می شود
- برخلاف کامپوننتهای React، زمانی که عناصر سفارشی از DOM جدا میشوند، از بین نمیروند و بنابراین میتوان چندین بار به آنها متصل شد.
-
firstUpdated
دوباره فراخوانی نخواهد شد
-
- مفید برای راهاندازی مجدد DOM یا پیوست کردن مجدد شنوندههای رویدادی که در هنگام قطع ارتباط پاکسازی شدهاند.
- توجه: ممکن است
connectedCallback
قبل ازfirstUpdated
تماس گرفته شود، بنابراین در اولین تماس ممکن است DOM در دسترس نباشد
componentDidUpdate
// React
componentDidUpdate(prevProps) {
if (this.props.title !== prevProps.title) {
this._chart.setTitle(this.props.title);
}
}
// Lit (ts)
updated(prevProps: PropertyValues<this>) {
if (prevProps.has('title')) {
this._chart.setTitle(this.title);
}
}
- معادل روشن
updated
می شود (با استفاده از زمان گذشته انگلیسی "به روز رسانی") - برخلاف React،
updated
نیز در رندر اولیه فراخوانی می شود - عملکرد مشابه React's
componentDidUpdate
componentWillUnmount
// React
componentWillUnmount() {
this.window.removeEventListener('resize', this.boundOnResize);
}
// Lit
disconnectedCallback() {
super.disconnectedCallback();
this.window.removeEventListener('resize', this.boundOnResize);
}
- معادل روشن شبیه به
disconnectedCallback
است - برخلاف کامپوننتهای React، وقتی عناصر سفارشی از DOM جدا میشوند، کامپوننت از بین نمیرود
- بر خلاف
componentWillUnmount
،disconnectedCallback
پس از حذف عنصر از درخت فراخوانی می شود - DOM داخل ریشه هنوز به زیر درخت ریشه متصل است
- مفید برای پاکسازی شنوندگان رویداد و مراجع لو رفته به طوری که مرورگر بتواند کامپوننت را جمع آوری زباله کند
ورزش کنید
import React from 'react';
import ReactDOM from 'react-dom';
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
در مثال بالا، یک ساعت ساده وجود دارد که کارهای زیر را انجام می دهد:
- "Hello World! It is" را رندر می کند و سپس زمان را نمایش می دهد
- هر ثانیه ساعت را به روز می کند
- هنگام پیاده شدن، فاصله زمانی که تیک را فراخوانی می کند پاک می کند
ابتدا با اعلان کلاس جزء شروع کنید:
// Lit (TS)
// some imports here are imported in advance
import {LitElement, html} from 'lit';
import {customElement, state} from 'lit/decorators.js';
@customElement('lit-clock')
class LitClock extends LitElement {
}
// Lit (JS)
// `html` is imported in advance
import {LitElement, html} from 'lit';
class LitClock extends LitElement {
}
customElements.define('lit-clock', LitClock);
در مرحله بعد، date
مقداردهی اولیه کنید و آن را یک ویژگی واکنشی داخلی با @state
اعلام کنید، زیرا کاربران مؤلفه مستقیماً date
تنظیم نمی کنند.
// Lit (TS)
import {LitElement, html} from 'lit';
import {customElement, state} from 'lit/decorators.js';
@customElement('lit-clock')
class LitClock extends LitElement {
@state() // declares internal reactive prop
private date = new Date(); // initialization
}
// Lit (JS)
import {LitElement, html} from 'lit';
class LitClock extends LitElement {
static get properties() {
return {
// declares internal reactive prop
date: {state: true}
}
}
constructor() {
super();
// initialization
this.date = new Date();
}
}
customElements.define('lit-clock', LitClock);
در مرحله بعد، قالب را رندر کنید.
// Lit (JS & TS)
render() {
return html`
<div>
<h1>Hello, World!</h1>
<h2>It is ${this.date.toLocaleTimeString()}.</h2>
</div>
`;
}
حال روش تیک را اجرا کنید.
tick() {
this.date = new Date();
}
بعد پیاده سازی componentDidMount
می آید. باز هم، آنالوگ Lit ترکیبی از firstUpdated
و connectedCallback
است. در مورد این کامپوننت، فراخوانی tick
با setInterval
نیازی به دسترسی به DOM داخل ریشه ندارد. علاوه بر این، زمانی که عنصر از درخت سند حذف میشود، فاصله زمانی پاک میشود، بنابراین اگر دوباره متصل شود، فاصله باید دوباره شروع شود. بنابراین، connectedCallback
در اینجا انتخاب بهتری است.
// Lit (TS)
@customElement('lit-clock')
class LitClock extends LitElement {
@state()
private date = new Date();
// initialize timerId for TS
private timerId = -1 as unknown as ReturnType<typeof setTimeout>;
connectedCallback() {
super.connectedCallback();
this.timerId = setInterval(
() => this.tick(),
1000
);
}
...
}
// Lit (JS)
constructor() {
super();
// initialization
this.date = new Date();
this.timerId = -1; // initialize timerId for JS
}
connectedCallback() {
super.connectedCallback();
this.timerId = setInterval(
() => this.tick(),
1000
);
}
در نهایت، فاصله را پاک کنید تا پس از جدا شدن عنصر از درخت سند، تیک را اجرا نکند.
// Lit (TS & JS)
disconnectedCallback() {
super.disconnectedCallback();
clearInterval(this.timerId);
}
با کنار هم گذاشتن همه، باید به شکل زیر باشد:
// Lit (TS)
import {LitElement, html} from 'lit';
import {customElement, state} from 'lit/decorators.js';
@customElement('lit-clock')
class LitClock extends LitElement {
@state()
private date = new Date();
private timerId = -1 as unknown as ReturnType<typeof setTimeout>;
connectedCallback() {
super.connectedCallback();
this.timerId = setInterval(
() => this.tick(),
1000
);
}
tick() {
this.date = new Date();
}
render() {
return html`
<div>
<h1>Hello, World!</h1>
<h2>It is ${this.date.toLocaleTimeString()}.</h2>
</div>
`;
}
disconnectedCallback() {
super.disconnectedCallback();
clearInterval(this.timerId);
}
}
// Lit (JS)
import {LitElement, html} from 'lit';
class LitClock extends LitElement {
static get properties() {
return {
date: {state: true}
}
}
constructor() {
super();
this.date = new Date();
}
connectedCallback() {
super.connectedCallback();
this.timerId = setInterval(
() => this.tick(),
1000
);
}
tick() {
this.date = new Date();
}
render() {
return html`
<div>
<h1>Hello, World!</h1>
<h2>It is ${this.date.toLocaleTimeString()}.</h2>
</div>
`;
}
disconnectedCallback() {
super.disconnectedCallback();
clearInterval(this.timerId);
}
}
customElements.define('lit-clock', LitClock);
7. قلاب
در این بخش با نحوه ترجمه مفاهیم React Hook به Lit آشنا می شوید.
مفاهیم React Hooks
قلابهای واکنش راهی را برای اجزای تابع فراهم میکنند تا به حالت «قلاب» شوند. چندین مزیت برای این وجود دارد.
- آنها استفاده مجدد از منطق حالتی را ساده می کنند
- به تقسیم یک جزء به توابع کوچکتر کمک کنید
علاوه بر این، تمرکز بر مؤلفههای مبتنی بر عملکرد، به مشکلات خاصی در نحو مبتنی بر کلاس React پرداخت، مانند:
- باید
props
ازconstructor
بهsuper
منتقل کنیم - مقداردهی اولیه نامرتب خصوصیات در
constructor
- این دلیلی بود که در آن زمان توسط تیم React بیان شد اما توسط ES2019 حل شد
- مشکلات ناشی از
this
دیگر به مؤلفه مربوط نمی شود
React Hooks مفاهیم در Lit
همانطور که در بخش Components & Props ذکر شد، Lit راهی برای ایجاد عناصر سفارشی از یک تابع ارائه نمی دهد، اما LitElement بیشتر مشکلات اصلی مولفه های کلاس React را برطرف می کند. به عنوان مثال:
// React (at the time of making hooks)
import React from 'react';
import ReactDOM from 'react-dom';
class MyEl extends React.Component {
constructor(props) {
super(props); // Leaky implementation
this.state = {count: 0};
this._chart = null; // Deemed messy
}
render() {
return (
<>
<div>Num times clicked {count}</div>
<button onClick={this.clickCallback}>click me</button>
</>
);
}
clickCallback() {
// Errors because `this` no longer refers to the component
this.setState({count: this.count + 1});
}
}
// Lit (ts)
class MyEl extends LitElement {
@property({type: Number}) count = 0; // No need for constructor to set state
private _chart = null; // Public class fields introduced to JS in 2019
render() {
return html`
<div>Num times clicked ${count}</div>
<button @click=${this.clickCallback}>click me</button>`;
}
private clickCallback() {
// No error because `this` refers to component
this.count++;
}
}
لیت چگونه به این مسائل رسیدگی می کند؟
-
constructor
هیچ آرگومان نمی پذیرد - همه bindings
@event
به صورت خودکار بهthis
متصل می شوند -
this
در اکثریت قریب به اتفاق موارد به مرجع عنصر سفارشی اشاره دارد - ویژگی های کلاس اکنون می توانند به عنوان اعضای کلاس نمونه سازی شوند. این کار پیاده سازی های مبتنی بر سازنده را پاک می کند
کنترلرهای واکنشی
مفاهیم اولیه پشت Hooks در Lit به عنوان کنترل کننده های واکنشی وجود دارد. الگوهای کنترل کننده واکنشی امکان به اشتراک گذاری منطق حالت، تقسیم اجزا به بیت های کوچکتر و مدولارتر و همچنین اتصال به چرخه حیات به روز رسانی یک عنصر را می دهد.
یک کنترلر واکنشی یک رابط شی است که می تواند به چرخه حیات به روز رسانی یک میزبان کنترل کننده مانند LitElement متصل شود.
چرخه حیات یک ReactiveController
و یک reactiveControllerHost
است:
interface ReactiveController {
hostConnected(): void;
hostUpdate(): void;
hostUpdated(): void;
hostDisconnected(): void;
}
interface ReactiveControllerHost {
addController(controller: ReactiveController): void;
removeController(controller: ReactiveController): void;
requestUpdate(): void;
readonly updateComplete: Promise<boolean>;
}
با ساختن یک کنترلر واکنشی و اتصال آن به یک میزبان با addController
، چرخه حیات کنترلر در کنار هاست فراخوانی می شود. به عنوان مثال، مثال ساعت را از بخش State & Lifecycle به یاد بیاورید:
import React from 'react';
import ReactDOM from 'react-dom';
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
در مثال بالا، یک ساعت ساده وجود دارد که کارهای زیر را انجام می دهد:
- "Hello World! It is" را رندر می کند و سپس زمان را نمایش می دهد
- هر ثانیه ساعت را به روز می کند
- هنگام پیاده شدن، فاصله زمانی که تیک را فراخوانی می کند پاک می کند
ساخت داربست جزء
ابتدا با اعلان کلاس کامپوننت شروع کنید و تابع render
را اضافه کنید.
// Lit (TS) - index.ts
import {LitElement, html} from 'lit';
import {customElement} from 'lit/decorators.js';
@customElement('my-element')
class MyElement extends LitElement {
render() {
return html`
<div>
<h1>Hello, world!</h1>
<h2>It is ${'time to get Lit'}.</h2>
</div>
`;
}
}
// Lit (JS) - index.js
import {LitElement, html} from 'lit';
class MyElement extends LitElement {
render() {
return html`
<div>
<h1>Hello, world!</h1>
<h2>It is ${'time to get Lit'}.</h2>
</div>
`;
}
}
customElements.define('my-element', MyElement);
ساخت کنترلر
اکنون به clock.ts
بروید و یک کلاس برای ClockController
بسازید و constructor
را تنظیم کنید:
// Lit (TS) - clock.ts
import {ReactiveController, ReactiveControllerHost} from 'lit';
export class ClockController implements ReactiveController {
private readonly host: ReactiveControllerHost;
constructor(host: ReactiveControllerHost) {
this.host = host;
host.addController(this);
}
hostConnected() {
}
private tick() {
}
hostDisconnected() {
}
}
// Lit (JS) - clock.js
export class ClockController {
constructor(host) {
this.host = host;
host.addController(this);
}
hostConnected() {
}
tick() {
}
hostDisconnected() {
}
}
یک کنترلر واکنشی میتواند به هر طریقی ساخته شود تا زمانی که رابط ReactiveController
به اشتراک بگذارد، اما استفاده از یک کلاس با constructor
که میتواند یک رابط ReactiveControllerHost
و همچنین هر ویژگی دیگری را که برای مقداردهی اولیه کنترلکننده لازم است استفاده کند، الگویی است که تیم Lit ترجیح میدهد. برای اکثر موارد اساسی استفاده کنید.
اکنون باید تماسهای چرخه حیات React را به تماسهای کنترلر ترجمه کنید. به طور خلاصه:
-
componentDidMount
- به LitElement's
connectedCallback
- به
hostConnected
کنترلر متصل است
- به LitElement's
-
ComponentWillUnmount
- به
disconnectedCallback
LitElement - به
hostDisconnected
کنترلر قطع شد
- به
برای اطلاعات بیشتر در مورد ترجمه چرخه عمر React به چرخه حیات Lit، به بخش State & Lifecycle مراجعه کنید.
در مرحله بعد، hostConnected
callback و متدهای tick
را پیاده سازی کنید و بازه زمانی در hostDisconnected
را همانطور که در مثال در بخش State & Lifecycle انجام شد پاک کنید.
// Lit (TS) - clock.ts
export class ClockController implements ReactiveController {
private readonly host: ReactiveControllerHost;
private interval = 0 as unknown as ReturnType<typeof setTimeout>;
date = new Date();
constructor(host: ReactiveControllerHost) {
this.host = host;
host.addController(this);
}
hostConnected() {
this.interval = setInterval(() => this.tick(), 1000);
}
private tick() {
this.date = new Date();
}
hostDisconnected() {
clearInterval(this.interval);
}
}
// Lit (JS) - clock.js
export class ClockController {
interval = 0;
host;
date = new Date();
constructor(host) {
this.host = host;
host.addController(this);
}
hostConnected() {
this.interval = setInterval(() => this.tick(), 1000);
}
tick() {
this.date = new Date();
}
hostDisconnected() {
clearInterval(this.interval);
}
}
با استفاده از کنترلر
برای استفاده از کنترلکننده ساعت، کنترلکننده را وارد کنید و مؤلفه را در index.ts
یا index.js
بهروزرسانی کنید.
// Lit (TS) - index.ts
import {LitElement, html, ReactiveController, ReactiveControllerHost} from 'lit';
import {customElement} from 'lit/decorators.js';
import {ClockController} from './clock.js';
@customElement('my-element')
class MyElement extends LitElement {
private readonly clock = new ClockController(this); // Instantiate
render() {
// Use controller
return html`
<div>
<h1>Hello, world!</h1>
<h2>It is ${this.clock.date.toLocaleTimeString()}.</h2>
</div>
`;
}
}
// Lit (JS) - index.js
import {LitElement, html} from 'lit';
import {ClockController} from './clock.js';
class MyElement extends LitElement {
clock = new ClockController(this); // Instantiate
render() {
// Use controller
return html`
<div>
<h1>Hello, world!</h1>
<h2>It is ${this.clock.date.toLocaleTimeString()}.</h2>
</div>
`;
}
}
customElements.define('my-element', MyElement);
برای استفاده از کنترلر، باید کنترلر را با ارسال یک مرجع به میزبان کنترلر (که جزء <my-element>
است) نمونه سازی کنید و سپس از کنترلر در روش render
استفاده کنید.
راه اندازی رندرهای مجدد در کنترلر
توجه داشته باشید که زمان را نشان می دهد، اما زمان به روز نمی شود. این به این دلیل است که کنترلر هر ثانیه تاریخ را تنظیم می کند، اما میزبان به روز نمی شود. این به این دلیل است که date
در کلاس ClockController
در حال تغییر است و دیگر کامپوننت تغییر نمی کند. این بدان معناست که پس از تنظیم date
روی کنترلر، باید به میزبان گفته شود که چرخه حیات به روز رسانی خود را با host.requestUpdate()
اجرا کند.
// Lit (TS & JS) - clock.ts / clock.js
private tick() {
this.date = new Date();
this.host.requestUpdate();
}
حالا ساعت باید تیک تاک کند!
برای مقایسه عمیق تر موارد استفاده رایج با قلاب، لطفاً به بخش موضوعات پیشرفته - قلاب مراجعه کنید.
8. کودکان
در این بخش نحوه استفاده از اسلات ها برای مدیریت کودکان در Lit را خواهید آموخت.
اسلات و کودکان
شکاف ها با اجازه دادن به شما در تودرتو کردن اجزا، ترکیب را فعال می کنند.
در React، کودکان از طریق props به ارث می رسند. اسلات پیشفرض props.children
است و تابع render
محل قرارگیری اسلات پیشفرض را مشخص میکند. به عنوان مثال:
const MyArticle = (props) => {
return <article>{props.children}</article>;
};
به خاطر داشته باشید که props.children
React Components هستند و نه عناصر HTML.
در Lit، کودکان در تابع رندر با عناصر اسلات ترکیب می شوند. توجه داشته باشید که کودکان به روشی مشابه React به ارث نمی رسند. در Lit، کودکان عناصر HTML هستند که به اسلات ها متصل هستند. این پیوست فرافکنی نامیده می شود.
@customElement("my-article")
export class MyArticle extends LitElement {
render() {
return html`
<article>
<slot></slot>
</article>
`;
}
}
اسلات های متعدد
در React، افزودن چند اسلات اساساً مشابه ارث بردن قطعات بیشتر است.
const MyArticle = (props) => {
return (
<article>
<header>
{props.headerChildren}
</header>
<section>
{props.sectionChildren}
</section>
</article>
);
};
به طور مشابه، افزودن عناصر <slot>
بیشتر اسلات های بیشتری را در Lit ایجاد می کند. اسلات های متعدد با ویژگی name
تعریف می شوند: <slot name="slot-name">
. این به کودکان اجازه می دهد تا اعلام کنند که کدام جایگاه به آنها اختصاص خواهد یافت.
@customElement("my-article")
export class MyArticle extends LitElement {
render() {
return html`
<article>
<header>
<slot name="headerChildren"></slot>
</header>
<section>
<slot name="sectionChildren"></slot>
</section>
</article>
`;
}
}
محتوای اسلات پیش فرض
اسلات ها زیردرخت خود را زمانی نمایش می دهند که هیچ گره ای در آن شکاف پیش بینی نشده باشد. هنگامی که گره ها به یک شکاف نمایش داده می شوند، آن شکاف درخت فرعی خود را نشان نمی دهد و در عوض گره های پیش بینی شده را نمایش می دهد.
@customElement("my-element")
export class MyElement extends LitElement {
render() {
return html`
<section>
<div>
<slot name="slotWithDefault">
<p>
This message will not be rendered when children are attached to this slot!
<p>
</slot>
</div>
</section>
`;
}
}
کودکان را به اسلات اختصاص دهید
در React، کودکان از طریق ویژگی های یک Component به اسلات اختصاص داده می شوند. در مثال زیر، عناصر React به headerChildren
و sectionChildren
props منتقل می شوند.
const MyNewsArticle = () => {
return (
<MyArticle
headerChildren={<h3>Extry, Extry! Read all about it!</h3>}
sectionChildren={<p>Children are props in React!</p>}
/>
);
};
در Lit، کودکان با استفاده از ویژگی slot
به اسلات اختصاص داده می شوند.
@customElement("my-news-article")
export class MyNewsArticle extends LitElement {
render() {
return html`
<my-article>
<h3 slot="headerChildren">
Extry, Extry! Read all about it!
</h3>
<p slot="sectionChildren">
Children are composed with slots in Lit!
</p>
</my-article>
`;
}
}
اگر شکاف پیشفرضی وجود نداشته باشد (مثلاً <slot>
) و هیچ شکافی وجود نداشته باشد که دارای ویژگی name
باشد (مثلاً <slot name="foo">
) که با ویژگی slot
فرزندان عنصر سفارشی مطابقت داشته باشد (به عنوان مثال <div slot="foo">
)، سپس آن گره نمایش داده نمی شود و نمایش داده نمی شود.
9. مراجع
گاهی اوقات، یک توسعه دهنده ممکن است نیاز به دسترسی به API یک HTMLElement داشته باشد.
در این بخش، نحوه به دست آوردن ارجاع عناصر در Lit را خواهید آموخت.
React References
یک مؤلفه React به یک سری فراخوانی تابع تبدیل می شود که در هنگام فراخوانی یک DOM مجازی ایجاد می کند. این DOM مجازی توسط ReactDOM تفسیر می شود و HTMLElements را ارائه می کند.
در React، Refs فضایی در حافظه است که حاوی یک HTMLElement تولید شده است.
const RefsExample = (props) => {
const inputRef = React.useRef(null);
const onButtonClick = React.useCallback(() => {
inputRef.current?.focus();
}, [inputRef]);
return (
<div>
<input type={"text"} ref={inputRef} />
<br />
<button onClick={onButtonClick}>
Click to focus on the input above!
</button>
</div>
);
};
در مثال بالا، کامپوننت React کارهای زیر را انجام می دهد:
- یک ورودی متن خالی و یک دکمه با متن ارائه دهید
- هنگامی که دکمه کلیک می شود، ورودی را متمرکز کنید
پس از رندر اولیه، React از طریق ویژگی ref
، inputRef.current
را به HTMLInputElement
تولید شده تنظیم می کند.
"References" را با @query
روشن کنید
Lit نزدیک به مرورگر زندگی می کند و یک انتزاع بسیار نازک از ویژگی های مرورگر بومی ایجاد می کند.
معادل React با refs
در Lit، HTMLElement است که توسط @query
و @queryAll
decorators برگردانده شده است.
@customElement("my-element")
export class MyElement extends LitElement {
@query('input') // Define the query
inputEl!: HTMLInputElement; // Declare the prop
// Declare the click event listener
onButtonClick() {
// Use the query to focus
this.inputEl.focus();
}
render() {
return html`
<input type="text">
<br />
<!-- Bind the click listener -->
<button @click=${this.onButtonClick}>
Click to focus on the input above!
</button>
`;
}
}
در مثال بالا، کامپوننت Lit کارهای زیر را انجام می دهد:
- با استفاده از decorator
@query
(ایجاد گیرنده برای یکHTMLInputElement
) یک ویژگی را درMyElement
تعریف می کند. - یک رویداد کلیکی را که
onButtonClick
نامیده می شود، اعلام و پیوست می کند. - ورودی را روی کلیک دکمه متمرکز می کند
در جاوا اسکریپت، دکوراتورهای @query
و @queryAll
به ترتیب querySelector
و querySelectorAll
را انجام می دهند. این معادل جاوا اسکریپت @query('input') inputEl!: HTMLInputElement;
get inputEl() {
return this.renderRoot.querySelector('input');
}
پس از اینکه کامپوننت Lit الگوی render
را به ریشه my-element
متعهد کرد، دکوراتور @query
اکنون به inputEl
اجازه میدهد اولین عنصر input
موجود در ریشه رندر را برگرداند. اگر @query
نتواند عنصر مشخص شده را پیدا کند، null
برمیگرداند.
اگر چندین عنصر input
در ریشه رندر وجود داشته باشد، @queryAll
لیستی از گره ها را برمی گرداند.
10. دولت میانجی
در این بخش، نحوه میانجیگری حالت بین کامپوننتها در Lit را یاد میگیرید.
قطعات قابل استفاده مجدد
React خطوط لوله رندر عملکردی را با جریان داده از بالا به پایین تقلید می کند. والدین از طریق وسایل به بچه ها حالت می دهند. کودکان از طریق تماسهای تلفنی موجود در وسایل ارتباطی با والدین ارتباط برقرار میکنند.
const CounterButton = (props) => {
const label = props.step < 0
? `- ${-1 * props.step}`
: `+ ${props.step}`;
return (
<button
onClick={() =>
props.addToCounter(props.step)}>{label}</button>
);
};
در مثال بالا، یک جزء React کارهای زیر را انجام می دهد:
- یک برچسب بر اساس مقدار
props.step
ایجاد می کند. - دکمه ای را با +step یا -step به عنوان برچسب آن ارائه می دهد
- مؤلفه والد را با فراخوانی
props.addToCounter
باprops.step
به عنوان آرگومان روی کلیک به روز می کند.
اگرچه امکان ارسال تماسهای بک در Lit وجود دارد، اما الگوهای معمولی متفاوت هستند. React Component در مثال بالا می تواند به عنوان یک Lit Component در مثال زیر نوشته شود:
@customElement('counter-button')
export class CounterButton extends LitElement {
@property({type: Number}) step: number = 0;
onClick() {
const event = new CustomEvent('update-counter', {
bubbles: true,
detail: {
step: this.step,
}
});
this.dispatchEvent(event);
}
render() {
const label = this.step < 0
? `- ${-1 * this.step}` // "- 1"
: `+ ${this.step}`; // "+ 1"
return html`
<button @click=${this.onClick}>${label}</button>
`;
}
}
در مثال بالا، یک Lit Component کارهای زیر را انجام می دهد:
-
step
خاصیت واکنشی را ایجاد کنید - یک رویداد سفارشی به نام
update-counter
ارسال کنید که مقدارstep
عنصر را با کلیک نشان میدهد
رویدادهای مرورگر از فرزندان به عناصر والدین حباب میشوند. رویدادها به کودکان امکان میدهند رویدادهای تعامل و تغییرات حالت را پخش کنند. React اساساً وضعیت را در جهت مخالف میگذراند، بنابراین دیدن React Components که رویدادها را به همان شیوه Lit Components ارسال میکند و به آن گوش میدهد غیرمعمول است.
مولفه های حالت دار
در React، استفاده از هوک ها برای مدیریت وضعیت رایج است. یک مؤلفه MyCounter
را می توان با استفاده مجدد از مؤلفه CounterButton
ایجاد کرد. به نحوه ارسال addToCounter
به هر دو نمونه CounterButton
توجه کنید.
const MyCounter = (props) => {
const [counterSum, setCounterSum] = React.useState(0);
const addToCounter = useCallback(
(step) => {
setCounterSum(counterSum + step);
},
[counterSum, setCounterSum]
);
return (
<div>
<h3>Σ: {counterSum}</h3>
<CounterButton
step={-1}
addToCounter={addToCounter} />
<CounterButton
step={1}
addToCounter={addToCounter} />
</div>
);
};
مثال بالا موارد زیر را انجام می دهد:
- یک حالت
count
ایجاد می کند. - یک تماس برگشتی ایجاد می کند که یک شماره را به وضعیت
count
اضافه می کند. -
CounterButton
ازaddToCounter
برای به روز رسانیcount
بهstep
در هر کلیک استفاده می کند.
پیاده سازی مشابه MyCounter
را می توان در Lit به دست آورد. توجه کنید که چگونه addToCounter
به counter-button
منتقل نمی شود. در عوض، callback به عنوان شنونده رویداد به رویداد @update-counter
در یک عنصر والد محدود میشود.
@customElement("my-counter")
export class MyCounter extends LitElement {
@property({type: Number}) count = 0;
addToCounter(e: CustomEvent<{step: number}>) {
// Get step from detail of event or via @query
this.count += e.detail.step;
}
render() {
return html`
<div @update-counter="${this.addToCounter}">
<h3>Σ ${this.count}</h3>
<counter-button step="-1"></counter-button>
<counter-button step="1"></counter-button>
</div>
`;
}
}
مثال بالا موارد زیر را انجام می دهد:
- یک ویژگی واکنشی به نام
count
ایجاد می کند که با تغییر مقدار، مؤلفه را به روز می کند - پاسخ تماس
addToCounter
را به شنونده رویداد@update-counter
متصل می کند - بهروزرسانیها با افزودن مقدار یافت شده در
detail.step
رویدادupdate-counter
count
- مقدار
step
counter-button
را از طریق ویژگیstep
تنظیم می کند
استفاده از ویژگیهای واکنشپذیر در Lit برای پخش تغییرات از والدین به فرزندان، مرسومتر است. به طور مشابه، استفاده از سیستم رویداد مرورگر برای حباب کردن جزئیات از پایین به بالا، تمرین خوبی است.
این رویکرد از بهترین شیوه ها پیروی می کند و به هدف Lit برای ارائه پشتیبانی بین پلتفرمی برای اجزای وب پایبند است.
11. یک ظاهر طراحی شده
در این بخش با استایل در Lit آشنا می شوید.
یک ظاهر طراحی شده
Lit راه های متعددی را برای استایل دادن به عناصر و همچنین راه حل داخلی ارائه می دهد.
سبک های درون خطی
Lit از سبک های درون خطی و همچنین اتصال به آنها پشتیبانی می کند.
import {LitElement, html, css} from 'lit';
import {customElement} from 'lit/decorators.js';
@customElement('my-element')
class MyElement extends LitElement {
render() {
return html`
<div>
<h1 style="color:orange;">This text is orange</h1>
<h1 style="color:rebeccapurple;">This text is rebeccapurple</h1>
</div>
`;
}
}
در مثال بالا 2 عنوان وجود دارد که هر کدام یک سبک درون خطی دارند.
اکنون مرزی را از border-color.js
به متن نارنجی وارد کرده و پیوند دهید:
...
import borderColor from './border-color.js';
...
html`
...
<h1 style="color:orange;${borderColor}">This text is orange</h1>
...`
محاسبه رشته سبک هر بار ممکن است کمی آزاردهنده باشد، بنابراین Lit دستورالعملی برای کمک به این موضوع ارائه می دهد.
styleMap
دستور styleMap
استفاده از جاوا اسکریپت را برای تنظیم سبک های درون خطی آسان تر می کند. به عنوان مثال:
import {LitElement, html, css} from 'lit';
import {customElement, property} from 'lit/decorators.js';
import {styleMap} from 'lit/directives/style-map.js';
@customElement('my-element')
class MyElement extends LitElement {
@property({type: String})
color = '#000'
render() {
// Define the styleMap
const headerStyle = styleMap({
'border-color': this.color,
});
return html`
<div>
<h1
style="border-style:solid;
<!-- Use the styleMap -->
border-width:2px;${headerStyle}">
This div has a border color of ${this.color}
</h1>
<input
type="color"
@input=${e => (this.color = e.target.value)}
value="#000">
</div>
`;
}
}
مثال بالا موارد زیر را انجام می دهد:
-
h1
با حاشیه و انتخابگر رنگ نمایش می دهد -
border-color
به مقدار انتخابگر رنگ تغییر می دهد
علاوه بر این، styleMap
وجود دارد که برای تنظیم سبک های h1
استفاده می شود. styleMap
از نحوی شبیه به نحو اتصال ویژگی style
React پیروی می کند.
CSSResult
روش پیشنهادی برای استایل دادن به کامپوننتها استفاده از قالب با برچسب css
به معنای واقعی کلمه است.
import {LitElement, html, css} from 'lit';
import {customElement} from 'lit/decorators.js';
const ORANGE = css`orange`;
@customElement('my-element')
class MyElement extends LitElement {
static styles = [
css`
#orange {
color: ${ORANGE};
}
#purple {
color: rebeccapurple;
}
`
];
render() {
return html`
<div>
<h1 id="orange">This text is orange</h1>
<h1 id="purple">This text is rebeccapurple</h1>
</div>
`;
}
}
مثال بالا موارد زیر را انجام می دهد:
- یک الگوی برچسب گذاری شده با CSS را به معنای واقعی کلمه با یک الزام آور اعلام می کند
- رنگ دو
h1
را با شناسه تنظیم می کند
مزایای استفاده از تگ قالب css
:
- یک بار در هر کلاس در مقابل هر نمونه تجزیه می شود
- با در نظر گرفتن قابلیت استفاده مجدد ماژول پیاده سازی شده است
- می تواند به راحتی استایل ها را در فایل های خود جدا کند
- سازگار با CSS Custom Properties polyfill
علاوه بر این، به تگ <style>
در index.html
توجه کنید:
<!-- index.html -->
<style>
h1 {
color: red !important;
}
</style>
Lit استایل اجزای شما را به ریشههای آنها محدود میکند. این بدان معنی است که استایل ها به داخل و خارج نشت نمی کنند. برای انتقال سبک ها به کامپوننت ها، تیم Lit استفاده از ویژگی های سفارشی CSS را توصیه می کند زیرا آنها می توانند به محدوده سبک Lit نفوذ کنند.
برچسب های سبک
همچنین میتوانید به سادگی تگهای <style>
را در قالبهای خود قرار دهید. مرورگر این تگهای سبک را کپی میکند، اما با قرار دادن آنها در قالبهای شما، آنها در هر نمونه کامپوننت به جای هر کلاس که در مورد الگوی برچسبگذاری شده css
وجود دارد، تجزیه میشوند. علاوه بر این، حذف مجدد CSSResult
s از مرورگر بسیار سریعتر است.
برچسب های پیوند
استفاده از <link rel="stylesheet">
در قالب شما نیز امکانی برای استایل ها است، اما این نیز توصیه نمی شود زیرا ممکن است باعث فلش اولیه محتوای بدون استایل (FOUC) شود.
12. موضوعات پیشرفته (اختیاری)
JSX و الگوسازی
DOM روشن و مجازی
Lit-html شامل یک DOM مجازی معمولی نمی شود که هر گره جداگانه را متفاوت کند. در عوض ، از ویژگی های عملکرد ذاتی در مورد الگوی برچسب زده شده ES2015 استفاده می کند. الگوی برچسب زده شده ، رشته های الگوی الگوی با توابع برچسب متصل به آنها هستند.
در اینجا نمونه ای از یک الگوی تحت اللفظی است:
const str = 'string';
console.log(`This is a template literal ${str}`);
در اینجا نمونه ای از یک الگوی برچسب زده شده است:
const tag = (strings, ...values) => ({strings, values});
const f = (x) => tag`hello ${x} how are you`;
console.log(f('world')); // {strings: ["hello ", " how are you"], values: ["world"]}
console.log(f('world').strings === f(1 + 2).strings); // true
در مثال بالا ، برچسب عملکرد tag
است و عملکرد f
فراخوانی از یک الگوی برچسب زده شده را برمی گرداند.
بسیاری از Magic Magic در LIT از این واقعیت ناشی می شود که آرایه های رشته ای که به عملکرد برچسب منتقل می شوند دارای یک نشانگر یکسان هستند (همانطور که در console.log
دوم نشان داده شده است. مرورگر یک آرایه strings
جدید را در هر دعوت عملکرد برچسب بازآفرینی نمی کند ، زیرا از همان الگوی تحت اللفظی استفاده می کند (یعنی در همان مکان در AST). بنابراین ، اتصال ، تجزیه و تحلیل و الگوی LIT می تواند از این ویژگی ها استفاده کند بدون اینکه در زمان اجرا بسیار متفاوت باشد.
این رفتار مرورگر داخلی از الگوی برچسب زده شده ، مزیت عملکردی را به شما می دهد. بیشتر دامنهای مجازی متعارف اکثر کار خود را در جاوا اسکریپت انجام می دهند. با این حال ، الگوی برچسب زده شده بیشتر تفاوت های خود را در C ++ مرورگر انجام می دهد.
اگر می خواهید با استفاده از الگوی الگوی برچسب HTML با React یا Preact استفاده کنید ، تیم LIT کتابخانه htm
توصیه می کند.
اگرچه ، همانطور که در سایت Google CodeLabs و چندین ویرایشگر کد آنلاین وجود دارد ، متوجه خواهید شد که برجسته سازی نحو الگوی برچسب بسیار رایج نیست. برخی از IDE ها و ویراستاران متن به طور پیش فرض مانند Atom و Highlighter CodeBlock Github از آنها پشتیبانی می کنند. تیم LIT همچنین برای حفظ پروژه هایی مانند lit-plugin
که یک افزونه VS Code است ، با جامعه بسیار نزدیک همکاری می کند و به پروژه های روشن شما اضافه می کند که به برجسته سازی ، نوع چک کردن و هوشمندی اضافه می کند.
Lit & JSX + React Dom
JSX در مرورگر اجرا نمی شود و در عوض از پیش پردازنده برای تبدیل JSX به تماس های عملکرد JavaScript (به طور معمول از طریق بابل) استفاده می کند.
به عنوان مثال ، بابل این را تغییر می دهد:
const element = <div className="title">Hello World!</div>;
ReactDOM.render(element, mountNode);
به این:
const element = React.createElement('div', {className: 'title'}, 'Hello World!');
ReactDOM.render(element, mountNode);
React Dom سپس خروجی React را می گیرد و آن را به DOM واقعی ترجمه می کند - خصوصیات ، ویژگی ها ، شنوندگان رویداد و همه.
LIT-HTML از الگوی الگوهای برچسب زده شده استفاده می کند که می تواند در مرورگر بدون انتقال یا پیش پردازنده اجرا شود. این بدان معنی است که برای شروع کار با LIT ، تمام آنچه شما نیاز دارید یک فایل HTML ، یک اسکریپت ماژول ES و یک سرور است. در اینجا یک فیلمنامه کاملاً مرورگر وجود دارد:
<!DOCTYPE html>
<html>
<head>
<script type="module">
import {html, render} from 'https://cdn.skypack.dev/lit';
render(
html`<div>Hello World!</div>`,
document.querySelector('.root')
)
</script>
</head>
<body>
<div class="root"></div>
</body>
</html>
علاوه بر این ، از آنجا که سیستم قالب بندی LIT ، LIT-HTML ، از DOM مجازی معمولی استفاده نمی کند بلکه از DOM API مستقیم استفاده می کند ، اندازه Lit 2 زیر 5 کیلوبایت مینیسم شده و در مقایسه با React (2.8kB) + React-Dom (39.4kB) gzipped است. 40 کیلوبایت کوچک شده و مجهز شده است.
رویدادها
React از یک سیستم رویداد مصنوعی استفاده می کند. این بدان معنی است که React-DOM باید هر رویدادی را که در هر مؤلفه مورد استفاده قرار می گیرد تعریف کند و یک شنونده رویداد شتر را برای هر نوع گره ارائه دهد. در نتیجه ، JSX روشی برای تعریف شنونده رویداد برای یک رویداد سفارشی ندارد و توسعه دهندگان باید از یک ref
استفاده کنند و سپس به طور ضرورتاً از شنونده استفاده کنند. این یک تجربه توسعه دهنده فرعی را هنگام ادغام کتابخانه هایی که در ذهن ندارند ، ایجاد می کند ، بنابراین منجر به نوشتن یک بسته بندی خاص واکنش می شود.
Lit-HTML به طور مستقیم به DOM دسترسی پیدا می کند و از رویدادهای بومی استفاده می کند ، بنابراین اضافه کردن شنوندگان رویداد به آسانی به عنوان @event-name=${eventNameListener}
آسان است. این بدان معنی است که تجزیه زمان کمتری برای اضافه کردن شنوندگان رویداد و همچنین شلیک رویدادها انجام می شود.
مؤلفه ها و غرفه ها
React مؤلفه ها و عناصر سفارشی
در زیر کاپوت ، LitElement از عناصر سفارشی برای بسته بندی اجزای خود استفاده می کند. عناصر سفارشی برخی از مبادلات بین مؤلفه های React را هنگام صحبت از مؤلفه ها معرفی می کنند (حالت و چرخه حیات بیشتر در بخش حالت و چرخه حیات مورد بحث قرار می گیرد).
برخی از مزایا عناصر سفارشی به عنوان یک سیستم مؤلفه دارند:
- بومی مرورگر و نیازی به ابزار ندارد
- در هر API مرورگر از
innerHTML
وdocument.createElement
تاquerySelector
قرار بگیرید - به طور معمول می تواند در چارچوب ها استفاده شود
- می توان با تنبلی در
customElements.define
و "Hydrate" DOM ثبت شد
برخی از عناصر سفارشی مضرات در مقایسه با مؤلفه های React:
- بدون تعریف یک کلاس نمی تواند یک عنصر سفارشی ایجاد کند (بنابراین هیچ مؤلفه عملکردی مانند JSX)
- باید حاوی یک برچسب بسته باشد
- توجه: علیرغم فروشندگان مرورگر راحتی توسعه دهند
- یک گره اضافی را به درخت DOM معرفی می کند که ممکن است باعث ایجاد مشکلات طرح شود
- باید از طریق JavaScript ثبت شود
LIT با عناصر سفارشی بیش از یک سیستم عناصر موزون رفته است زیرا عناصر سفارشی در مرورگر ساخته شده اند ، و تیم LIT معتقد است که مزایای چارچوب متقابل از مزایای ارائه شده توسط یک لایه انتزاع جزء است. در حقیقت ، تلاش های تیم LIT در فضای LIT-SSR با ثبت نام JavaScript بر موضوعات اصلی غلبه کرده است. علاوه بر این ، برخی از شرکت ها مانند GitHub از ثبت نام تنبل عنصر سفارشی استفاده می کنند تا به تدریج صفحات با استعداد اختیاری را ارتقا دهند.
انتقال داده ها به عناصر سفارشی
یک تصور غلط رایج با عناصر سفارشی این است که داده ها فقط به عنوان رشته ها قابل انتقال هستند. این تصور غلط احتمالاً از این واقعیت ناشی می شود که ویژگی های عناصر فقط به عنوان رشته ها می توانند نوشته شوند. اگرچه درست است که LIT ویژگی های رشته ای را به انواع تعریف شده خود می بخشد ، عناصر سفارشی همچنین می توانند داده های پیچیده را به عنوان خواص بپذیرند.
به عنوان مثال - با توجه به تعریف زیر زیر:
// data-test.ts
import {LitElement, html} from 'lit';
import {customElement, property} from 'lit/decorators.js';
@customElement('data-test')
class DataTest extends LitElement {
@property({type: Number})
num = 0;
@property({attribute: false})
data = {a: 0, b: null, c: [html`<div>hello</div>`, html`<div>world</div>`]}
render() {
return html`
<div>num + 1 = ${this.num + 1}</div>
<div>data.a = ${this.data.a}</div>
<div>data.b = ${this.data.b}</div>
<div>data.c = ${this.data.c}</div>`;
}
}
یک num
واکنشی بدوی تعریف شده است که مقدار رشته یک ویژگی را به یک number
تبدیل می کند ، و سپس ساختار داده پیچیده با attribute:false
که کنترل ویژگی LIT را غیرفعال می کند.
این نحوه انتقال داده ها به این عنصر سفارشی است:
<head>
<script type="module">
import './data-test.js'; // loads element definition
import {html} from './data-test.js';
const el = document.querySelector('data-test');
el.data = {
a: 5,
b: null,
c: [html`<div>foo</div>`,html`<div>bar</div>`]
};
</script>
</head>
<body>
<data-test num="5"></data-test>
</body>
چرخه حیات
سایر تماسهای چرخه چرخه React
static getDerivedStateFromProps
هیچ معادل در LIT وجود ندارد زیرا Props و حالت هر دو ویژگی کلاس یکسان هستند
shouldComponentUpdate
- معادل LIT
shouldUpdate
است - بر خلاف React به اولین رندر فراخوانده شد
- در عملکرد مشابه با React's
shouldComponentUpdate
getSnapshotBeforeUpdate
در LIT ، getSnapshotBeforeUpdate
شبیه به update
و willUpdate
است
willUpdate
- قبل از
update
فراخوانده شده است - بر خلاف
getSnapshotBeforeUpdate
،willUpdate
قبل ازrender
خوانده می شود - تغییر در خصوصیات واکنشی در
willUpdate
چرخه بروزرسانی را دوباره تحریک نمی کند - مکان خوبی برای محاسبه مقادیر خاصیت که به سایر خصوصیات بستگی دارد و در بقیه فرآیند به روزرسانی استفاده می شود
- این روش در سرور در SSR فراخوانی می شود ، بنابراین دسترسی به DOM در اینجا توصیه نمی شود
update
- پس از
willUpdate
نامیده می شود - بر خلاف
getSnapshotBeforeUpdate
،update
قبل ازrender
خوانده می شود - در صورت تغییر
super.update
تغییرات در خصوصیات واکنشی درupdate
، چرخه بروزرسانی را دوباره تغییر نمی دهد - مکان خوبی برای گرفتن اطلاعات از DOM اطراف مؤلفه قبل از اینکه خروجی ارائه شده به DOM متعهد شود
- این روش در SSR به سرور فراخوانی نمی شود
سایر تماس های چرخه عمر روشن
چندین تماس برگشتی چرخه عمر وجود دارد که در بخش قبلی ذکر نشده است زیرا در React هیچ آنالوگ برای آنها وجود ندارد. آنها عبارتند از:
attributeChangedCallback
هنگامی که یکی از عنصر observedAttributes
تغییر یافته است ، فراخوانی می شود. هر دو observedAttributes
و attributeChangedCallback
بخشی از مشخصات عناصر سفارشی هستند و توسط Lit در زیر کاپوت اجرا می شوند تا یک ویژگی API برای عناصر روشن ارائه دهند.
adoptedCallback
هنگامی که این مؤلفه به یک سند جدید منتقل می شود ، به عنوان مثال از یک documentFragment
HTMLTemplateElement
به document
اصلی منتقل می شود. این پاسخ به تماس همچنین بخشی از مشخصات عناصر سفارشی است و فقط باید برای موارد استفاده پیشرفته در هنگام تغییر اسناد مورد استفاده قرار گیرد.
سایر روشها و خصوصیات چرخه عمر
این روش ها و خصوصیات اعضای کلاس هستند که می توانید با آنها تماس بگیرید ، نادیده بگیرید یا در انتظار کمک به دستکاری در روند چرخه عمر باشید.
updateComplete
این Promise
است که هنگامی که عنصر به روزرسانی را به پایان رساند ، حل می شود زیرا به روزرسانی و به روزرسانی های حیات ناهمزمان هستند. یک مثال:
async nextButtonClicked() {
this.step++;
// Wait for the next "step" state to render
await this.updateComplete;
this.dispatchEvent(new Event('step-rendered'));
}
getUpdateComplete
این روشی است که باید در صورت برطرف شدن updateComplete
برای سفارشی سازی بیش از حد مورد استفاده قرار گیرد. این زمانی متداول است که یک مؤلفه در حال ارائه یک مؤلفه کودک باشد و چرخه رندر آنها باید همگام باشد. به عنوان مثال
class MyElement extends LitElement {
...
async getUpdateComplete() {
await super.getUpdateComplete();
await this.myChild.updateComplete;
}
}
performUpdate
این روش همان چیزی است که به عنوان تماس های برگشتی چرخه عمر به روزرسانی می نامد. این امر به طور کلی لازم نیست به جز موارد نادری که به روزرسانی باید همزمان یا برای برنامه ریزی سفارشی انجام شود.
hasUpdated
اگر مؤلفه حداقل یک بار به روز شود ، این ویژگی true
است.
isConnected
بخشی از مشخصات عناصر سفارشی ، اگر این عنصر در حال حاضر به درخت اسناد اصلی وصل شود ، این ویژگی true
خواهد بود.
تجسم چرخه عمر به روزرسانی روشن
3 قسمت برای چرخه عمر به روزرسانی وجود دارد:
- پیش به روز رسانی
- به روز رسانی
- پس به روز
از پیش بروزرسانی
پس از requestUpdate
، به روزرسانی برنامه ریزی شده در انتظار است.
به روز رسانی
پس به روز
قلاب
چرا قلاب
قلاب ها برای موارد استفاده از مؤلفه های ساده که به حالت نیاز دارند ، به React معرفی شدند. در بسیاری از موارد ساده ، اجزای عملکرد با قلاب بسیار ساده تر و خواندنی تر از همتایان کامپوننت کلاس خود هستند. اگرچه ، هنگام معرفی به روزرسانی های حالت ناهمزمان و همچنین عبور داده ها بین قلاب یا جلوه ها ، الگوی قلاب ها تمایل به کفایت ندارند و یک راه حل مبتنی بر کلاس مانند کنترل کننده های واکنشی تمایل به درخشش دارد.
درخواست API قلاب و کنترل کننده
نوشتن یک قلاب که درخواست داده از یک API را دارد ، معمول است. به عنوان مثال ، از این مؤلفه عملکرد React استفاده کنید که موارد زیر را انجام می دهد:
-
index.tsx
- متن را ارائه می دهد
- پاسخ
useAPI
را ارائه می دهد- شناسه کاربر + نام کاربری
- پیام خطا
- 404 هنگام رسیدن به کاربر 11 (با طراحی)
- اگر API واکشی سقط شود ، خطای سقط جنین
- پیام بارگیری
- دکمه اکشن را ارائه می دهد
- کاربر بعدی: که API را برای کاربر بعدی واگذار می کند
- لغو: کدام یک از API را سقط می کند و خطایی را نشان می دهد
-
useApi.tsx
- یک قلاب سفارشی
useApi
را تعریف می کند - آیا یک شیء کاربر را از یک API واگذار می کند
- منتشر می شود:
- نام کاربری
- آیا Fetch در حال بارگیری است
- هر پیام خطایی
- پاسخ به تماس برای سقط جنین
- در صورت برچیدن ، سقط جنین در حال انجام است
- یک قلاب سفارشی
در اینجا اجرای کنترل کننده واکنشی LIT + است.
غذای آماده:
- کنترل کننده های واکنشی بیشتر شبیه قلاب های سفارشی هستند
- عبور داده های غیر قابل ارائه بین تماس و اثرات
- React از
useRef
برای انتقال داده ها بینuseEffect
وuseCallback
استفاده می کند - LIT از یک ملک کلاس خصوصی استفاده می کند
- React اساساً تقلید از رفتار یک ملک کلاس خصوصی است
- React از
علاوه بر این ، اگر شما واقعاً دوست دارید نحو مؤلفه عملکرد React را با قلاب ها اما همان محیط بی سیم LIT را دوست داشته باشید ، تیم LIT به شدت کتابخانه خالی از سکنه را توصیه می کند.
بچه ها
شکاف پیش فرض
هنگامی که به عناصر HTML یک ویژگی slot
داده نمی شود ، آنها به شکاف پیش فرض بی نام اختصاص می یابد. در مثال زیر ، MyApp
یک پاراگراف را به یک شکاف نامگذاری می کند. پاراگراف دیگر به طور پیش فرض به شکاف بی نام خواهد بود ".
@customElement("my-element")
export class MyElement extends LitElement {
render() {
return html`
<section>
<div>
<slot></slot>
</div>
<div>
<slot name="custom-slot"></slot>
</div>
</section>
`;
}
}
@customElement("my-app")
export class MyApp extends LitElement {
render() {
return html`
<my-element>
<p slot="custom-slot">
This paragraph will be placed in the custom-slot!
</p>
<p>
This paragraph will be placed in the unnamed default slot!
</p>
</my-element>
`;
}
}
به روزرسانی های اسلات
هنگامی که ساختار فرزندان شکاف تغییر می کند ، یک رویداد slotchange
اخراج می شود. یک مؤلفه روشن می تواند یک لیست کننده رویداد را به یک رویداد slotchange
متصل کند. در مثال زیر ، اولین شکاف موجود در shadowRoot
، نودیدهای اختصاصی خود را به کنسول در slotchange
وارد می کنند.
@customElement("my-element")
export class MyElement extends LitElement {
onSlotChange(e: Event) {
const slot = this.shadowRoot.querySelector('slot');
console.log(slot.assignedNodes({flatten: true}));
}
render() {
return html`
<section>
<div>
<slot @slotchange="{this.onSlotChange}"></slot>
</div>
</section>
`;
}
}
مراجع
تولید مرجع
پس از فراخوانی توابع render
آنها ، هر دو را روشن و واکنش نشان می دهد. اما ارزش این را دارد که بررسی کنید که چگونه React و Lit DOM را که بعداً از طریق یک @query
Litquery یا یک مرجع React بازگردانده می شود ، بازگرداند.
React یک خط لوله کاربردی است که اجزای React را ایجاد می کند نه htmlelements. از آنجا که قبل از ارائه HTMLElement ، یک Ref اعلام می شود ، فضایی در حافظه اختصاص می یابد. به همین دلیل است که شما null
به عنوان مقدار اولیه یک Ref می بینید ، زیرا عنصر DOM واقعی هنوز ایجاد نشده است (یا ارائه شده است) یعنی useRef(null)
.
پس از Reactdom یک مؤلفه React را به یک HTMLElement تبدیل می کند ، به دنبال یک ویژگی به نام ref
در ReactComponent است. در صورت وجود ، Reactdom مرجع HtmlElement را به ref.current
ارائه می دهد.
LitElement از تابع برچسب الگوی html
از LIT-HTML برای تهیه یک عنصر الگوی زیر هود استفاده می کند. LitElement محتویات الگو را به DOM سایه یک عنصر سفارشی پس از ارائه می دهد. Shadow Dom یک درخت DOM Scoped است که توسط یک ریشه سایه محصور شده است. دکوراتور @query
سپس یک ویژگی را برای این ملک ایجاد می کند که اساساً یک this.shadowRoot.querySelector
را روی ریشه scoped انجام می دهد.
چندین عنصر را پرس و جو کنید
در مثال زیر ، دکوراتور @queryAll
دو پاراگراف را در ریشه سایه به عنوان NodeList
باز می گرداند.
@customElement("my-element")
export class MyElement extends LitElement {
@queryAll('p')
paragraphs!: NodeList;
render() {
return html`
<p>Hello, world!</p>
<p>How are you?</p>
`;
}
}
در اصل ، @queryAll
برای paragraphs
که نتایج this.shadowRoot.querySelectorAll()
برمی گرداند ، یک گیرنده ایجاد می کند. در JavaScript ، یک گیرنده را می توان برای انجام همان هدف اعلام کرد:
get paragraphs() {
return this.renderRoot.querySelectorAll('p');
}
عناصر تغییر پرس و جو
دکوراتور @queryAsync
برای کنترل گره ای که می تواند براساس وضعیت خاصیت عنصر دیگری تغییر کند ، مناسب تر است.
در مثال زیر ، @queryAsync
اولین عنصر پاراگراف را پیدا می کند. با این حال ، یک عنصر پاراگراف فقط هنگامی ارائه می شود که renderParagraph
به طور تصادفی یک عدد عجیب و غریب ایجاد می کند. دستورالعمل @queryAsync
وعده ای را برمی گرداند که در صورت موجود بودن پاراگراف اول برطرف می شود.
@customElement("my-dissappearing-paragraph")
export class MyDisapppearingParagraph extends LitElement {
@queryAsync('p')
paragraph!: Promise<HTMLElement>;
renderParagraph() {
const randomNumber = Math.floor(Math.random() * 10)
if (randomNumber % 2 === 0) {
return "";
}
return html`<p>This checkbox is checked!`
}
render() {
return html`
${this.renderParagraph()}
`;
}
}
حالت واسطه
در React ، کنوانسیون استفاده از تماس تلفنی است زیرا دولت توسط خود React واسطه می شود. React بهتر است به وضعیت ارائه شده توسط عناصر اعتماد نکنید. DOM به سادگی تأثیر روند ارائه است.
حالت خارجی
می توان از Redux ، Mobx یا هر کتابخانه مدیریت دولت دیگر در کنار Lit استفاده کرد.
اجزای روشن در دامنه مرورگر ایجاد می شوند. بنابراین هر کتابخانه ای که در دامنه مرورگر نیز وجود دارد ، در دسترس Lit است. بسیاری از کتابخانه های شگفت انگیز برای استفاده از سیستم های مدیریت دولت موجود در Lit ساخته شده اند.
در اینجا یک سریال توسط Vaadin توضیح داده شده است که چگونه می توان Redux را در یک جزء روشن استفاده کرد.
نگاهی به Lit-Mobx از Adobe بیندازید تا ببینید که چگونه یک سایت در مقیاس بزرگ می تواند Mobx را در Lit ایجاد کند.
همچنین ، عناصر Apollo را بررسی کنید تا ببینید که چگونه توسعه دهندگان از جمله GraphQL در مؤلفه های وب خود هستند.
LIT با ویژگی های مرورگر بومی کار می کند و بیشتر راه حل های مدیریت ایالتی در دامنه مرورگر می تواند در یک جزء LIT استفاده شود.
یک ظاهر طراحی شده
سایه DOM
برای محصور کردن سبک ها و DOM در یک عنصر سفارشی ، Lit از Shadow Dom استفاده می کند. ریشه های سایه درخت سایه ای جدا از درخت اسناد اصلی تولید می کنند. این بدان معناست که بیشتر سبک ها به این سند دست و پنجه نرم می کنند. برخی از سبک های خاص مانند رنگ و سایر سبک های مربوط به قلم نشت می کنند.
Shadow Dom همچنین مفاهیم و انتخاب کنندگان جدید را به مشخصات CSS معرفی می کند:
:host, :host(:hover), :host([hover]) {
/* Styles the element in which the shadow root is attached to */
}
slot[name="title"]::slotted(*), slot::slotted(:hover), slot::slotted([hover]) {
/*
* Styles the elements projected into a slot element. NOTE: the spec only allows
* styling the direcly slotted elements. Children of those elements are not stylable.
*/
}
به اشتراک گذاری سبک ها
LIT به اشتراک گذاشتن سبک های بین اجزای سازنده در قالب CSSTemplateResults
از طریق برچسب های الگوی css
آسان می شود. به عنوان مثال:
// typography.ts
export const body1 = css`
.body1 {
...
}
`;
// my-el.ts
import {body1} from './typography.ts';
@customElement('my-el')
class MyEl Extends {
static get styles = [
body1,
css`/* local styles come after so they will override bod1 */`
]
render() {
return html`<div class="body1">...</div>`
}
}
موضوع بندی
ریشه های سایه کمی برای مضامین معمولی که به طور معمول رویکردهای برچسب از بالا به پایین هستند ، وجود دارد. روش متعارف برای مقابله با مضامین با مؤلفه های وب که از Shadow DOM استفاده می کنند ، در معرض یک API سبک از طریق خواص سفارشی CSS است. به عنوان مثال ، این الگویی است که طراحی مواد از آن استفاده می کند:
.mdc-textfield-outline {
border-color: var(--mdc-theme-primary, /* default value */ #...);
}
.mdc-textfield--input {
caret-color: var(--mdc-theme-primary, #...);
}
کاربر سپس با استفاده از مقادیر خاصیت سفارشی ، موضوع سایت را تغییر می دهد:
html {
--mdc-theme-primary: #F00;
}
html[dark] {
--mdc-theme-primary: #F88;
}
اگر مضامین از بالا به پایین یک ضرورت است و شما قادر به افشای سبک ها نیستید ، همیشه می توانید Shadow Dom را با غلبه بر createRenderRoot
غیرفعال کنید تا this
برگردانید که سپس الگوی اجزای شما را به خود عنصر سفارشی تبدیل می کند نه اینکه به یک ریشه سایه متصل شود. به عنصر سفارشی با این کار از دست خواهید داد: محصور سازی سبک ، محاصره DOM و اسلات.
تولید
یعنی 11
اگر شما نیاز به پشتیبانی از مرورگرهای قدیمی تر مانند IE 11 دارید ، باید برخی از پلی فیل ها را که در حدود 33 کیلوبایت دیگر به وجود می آیند ، بارگیری کنید. اطلاعات بیشتر را می توان در اینجا یافت.
بسته های مشروط
تیم LIT توصیه می کند که دو بسته مختلف را ارائه دهید ، یکی برای IE 11 و دیگری برای مرورگرهای مدرن. چندین مزیت برای این وجود دارد:
- خدمت به ES 6 سریعتر است و به بیشتر مشتری های شما خدمت می کند
- ES 5 Transpiled به طور قابل توجهی اندازه بسته نرم افزاری را افزایش می دهد
- بسته های مشروط بهترین های هر دو جهان را به شما می دهند
- IE 11 پشتیبانی
- بدون کندی در مرورگرهای مدرن
اطلاعات بیشتر در مورد نحوه ساخت یک بسته نرم افزاری مشروط را می توان در سایت مستندات ما در اینجا یافت.