برای توسعه دهندگان React روشن شد

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 می شود.

نمودار میله ای اندازه بسته نرم افزاری کوچک شده و فشرده شده در کیلوبایت. نوار روشن 5 کیلوبایت و React + React DOM 42.2 کیلوبایت است

سریع است

در معیارهای عمومی که سیستم قالب‌سازی Lit، lit-html، را با VDOM React مقایسه می‌کنند، lit-html در بدترین حالت 8 تا 10 درصد سریع‌تر از React و در موارد استفاده رایج‌تر 50 درصد سریع‌تر ظاهر می‌شود.

LitElement (کلاس پایه کامپوننت Lit) حداقل سربار را به lit-html اضافه می کند، اما عملکرد React را در مقایسه با ویژگی های مؤلفه مانند استفاده از حافظه و تعامل و زمان راه اندازی 16 تا 30 درصد شکست می دهد .

نمودار میله ای گروه بندی شده عملکرد در مقایسه با روشنایی با React در میلی ثانیه (کمتر بهتر است)

نیازی به ساخت ندارد

با ویژگی‌های جدید مرورگر مانند ماژول‌های 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 همکاری می‌کند.

اسکرین شات یک IDE که بررسی نوع نامناسبی را برای تنظیم بولی مشخص شده به یک عدد نشان می دهد.

تصویری از یک IDE که پیشنهادهای هوشمندانه را نشان می دهد

ابزارهای توسعه دهنده در مرورگر تعبیه شده اند

اجزای روشن فقط عناصر HTML در DOM هستند . این بدان معناست که برای بازرسی اجزای خود، نیازی به نصب ابزار یا افزونه برای مرورگر خود ندارید .

شما به سادگی می توانید ابزارهای توسعه دهنده را باز کنید، یک عنصر را انتخاب کنید و ویژگی ها یا وضعیت آن را بررسی کنید.

تصویر ابزارهای توسعه‌دهنده Chrome که $0 را نشان می‌دهد <mwc-textfield>، $0.value hello world، $0.outlined را true، و {$0} گسترش ویژگی را نشان می‌دهد.

این با در نظر گرفتن رندر سمت سرور (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 استفاده می کنید، می توانید از این نقاط بازرسی برای دانلود کد شروع برای هر مرحله و همچنین برای بررسی کار خود استفاده کنید.

کاوش در رابط کاربری روشنایی زمین بازی

نوار برگه انتخابگر فایل دارای برچسب بخش 1، بخش ویرایش کد به عنوان بخش 2، پیش نمایش خروجی به عنوان بخش 3 و دکمه پیش نمایش بارگذاری مجدد به عنوان بخش 4 است.

اسکرین شات رابط کاربری زمین بازی Lit بخش هایی را که در این کد لبه استفاده خواهید کرد برجسته می کند.

  1. انتخابگر فایل به دکمه پلاس توجه کنید...
  2. ویرایشگر فایل.
  3. پیش نمایش کد.
  4. دکمه بارگذاری مجدد
  5. دکمه دانلود.

تنظیم VS Code (پیشرفته)

در اینجا مزایای استفاده از این تنظیمات VS Code وجود دارد:

  • بررسی نوع الگو
  • هوشمندسازی الگو و تکمیل خودکار

اگر NPM، VS Code (با افزونه lit-plugin ) را قبلاً نصب کرده‌اید و می‌دانید چگونه از آن محیط استفاده کنید، می‌توانید به سادگی این پروژه‌ها را با انجام کارهای زیر دانلود و شروع کنید:

  • دکمه دانلود را فشار دهید
  • محتویات فایل tar را در یک دایرکتوری استخراج کنید
  • (اگر TS) یک tsconfig سریع راه اندازی کنید که ماژول های es و es2015+ را خروجی می دهد
  • سرور توسعه دهنده ای را نصب کنید که بتواند مشخصه های ماژول خالی را حل کند (تیم Lit @web/dev-server را توصیه می کند)
  • سرور توسعه دهنده را اجرا کرده و مرورگر خود را باز کنید (اگر از @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 کنترلر متصل است
  • 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>&Sigma;: {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>&Sigma; ${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 قسمت برای چرخه عمر به روزرسانی وجود دارد:

  • پیش به روز رسانی
  • به روز رسانی
  • پس به روز

از پیش بروزرسانی

نمودار محرک گره ای از گره ها با نام پاسخ به تماس. سازنده درخواست Update. property به تنظیم کننده املاک. AttributeChangedCallback به تنظیم کننده املاک. تنظیم کننده املاک به Haschanged. به درخواست OUPDATE. RequestUpdate به بعدی اشاره می کند ، نمودار چرخه عمر را به روز کنید.

پس از requestUpdate ، به روزرسانی برنامه ریزی شده در انتظار است.

به روز رسانی

نمودار محرک گره ای از گره ها با نام پاسخ به تماس. فلش از تصویر قبلی از نقاط چرخه عمر پیش از به روزرسانی برای اجرای برنامه. اجرا به صورت ترکیبی. userupdate به هر دو "به روزرسانی کامل در صورت دروغ" و همچنین Willupdate اشاره می کند. Willupdate برای به روزرسانی. هر دو رندر و همچنین به نمودار چرخه عمر پس از به روزرسانی را به روز کنید. رندر همچنین به نمودار چرخه عمر پس از به روزرسانی اشاره می کند.

پس به روز

نمودار محرک گره ای از گره ها با نام پاسخ به تماس. فلش از تصویر قبلی نقاط چرخه عمر به روزرسانی به FirstUpdated. Firstupded برای به روز شده. به روزرسانی به روز شده.

قلاب

چرا قلاب

قلاب ها برای موارد استفاده از مؤلفه های ساده که به حالت نیاز دارند ، به 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 را با قلاب ها اما همان محیط بی سیم 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 پشتیبانی
    • بدون کندی در مرورگرهای مدرن

اطلاعات بیشتر در مورد نحوه ساخت یک بسته نرم افزاری مشروط را می توان در سایت مستندات ما در اینجا یافت.