প্রতিক্রিয়া বিকাশকারীদের জন্য আলো

১. ভূমিকা

লিট কী

Lit হলো দ্রুত ও হালকা ওয়েব কম্পোনেন্ট তৈরির একটি সহজ লাইব্রেরি, যা যেকোনো ফ্রেমওয়ার্কে, এমনকি কোনো ফ্রেমওয়ার্ক ছাড়াই কাজ করে। Lit ব্যবহার করে আপনি শেয়ারযোগ্য কম্পোনেন্ট, অ্যাপ্লিকেশন, ডিজাইন সিস্টেম এবং আরও অনেক কিছু তৈরি করতে পারেন।

আপনি যা শিখবেন

React-এর বিভিন্ন ধারণাকে Lit-এ কীভাবে অনুবাদ করা যায়, যেমন:

  • JSX এবং টেমপ্লেটিং
  • উপাদান ও সরঞ্জাম
  • অবস্থা ও জীবনচক্র
  • হুক
  • শিশুরা
  • রেফারেন্স
  • মধ্যস্থতাকারী রাষ্ট্র

আপনি যা তৈরি করবেন

এই কোডল্যাবটি শেষে রিয়্যাক্ট কম্পোনেন্টের ধারণাগুলোকে সেগুলোর লিট (Lit) প্রতিরূপে রূপান্তর করতে সক্ষম হবেন।

আপনার যা যা লাগবে

  • ক্রোম, সাফারি, ফায়ারফক্স বা এজ-এর সর্বশেষ সংস্করণ।
  • এইচটিএমএল, সিএসএস, জাভাস্ক্রিপ্ট এবং ক্রোম ডেভটুলস সম্পর্কে জ্ঞান।
  • রিঅ্যাক্ট সম্পর্কে জ্ঞান
  • (উন্নত) সেরা ডেভেলপমেন্ট অভিজ্ঞতার জন্য VS Code ডাউনলোড করুন। এছাড়াও আপনার lit-plugin for VS Code এবং NPM প্রয়োজন হবে।

২. সাহিত্য বনাম প্রতিক্রিয়া

Lit-এর মূল ধারণা এবং সক্ষমতা অনেক দিক থেকে React-এর অনুরূপ, কিন্তু Lit-এর কিছু গুরুত্বপূর্ণ পার্থক্য এবং স্বতন্ত্র বৈশিষ্ট্যও রয়েছে:

এটা ছোট

Lit খুবই ছোট: মিনিফাইড এবং জিপড করার পর এর সাইজ দাঁড়ায় প্রায় ৫ কিলোবাইট, যেখানে React + ReactDOM-এর সাইজ ৪০+ কিলোবাইট

মিনিফাইড ও কম্প্রেসড বান্ডেল সাইজের (কেবি-তে) বার চার্ট। আলোকিত বারটি ৫ কেবি এবং রিয়্যাক্ট + রিয়্যাক্ট ডোম ৪২.২ কেবি।

এটা দ্রুত

পাবলিক বেঞ্চমার্কে , যেখানে Lit-এর টেমপ্লেটিং সিস্টেম lit-html-কে React-এর VDOM-এর সাথে তুলনা করা হয়, সেখানে সবচেয়ে খারাপ পরিস্থিতিতে lit-html, React-এর চেয়ে ৮-১০% দ্রুততর এবং সাধারণ ব্যবহারের ক্ষেত্রে ৫০%-এরও বেশি দ্রুততর প্রমাণিত হয়েছে।

LitElement (Lit-এর কম্পোনেন্ট বেস ক্লাস) lit-html-এ সামান্য ওভারহেড যোগ করে, কিন্তু মেমরি ব্যবহার, ইন্টারঅ্যাকশন এবং স্টার্টআপ টাইমের মতো কম্পোনেন্ট ফিচারগুলোর তুলনা করলে এটি React-এর পারফরম্যান্সকে ১৬-৩০% পর্যন্ত ছাড়িয়ে যায়

মিলিসেকেন্ডে lit এবং React-এর পারফরম্যান্সের তুলনামূলক গ্রুপড বার চার্ট (মান যত কম, পারফরম্যান্স তত ভালো)।

নির্মাণের প্রয়োজন নেই

ES মডিউল এবং ট্যাগড টেমপ্লেট লিটারেলের মতো নতুন ব্রাউজার ফিচারগুলোর কারণে, Lit চালানোর জন্য আর কম্পাইলেশনের প্রয়োজন হয় না । এর মানে হলো, একটি স্ক্রিপ্ট ট্যাগ, একটি ব্রাউজার এবং একটি সার্ভার দিয়েই ডেভেলপমেন্ট এনভায়রনমেন্ট সেট আপ করে কাজ শুরু করা যায়।

ES মডিউল এবং Skypack বা UNPKG-এর মতো আধুনিক CDN-এর সাহায্যে, কাজ শুরু করার জন্য আপনার হয়তো NPM-এরও প্রয়োজন হবে না!

তবে, আপনি চাইলে এখনও লিট কোড তৈরি এবং অপ্টিমাইজ করতে পারেন । নেটিভ ইএস মডিউলকে কেন্দ্র করে সাম্প্রতিক ডেভেলপার একীকরণ লিটের জন্য ভালো হয়েছে – লিট হলো সাধারণ জাভাস্ক্রিপ্ট এবং এর জন্য ফ্রেমওয়ার্ক-নির্দিষ্ট সিএলআই বা বিল্ড হ্যান্ডলিংয়ের কোনো প্রয়োজন নেই

ফ্রেমওয়ার্ক অজ্ঞ

Lit-এর কম্পোনেন্টগুলো ওয়েব কম্পোনেন্টস নামক একগুচ্ছ ওয়েব স্ট্যান্ডার্ডের উপর ভিত্তি করে তৈরি। এর মানে হলো, Lit-এ কোনো কম্পোনেন্ট তৈরি করলে তা বর্তমান ও ভবিষ্যতের ফ্রেমওয়ার্কগুলোতেও কাজ করবে । যদি এটি HTML এলিমেন্ট সমর্থন করে, তবে এটি ওয়েব কম্পোনেন্টসও সমর্থন করবে।

ফ্রেমওয়ার্কগুলোর মধ্যে পারস্পরিক যোগাযোগের ক্ষেত্রে একমাত্র সমস্যা তখনই দেখা দেয়, যখন ফ্রেমওয়ার্কগুলো DOM-এর জন্য সীমাবদ্ধ সমর্থন প্রদান করে। React এই ধরনের একটি ফ্রেমওয়ার্ক, কিন্তু এটি Refs-এর মাধ্যমে এস্কেপ হ্যাচের সুযোগ দেয়, এবং React-এ Refs ব্যবহার করা ডেভেলপারদের জন্য ভালো অভিজ্ঞতা নয়।

লিট টিম @lit-labs/react নামে একটি পরীক্ষামূলক প্রজেক্ট নিয়ে কাজ করছে, যেটি স্বয়ংক্রিয়ভাবে আপনার লিট কম্পোনেন্টগুলো পার্স করে একটি রিয়্যাক্ট র‍্যাপার তৈরি করবে, ফলে আপনাকে আর রেফস (refs) ব্যবহার করতে হবে না।

এছাড়াও, Custom Elements Everywhere আপনাকে দেখাবে কোন ফ্রেমওয়ার্ক এবং লাইব্রেরিগুলো কাস্টম এলিমেন্টের সাথে ভালোভাবে কাজ করে!

প্রথম শ্রেণীর টাইপস্ক্রিপ্ট সমর্থন

যদিও আপনার Lit-এর সমস্ত কোড জাভাস্ক্রিপ্টে লেখা সম্ভব, Lit টাইপস্ক্রিপ্টে লেখা হয় এবং Lit টিম ডেভেলপারদেরও টাইপস্ক্রিপ্ট ব্যবহার করার পরামর্শ দেয়!

লিট টিম lit-analyzer এবং lit-plugin ব্যবহার করে লিট টেমপ্লেটগুলিতে ডেভেলপমেন্ট এবং বিল্ড উভয় পর্যায়েই টাইপস্ক্রিপ্ট টাইপ চেকিং এবং ইন্টেলিসেন্স নিয়ে আসার প্রকল্পগুলি রক্ষণাবেক্ষণে লিট কমিউনিটির সাথে কাজ করে আসছে।

একটি IDE-র স্ক্রিনশট, যেখানে চিহ্নিত বুলিয়ানটিকে একটি সংখ্যায় সেট করার ক্ষেত্রে অনুপযুক্ত টাইপ চেক দেখানো হচ্ছে।

একটি IDE-র স্ক্রিনশট যেখানে ইন্টেলিসেন্স সাজেশন দেখানো হচ্ছে

ডেভ টুলগুলো ব্রাউজারের মধ্যেই তৈরি করা থাকে।

লিট কম্পোনেন্টগুলো হলো DOM-এর এইচটিএমএল এলিমেন্ট । এর মানে হলো, আপনার কম্পোনেন্টগুলো পরিদর্শন করার জন্য ব্রাউজারে কোনো টুল বা এক্সটেনশন ইনস্টল করার প্রয়োজন নেই

আপনি খুব সহজেই ডেভ টুলস খুলে, একটি এলিমেন্ট নির্বাচন করে তার প্রোপার্টি বা স্টেট অন্বেষণ করতে পারেন।

ক্রোম ডেভ টুলসের একটি চিত্র যেখানে দেখানো হচ্ছে $0 দিলে <mwc-textfield> রিটার্ন হয়, $0.value দিলে hello world রিটার্ন হয়, $0.outlined দিলে true রিটার্ন হয়, এবং {$0} দিলে প্রপার্টি এক্সপ্যানশন দেখা যায়।

এটি সার্ভার সাইড রেন্ডারিং (SSR) মাথায় রেখে তৈরি করা হয়েছে।

Lit 2-কে SSR সাপোর্টের কথা মাথায় রেখে তৈরি করা হয়েছে। এই কোডল্যাবটি লেখার সময় পর্যন্ত, Lit টিম এখনও SSR টুলগুলোকে একটি স্থিতিশীল রূপে প্রকাশ করেনি, কিন্তু Lit টিম ইতোমধ্যেই Google-এর বিভিন্ন প্রোডাক্টে সার্ভার সাইড রেন্ডারড কম্পোনেন্ট ডেপ্লয় করছে এবং React অ্যাপ্লিকেশনের মধ্যে SSR পরীক্ষা করেছে। Lit টিম শীঘ্রই GitHub-এ এই টুলগুলো বাহ্যিকভাবে প্রকাশ করবে বলে আশা করছে।

এরই মধ্যে আপনারা এখানে লিট টিমের অগ্রগতি অনুসরণ করতে পারেন।

এতে বিনিয়োগের পরিমাণ কম।

Lit ব্যবহার করার জন্য খুব বেশি প্রতিশ্রুতিবদ্ধ হওয়ার প্রয়োজন নেই! আপনি Lit-এ কম্পোনেন্ট তৈরি করে আপনার বিদ্যমান প্রজেক্টে যোগ করতে পারেন। যদি সেগুলো আপনার পছন্দ না হয়, তবে আপনাকে একবারে পুরো অ্যাপটি রূপান্তর করতে হবে না, কারণ ওয়েব কম্পোনেন্টগুলো অন্যান্য ফ্রেমওয়ার্কেও কাজ করে!

আপনি কি Lit-এ একটি সম্পূর্ণ অ্যাপ তৈরি করেছেন এবং অন্য কিছুতে পরিবর্তন করতে চান? তাহলে, আপনি আপনার বর্তমান Lit অ্যাপ্লিকেশনটিকে আপনার নতুন ফ্রেমওয়ার্কের ভিতরে রাখতে পারেন এবং আপনার ইচ্ছামত সবকিছু নতুন ফ্রেমওয়ার্কের কম্পোনেন্টগুলোতে স্থানান্তর করতে পারেন।

এছাড়াও, অনেক আধুনিক ফ্রেমওয়ার্ক ওয়েব কম্পোনেন্টে আউটপুট সমর্থন করে , যার ফলে সেগুলোকে সাধারণত নিজেরাই একটি Lit এলিমেন্টের ভেতরে রাখা যায়।

৩. প্রস্তুতি গ্রহণ এবং খেলার মাঠটি ঘুরে দেখা

এই কোডল্যাবটি করার দুটি উপায় আছে:

  • আপনি এটি সম্পূর্ণ অনলাইনে, ব্রাউজারে করতে পারেন।
  • (উন্নত) আপনি VS Code ব্যবহার করে আপনার স্থানীয় মেশিনে এটি করতে পারেন।

কোড অ্যাক্সেস করা

পুরো কোডল্যাব জুড়ে লিট প্লেগ্রাউন্ডের লিঙ্কগুলো এইভাবে থাকবে:

প্লেগ্রাউন্ড হলো একটি কোড স্যান্ডবক্স যা সম্পূর্ণভাবে আপনার ব্রাউজারে চলে। এটি টাইপস্ক্রিপ্ট এবং জাভাস্ক্রিপ্ট ফাইল কম্পাইল ও রান করতে পারে এবং নোড মডিউলের ইম্পোর্টগুলোও স্বয়ংক্রিয়ভাবে সমাধান করতে পারে। যেমন:

// before
import './my-file.js';
import 'lit';

// after
import './my-file.js';
import 'https://cdn.skypack.dev/lit';

আপনি এই চেকপয়েন্টগুলোকে প্রারম্ভিক বিন্দু হিসেবে ব্যবহার করে লিট প্লেগ্রাউন্ডে সম্পূর্ণ টিউটোরিয়ালটি করতে পারেন। আপনি যদি ভিএস কোড ব্যবহার করেন, তবে এই চেকপয়েন্টগুলো ব্যবহার করে যেকোনো ধাপের প্রারম্ভিক কোড ডাউনলোড করতে পারবেন, এবং নিজের কাজ যাচাই করার জন্যও এগুলো ব্যবহার করতে পারবেন।

আলোকিত খেলার মাঠের UI অন্বেষণ করা

ফাইল সিলেক্টর ট্যাব বারটিকে সেকশন ১, কোড এডিটিং সেকশনকে সেকশন ২, আউটপুট প্রিভিউকে সেকশন ৩ এবং প্রিভিউ রিলোড বাটনকে সেকশন ৪ হিসেবে চিহ্নিত করা হয়েছে।

লিট প্লেগ্রাউন্ড UI স্ক্রিনশটটি সেই বিভাগগুলিকে হাইলাইট করে যা আপনি এই কোডল্যাবে ব্যবহার করবেন।

  1. ফাইল নির্বাচক। প্লাস বাটনটি লক্ষ্য করুন...
  2. ফাইল সম্পাদক।
  3. কোড প্রিভিউ।
  4. রিলোড বাটন।
  5. ডাউনলোড বাটন।

ভিএস কোড সেটআপ (উন্নত)

এই ভিএস কোড সেটআপটি ব্যবহারের সুবিধাগুলো নিচে দেওয়া হলো:

  • টেমপ্লেট প্রকার যাচাইকরণ
  • টেমপ্লেট ইন্টেলিসেন্স এবং স্বয়ংক্রিয় সমাপ্তি

যদি আপনার NPM, VS Code ( lit-plugin প্লাগইন সহ) আগে থেকেই ইনস্টল করা থাকে এবং আপনি সেই পরিবেশটি ব্যবহার করতে জানেন, তাহলে আপনি নিম্নলিখিত পদক্ষেপগুলো অনুসরণ করে সহজেই এই প্রজেক্টগুলো ডাউনলোড ও চালু করতে পারেন:

  • ডাউনলোড বোতামটি চাপুন
  • tar ফাইলের বিষয়বস্তু একটি ডিরেক্টরিতে এক্সট্র্যাক্ট করুন।
  • (যদি TS হয়) দ্রুত একটি tsconfig সেট আপ করুন যা es মডিউল এবং es2015+ আউটপুট করে।
  • এমন একটি ডেভ সার্ভার ইনস্টল করুন যা বেয়ার মডিউল স্পেসিফায়ার রিজলভ করতে পারে (লিট টিম @web/dev-server ব্যবহারের পরামর্শ দেয়)।
    • এখানে package.json একটি উদাহরণ দেওয়া হল।
  • ডেভ সার্ভারটি চালু করুন এবং আপনার ব্রাউজারটি খুলুন (যদি আপনি @web/dev-server ব্যবহার করেন তবে npx web-dev-server --node-resolve --watch --open ব্যবহার করতে পারেন)।
    • আপনি যদি উদাহরণ package.json ব্যবহার করেন, তাহলে npm run dev ব্যবহার করুন।

৪. জেএসএক্স ও টেমপ্লেটিং

এই অংশে আপনারা সাহিত্যের টেমপ্লেটিং-এর প্রাথমিক বিষয়গুলো শিখবেন।

JSX এবং Lit টেমপ্লেট

JSX হলো জাভাস্ক্রিপ্টের একটি সিনট্যাক্স এক্সটেনশন যা React ব্যবহারকারীদের তাদের জাভাস্ক্রিপ্ট কোডে সহজে টেমপ্লেট লিখতে সাহায্য করে। Lit টেমপ্লেটগুলোও একই ধরনের কাজ করে: কোনো কম্পোনেন্টের UI-কে তার স্টেটের ফাংশন হিসেবে প্রকাশ করা।

মৌলিক সিনট্যাক্স

React-এ আপনি একটি 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-এ, টেমপ্লেটগুলোকে LIT eral নামক একটি html ট্যাগযুক্ত টেমপ্লেটের মধ্যে রাখা হয়, আর ঘটনাচক্রে Lit নামটি এখান থেকেই এসেছে!

টেমপ্লেট মান

লিট টেমপ্লেটগুলো অন্য লিট টেমপ্লেট গ্রহণ করতে পারে, যা TemplateResult ) নামে পরিচিত। উদাহরণস্বরূপ, name ইটালিক ( <i> ) ট্যাগের মধ্যে রাখুন এবং এটিকে একটি ট্যাগড টেমপ্লেট লিটারেল দিয়ে মুড়ে দিন। দ্রষ্টব্য: অবশ্যই ব্যাকটিক অক্ষর ( ` ) ব্যবহার করবেন, সিঙ্গেল কোট অক্ষর ( ' ) নয়।

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
);

Lit 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 ইনপুটটি দেখুন:

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
);

উপরের উদাহরণে, একটি ইনপুট সংজ্ঞায়িত করা হয়েছে যা নিম্নলিখিত কাজগুলো করে:

  • একটি সংজ্ঞায়িত ভেরিয়েবলে (এই ক্ষেত্রে false) নিষ্ক্রিয় সেট করে।
  • ক্লাসটিকে static-class এবং একটি ভেরিয়েবলে সেট করে (এই ক্ষেত্রে "static-class my-class" )।
  • একটি ডিফল্ট মান সেট করে

সাহিত্যে আপনাকে নিম্নলিখিত কাজগুলো করতে হবে:

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 উদাহরণটিতে disabled অ্যাট্রিবিউটটি টগল করার জন্য একটি বুলিয়ান বাইন্ডিং যোগ করা হয়েছে।

এরপরে, className পরিবর্তে সরাসরি class অ্যাট্রিবিউটের সাথে একটি বাইন্ডিং থাকে। class অ্যাট্রিবিউটে একাধিক বাইন্ডিং যোগ করা যেতে পারে, যদি না আপনি classMap ডিরেক্টিভ ব্যবহার করেন, যা ক্লাস টগল করার জন্য একটি ডিক্লারেটিভ হেল্পার।

অবশেষে, ইনপুটে value প্রপার্টি সেট করা হয়। React-এর মতো নয়, এটি ইনপুট এলিমেন্টকে রিড-অনলি করে না, কারণ এটি ইনপুটের নেটিভ ইমপ্লিমেন্টেশন এবং আচরণ অনুসরণ করে।

লিট প্রপ বাইন্ডিং সিনট্যাক্স

html`<my-element ?attribute-name=${booleanVar}>`;
  • ? প্রিফিক্সটি হলো কোনো এলিমেন্টের অ্যাট্রিবিউট টগল করার বাইন্ডিং সিনট্যাক্স।
  • inputRef.toggleAttribute('attribute-name', booleanVar) এর সমতুল্য
  • যেসব এলিমেন্ট disabled ব্যবহার করে তাদের জন্য এটি উপযোগী, কারণ disabled="false" কেও DOM true হিসেবেই পড়ে, যেহেতু inputElement.hasAttribute('disabled') === true
html`<my-element .property-name=${anyVar}>`;
  • কোনো এলিমেন্টের প্রপার্টি সেট করার জন্য . প্রিফিক্সটি হলো বাইন্ডিং সিনট্যাক্স।
  • inputRef.propertyName = anyVar এর সমতুল্য
  • অবজেক্ট, অ্যারে বা ক্লাসের মতো জটিল ডেটা পাস করার জন্য ভালো।
html`<my-element attribute-name=${stringVar}>`;
  • একটি এলিমেন্টের অ্যাট্রিবিউটের সাথে আবদ্ধ হয়
  • inputRef.setAttribute('attribute-name', stringVar) এর সমতুল্য
  • মৌলিক মান, স্টাইল রুল সিলেক্টর এবং কোয়েরি সিলেক্টরের জন্য ভালো।

পাশ দিয়ে যাওয়া হ্যান্ডলাররা

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
);

উপরের উদাহরণে, একটি ইনপুট সংজ্ঞায়িত করা হয়েছে যা নিম্নলিখিত কাজগুলো করে:

  • ইনপুটটি ক্লিক করা হলে 'click' শব্দটি লগ করুন।
  • ব্যবহারকারী যখন কোনো অক্ষর টাইপ করে, তখন ইনপুটের মানটি লগ করুন।

সাহিত্যে আপনাকে নিম্নলিখিত কাজগুলো করতে হবে:

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 হলে ফায়ার হয় (রিঅ্যাক্ট এই ইভেন্টগুলোকে অ্যাবস্ট্রাক্ট করে রাখে)।

লিট ইভেন্ট হ্যান্ডলার সিনট্যাক্স

html`<my-element @event-name=${() => {...}}></my-element>`;
  • @ প্রিফিক্সটি হলো ইভেন্ট লিসেনারের জন্য বাইন্ডিং সিনট্যাক্স।
  • inputRef.addEventListener('event-name', ...) এর সমতুল্য
  • নেটিভ DOM ইভেন্টের নাম ব্যবহার করে

৫. উপাদান ও সরঞ্জাম

এই অংশে আপনারা লিট ক্লাসের উপাদান ও ফাংশন সম্পর্কে জানবেন। স্টেট এবং হুকস নিয়ে পরবর্তী অংশগুলোতে আরও বিস্তারিত আলোচনা করা হয়েছে।

ক্লাস কম্পোনেন্ট এবং লিটএলিমেন্ট

React ক্লাস কম্পোনেন্টের Lit সমতুল্য হলো LitElement, এবং Lit-এর 'রিঅ্যাক্টিভ প্রোপার্টিজ' ধারণাটি React-এর props এবং state-এর একটি সংমিশ্রণ। উদাহরণস্বরূপ:

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 এর ডিফল্ট মান খালি স্ট্রিং ( "" ) হিসেবে সেট করে।
  • "Elliott" name পুনরায় বরাদ্দ করা হয়েছে

LitElement-এ আপনি এটি এভাবেই করবেন।

টাইপস্ক্রিপ্টে:

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 = '';
  • একটি পাবলিক রিঅ্যাক্টিভ প্রপার্টি সংজ্ঞায়িত করে – যা আপনার কম্পোনেন্টের পাবলিক এপিআই-এর একটি অংশ।
  • আপনার কম্পোনেন্টে একটি অ্যাট্রিবিউট (ডিফল্টরূপে) এবং একটি প্রপার্টি প্রকাশ করে।
  • কম্পোনেন্টের অ্যাট্রিবিউটগুলোকে (যা স্ট্রিং) একটি ভ্যালুতে কীভাবে রূপান্তর করতে হবে তা সংজ্ঞায়িত করে।
static get properties() {
  return {
    name: {type: String}
  }
}
  • এটি @property TS ডেকোরেটরের মতোই কাজ করে, কিন্তু এটি জাভাস্ক্রিপ্টে নেটিভভাবে চলে।
render() {
  return html`<h1>Hello, ${this.name}</h1>`
}
  • যখনই কোনো রিঅ্যাক্টিভ প্রপার্টি পরিবর্তন করা হয়, তখনই এটি কল করা হয়।
@customElement('welcome-banner')
class WelcomeBanner extends LitElement {
  ...
}
  • এর মাধ্যমে একটি HTML এলিমেন্ট ট্যাগের নামকে একটি ক্লাস ডেফিনিশনের সাথে যুক্ত করা হয়।
  • কাস্টম এলিমেন্টস স্ট্যান্ডার্ড অনুযায়ী, ট্যাগ নামে অবশ্যই একটি হাইফেন (-) থাকতে হবে।
  • LitElement-এর ভেতরের this কাস্টম এলিমেন্টের ইনস্ট্যান্সকে নির্দেশ করে (এই ক্ষেত্রে <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-এ ফাংশন কম্পোনেন্টের হুবহু কোনো ব্যাখ্যা নেই, কারণ এটি JSX বা কোনো প্রিপ্রসেসর ব্যবহার করে না। তবে, এমন একটি ফাংশন তৈরি করা বেশ সহজ যা প্রোপার্টি গ্রহণ করে এবং সেই প্রোপার্টিগুলোর উপর ভিত্তি করে DOM রেন্ডার করে। উদাহরণস্বরূপ:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="Elliott"/>
ReactDOM.render(
  element,
  mountNode
);

সাহিত্যে এটি হবে:

import {html, render} from 'lit';

function Welcome(props) {
  return html`<h1>Hello, ${props.name}</h1>`;
}

render(
  Welcome({name: 'Elliott'}),
  document.body.querySelector('#root')
);

৬. অবস্থা ও জীবনচক্র

এই অংশে আপনারা লিটের অবস্থা ও জীবনচক্র সম্পর্কে জানতে পারবেন।

রাজ্য

লিটের 'রিঅ্যাক্টিভ প্রোপার্টিজ' ধারণাটি হলো রিঅ্যাক্টের স্টেট এবং প্রপস-এর একটি মিশ্রণ। রিঅ্যাক্টিভ প্রোপার্টিজ পরিবর্তিত হলে কম্পোনেন্টের লাইফসাইকেল সক্রিয় করতে পারে। রিঅ্যাক্টিভ প্রোপার্টিজ দুই ধরনের হয়ে থাকে:

পাবলিক প্রতিক্রিয়াশীল বৈশিষ্ট্য

// 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-এর props এবং state-এর মতো কিন্তু পরিবর্তনযোগ্য
  • পাবলিক এপিআই যা কম্পোনেন্টের ব্যবহারকারীরা অ্যাক্সেস ও সেট করতে পারে।

অভ্যন্তরীণ প্রতিক্রিয়াশীল অবস্থা

// 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-এর state-এর অনুরূপ কিন্তু পরিবর্তনযোগ্য
  • ব্যক্তিগত অভ্যন্তরীণ অবস্থা যা সাধারণত কম্পোনেন্ট বা সাবক্লাসের ভেতর থেকে অ্যাক্সেস করা হয়

জীবনচক্র

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';
  }
}
  • Lit equivalent is also constructor
  • সুপার কলে কিছু পাঠানোর কোনো প্রয়োজন নেই।
  • যাদের দ্বারা আহ্বান করা হয়েছে (সম্পূর্ণরূপে অন্তর্ভুক্তিমূলক নয়):
    • document.createElement
    • document.innerHTML
    • new ComponentClass()
    • যদি পৃষ্ঠায় একটি অপরিবর্তিত ট্যাগ নাম থাকে এবং সংজ্ঞাটি @customElement বা customElements.define দিয়ে লোড ও নিবন্ধিত করা হয়
  • React-এর constructor কার্যকারিতার অনুরূপ

render

// React
render() {
  return <div>Hello World</div>
}

// Lit
render() {
  return html`<div>Hello World</div>`;
}
  • সাহিত্যিক সমতুল্যও render
  • যেকোনো রেন্ডারযোগ্য ফলাফল ফেরত দিতে পারে, যেমন TemplateResult বা string ইত্যাদি।
  • React-এর মতোই, render() একটি pure function হওয়া উচিত।
  • createRenderRoot() যে নোডটি রিটার্ন করবে (ডিফল্টরূপে ShadowRoot ), সেখানেই রেন্ডার করা হবে।

componentDidMount

componentDidMount হলো Lit-এর firstUpdated এবং connectedCallback উভয় লাইফসাইকেল কলব্যাকের একটি সংমিশ্রণের অনুরূপ।

firstUpdated

import Chart from 'chart.js';

// React
componentDidMount() {
  this._chart = new Chart(this.chartElRef.current, {...});
}

// Lit
firstUpdated() {
  this._chart = new Chart(this.chartEl, {...});
}
  • কম্পোনেন্টের টেমপ্লেটটি যখন প্রথমবার কম্পোনেন্টের রুটে রেন্ডার করা হয়, তখন এটি কল করা হয়।
  • এলিমেন্টটি সংযুক্ত থাকলেই কেবল এটি কল করা হবে; যেমন, নোডটি DOM ট্রি-তে যুক্ত না হওয়া পর্যন্ত এটি document.createElement('my-component') এর মাধ্যমে কল করা হবে না।
  • যেসব কম্পোনেন্টের জন্য কম্পোনেন্ট দ্বারা রেন্ডার করা DOM প্রয়োজন, সেগুলোর সেটআপ করার জন্য এটি একটি ভালো জায়গা।
  • React-এর 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 (এখানে "update" শব্দটির ইংরেজি অতীত কাল ব্যবহার করা হয়েছে)।
  • React-এর থেকে ভিন্ন, updated প্রাথমিক রেন্ডারের সময়েও কল করা হয়।
  • React-এর componentDidUpdate এর কার্যকারিতার অনুরূপ।

componentWillUnmount

// React
componentWillUnmount() {
  this.window.removeEventListener('resize', this.boundOnResize);
}

// Lit
disconnectedCallback() {
  super.disconnectedCallback();
  this.window.removeEventListener('resize', this.boundOnResize);
}
  • Lit equivalent, 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 এর একটি মিশ্রণ। এই কম্পোনেন্টের ক্ষেত্রে, setInterval দিয়ে tick কল করার জন্য রুটের ভেতরের 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);

৭. হুক

এই অংশে, আপনি শিখবেন কিভাবে React Hook-এর ধারণাগুলোকে Lit-এ অনুবাদ করতে হয়।

রিঅ্যাক্ট হুকস এর ধারণাগুলি

রিঅ্যাক্ট হুকস ফাংশন কম্পোনেন্টগুলোকে স্টেটের সাথে 'হুক' করার একটি উপায় প্রদান করে। এর বেশ কিছু সুবিধা রয়েছে।

  • এগুলো স্টেটফুল লজিকের পুনঃব্যবহারকে সহজ করে তোলে।
  • একটি উপাদানকে ছোট ছোট ফাংশনে বিভক্ত করতে সাহায্য করে

এছাড়াও, ফাংশন-ভিত্তিক কম্পোনেন্টের উপর জোর দেওয়ার ফলে React-এর ক্লাস-ভিত্তিক সিনট্যাক্সের কিছু সমস্যার সমাধান হয়েছে, যেমন:

  • constructor থেকে super -এ props পাস করতে হচ্ছে
  • constructor প্রোপার্টিগুলোর অগোছালো প্রারম্ভিকীকরণ
    • এটি ছিল তৎকালীন রিয়্যাক্ট টিমের দেওয়া একটি কারণ, যা ES2019 দ্বারা সমাধান করা হয়েছিল।
  • this কারণে সৃষ্ট সমস্যাগুলো আর কম্পোনেন্টটিকে নির্দেশ করছে না।

লিট-এ রিয়্যাক্ট হুকস-এর ধারণা

কম্পোনেন্টস ও প্রপস বিভাগে যেমন উল্লেখ করা হয়েছে, Lit কোনো ফাংশন থেকে কাস্টম এলিমেন্ট তৈরি করার উপায় দেয় না, কিন্তু LitElement রিয়্যাক্ট ক্লাস কম্পোনেন্টগুলোর বেশিরভাগ প্রধান সমস্যার সমাধান করে। উদাহরণস্বরূপ:

// 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 কোনো আর্গুমেন্ট গ্রহণ করে না।
  • সমস্ত @event বাইন্ডিং স্বয়ংক্রিয়ভাবে this সাথে বাইন্ড হয়।
  • অধিকাংশ ক্ষেত্রেই this কাস্টম এলিমেন্টের রেফারেন্সকে বোঝায়।
  • এখন থেকে ক্লাস প্রোপার্টিগুলোকে ক্লাস মেম্বার হিসেবে ইনস্ট্যানশিয়েট করা যাবে। এর ফলে কনস্ট্রাক্টর-ভিত্তিক ইমপ্লিমেন্টেশনগুলো আরও পরিচ্ছন্ন হয়েছে।

প্রতিক্রিয়াশীল নিয়ন্ত্রক

হুকস-এর পেছনের মূল ধারণাগুলো লিট-এ রিঅ্যাক্টিভ কন্ট্রোলার হিসেবে বিদ্যমান। রিঅ্যাক্টিভ কন্ট্রোলার প্যাটার্নগুলো স্টেটফুল লজিক শেয়ার করা, কম্পোনেন্টগুলোকে ছোট ও আরও মডুলার অংশে বিভক্ত করা, এবং কোনো এলিমেন্টের আপডেট লাইফসাইকেলে হুক করার সুযোগ দেয়।

একটি রিঅ্যাক্টিভ কন্ট্রোলার হলো এমন একটি অবজেক্ট ইন্টারফেস যা লিটএলিমেন্টের মতো কোনো কন্ট্রোলার হোস্টের আপডেট লাইফসাইকেলে যুক্ত হতে পারে।

একটি 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 ইন্টারফেসের পাশাপাশি কন্ট্রোলারটি ইনিশিয়ালাইজ করার জন্য প্রয়োজনীয় অন্যান্য প্রোপার্টিগুলোও গ্রহণ করতে পারে।

এখন আপনাকে React লাইফসাইকেল কলব্যাকগুলোকে কন্ট্রোলার কলব্যাকে অনুবাদ করতে হবে। সংক্ষেপে:

  • componentDidMount
    • LitElement-এর connectedCallback
    • কন্ট্রোলারের hostConnected
  • ComponentWillUnmount
    • LitElement-এর disconnectedCallback
    • কন্ট্রোলারের hostDisconnected

React লাইফসাইকেলকে Lit লাইফসাইকেলে রূপান্তর করার বিষয়ে আরও তথ্যের জন্য, State & Lifecycle বিভাগটি দেখুন।

এরপরে, state & Lifecycle সেকশনের উদাহরণে যেমন করা হয়েছে, সেভাবে hostConnected কলব্যাক এবং tick মেথডগুলো ইমপ্লিমেন্ট করুন, এবং hostDisconnected মধ্যে ইন্টারভ্যালটি পরিষ্করণ করুন।

// 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();
}

এখন সময় ফুরিয়ে আসার কথা!

হুক-এর সাধারণ ব্যবহারগুলোর আরও বিশদ তুলনার জন্য, অনুগ্রহ করে "উন্নত বিষয়সমূহ - হুকস" বিভাগটি দেখুন।

৮. শিশুরা

এই অংশে, আপনি শিখবেন কীভাবে সাহিত্য বিষয়ে শিশুদের পরিচালনা করতে স্লট ব্যবহার করতে হয়।

স্লট এবং শিশুরা

স্লট কম্পোনেন্টগুলিকে নেস্ট করার সুযোগ দিয়ে কম্পোজিশন সক্ষম করে।

React-এ, `props`-এর মাধ্যমে `children` ইনহেরিট করা হয়। ডিফল্ট স্লটটি হলো props.children এবং render ফাংশনটি নির্ধারণ করে দেয় যে ডিফল্ট স্লটটি কোথায় অবস্থান করবে। উদাহরণস্বরূপ:

const MyArticle = (props) => {
 return <article>{props.children}</article>;
};

মনে রাখবেন যে props.children হলো React Component, HTML এলিমেন্ট নয়।

Lit-এ, রেন্ডার ফাংশনের মধ্যে স্লট এলিমেন্ট ব্যবহার করে চাইল্ড এলিমেন্ট তৈরি করা হয়। লক্ষ্য করুন, React-এর মতো করে চাইল্ড এলিমেন্ট ইনহেরিট হয় না। Lit-এ, চাইল্ড এলিমেন্টগুলো হলো স্লটের সাথে সংযুক্ত HTMLElement। এই সংযুক্তিকে প্রোজেকশন (Projection) বলা হয়।

@customElement("my-article")
export class MyArticle extends LitElement {
  render() {
    return html`
      <article>
        <slot></slot>
      </article>
   `;
  }
}

একাধিক স্লট

React-এ একাধিক স্লট যোগ করা মূলত আরও props ইনহেরিট করার মতোই।

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 প্রপস-এ পাস করা হয়েছে।

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"> ) কাস্টম এলিমেন্টের চাইল্ডদের (যেমন <div slot="foo"> ) slot অ্যাট্রিবিউটের সাথে মেলে, তাহলে সেই নোডটি প্রজেক্টেড হবে না এবং প্রদর্শিত হবে না।

৯. তথ্যসূত্র

মাঝে মাঝে একজন ডেভেলপারের কোনো HTMLElement-এর API অ্যাক্সেস করার প্রয়োজন হতে পারে।

এই অংশে, আপনি শিখবেন কীভাবে সাহিত্যশাস্ত্রে উপাদানের রেফারেন্স অর্জন করতে হয়।

রিঅ্যাক্ট রেফারেন্স

একটি React কম্পোনেন্টকে ট্রান্সপাইল করে একাধিক ফাংশন কলে পরিণত করা হয়, যা আহ্বান করা হলে একটি ভার্চুয়াল ডোম (virtual DOM) তৈরি করে। এই ভার্চুয়াল ডোমটি ReactDOM দ্বারা ইন্টারপ্রেট করা হয় এবং HTMLElement-সমূহকে রেন্ডার করে।

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 অ্যাট্রিবিউটের মাধ্যমে তৈরি হওয়া HTMLInputElement টির inputRef.current সেট করে দেবে।

@query সহ "রেফারেন্স" সাহিত্য

লিট ব্রাউজারের খুব কাছাকাছি থাকে এবং ব্রাউজারের নিজস্ব বৈশিষ্ট্যগুলোর ওপর একটি অত্যন্ত পাতলা বিমূর্ততা তৈরি করে।

Lit-এর refs এর React সমতুল্য হলো @query এবং @queryAll ডেকোরেটর দ্বারা রিটার্ন করা HTMLElement।

@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 কম্পোনেন্টটি নিম্নলিখিত কাজগুলো করে:

  • @query ডেকোরেটর ব্যবহার করে MyElement এর উপর একটি প্রপার্টি সংজ্ঞায়িত করে (একটি HTMLInputElement জন্য গেটার তৈরি করে)।
  • onButtonClick নামের একটি ক্লিক ইভেন্ট কলব্যাক ঘোষণা ও সংযুক্ত করে।
  • বাটন ক্লিকে ইনপুট ফোকাস করে।

জাভাস্ক্রিপ্টে, @query এবং @queryAll ডেকোরেটরগুলো যথাক্রমে querySelector এবং querySelectorAll কার্য সম্পাদন করে। এটি হলো @query('input') inputEl!: HTMLInputElement; এর জাভাস্ক্রিপ্ট সমতুল্য রূপ।

get inputEl() {
  return this.renderRoot.querySelector('input');
}

Lit কম্পোনেন্টটি my-element এর রুটে ` render মেথডের টেমপ্লেট কমিট করার পর, @query ডেকোরেটরটি inputEl রেন্ডার রুটে পাওয়া প্রথম input এলিমেন্টটি রিটার্ন করার সুযোগ দেবে। যদি @query নির্দিষ্ট এলিমেন্টটি খুঁজে না পায়, তবে এটি null রিটার্ন করবে।

রেন্ডার রুটে একাধিক input এলিমেন্ট থাকলে, @queryAll নোডগুলোর একটি তালিকা রিটার্ন করত।

১০. মধ্যস্থতাকারী রাষ্ট্র

এই অংশে, আপনি শিখবেন কীভাবে সাহিত্যের উপাদানগুলোর মধ্যে অবস্থার মধ্যস্থতা করতে হয়।

পুনর্ব্যবহারযোগ্য উপাদান

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.step আর্গুমেন্ট হিসেবে ` props.addToCounter কল করার মাধ্যমে প্যারেন্ট কম্পোনেন্টটি আপডেট করা হয়।

যদিও 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>
    `;
  }
}

উপরের উদাহরণে, একটি লিট কম্পোনেন্ট নিম্নলিখিত কাজগুলো করবে:

  • প্রতিক্রিয়াশীল প্রপার্টি step তৈরি করুন
  • ক্লিক করার পর এলিমেন্টটির step ভ্যালু বহনকারী update-counter নামক একটি কাস্টম ইভেন্ট প্রেরণ করুন।

ব্রাউজার ইভেন্টগুলো চাইল্ড এলিমেন্ট থেকে প্যারেন্ট এলিমেন্টে ছড়িয়ে পড়ে। ইভেন্টের মাধ্যমে চাইল্ড এলিমেন্টগুলো ইন্টারঅ্যাকশন ইভেন্ট এবং স্টেট পরিবর্তন ব্রডকাস্ট করতে পারে। রিয়্যাক্ট মূলত এর বিপরীত দিকে স্টেট পাস করে, তাই লিট কম্পোনেন্টের মতো করে রিয়্যাক্ট কম্পোনেন্টকে ইভেন্ট ডিসপ্যাচ করতে এবং শুনতে দেখাটা সচরাচর দেখা যায় না।

স্টেটফুল উপাদান

React-এ স্টেট ম্যানেজ করার জন্য হুকস ব্যবহার করা একটি প্রচলিত পদ্ধতি। CounterButton কম্পোনেন্টটি পুনরায় ব্যবহার করে একটি MyCounter কম্পোনেন্ট তৈরি করা যায়। লক্ষ্য করুন, কীভাবে CounterButton এর উভয় ইনস্ট্যান্সেই addToCounter পাস করা হয়েছে।

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 প্রতি ক্লিকে step count আপডেট করার জন্য addToCounter ব্যবহার করে।

Lit-এ MyCounter এর একটি অনুরূপ বাস্তবায়ন করা যেতে পারে। লক্ষ্য করুন, এখানে addToCounter counter-button এ পাস করা হয়নি। এর পরিবর্তে, কলব্যাকটিকে একটি প্যারেন্ট এলিমেন্টের @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 ইভেন্ট লিসেনারের সাথে সংযুক্ত করে।
  • update-counter ইভেন্টের detail.step এ প্রাপ্ত মানটি যোগ করার মাধ্যমে count আপডেট করা হয়।
  • step অ্যাট্রিবিউটের মাধ্যমে counter-button step মান নির্ধারণ করে।

Lit-এ প্যারেন্ট থেকে চাইল্ডে পরিবর্তন ব্রডকাস্ট করার জন্য রিঅ্যাক্টিভ প্রোপার্টি ব্যবহার করাই বেশি প্রচলিত। একইভাবে, নিচ থেকে উপরের দিকে বিস্তারিত তথ্য পৌঁছে দেওয়ার জন্য ব্রাউজারের ইভেন্ট সিস্টেম ব্যবহার করাও একটি ভালো অভ্যাস।

এই পদ্ধতিটি সর্বোত্তম রীতি অনুসরণ করে এবং ওয়েব কম্পোনেন্টগুলোর জন্য ক্রস-প্ল্যাটফর্ম সমর্থন প্রদানের 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>
    `;
  }
}

উপরের উদাহরণটিতে দুটি হেডিং রয়েছে, যার প্রত্যেকটিতে ইনলাইন স্টাইল ব্যবহার করা হয়েছে।

এখন border-color.js থেকে একটি বর্ডার ইম্পোর্ট করে কমলা টেক্সটটির সাথে যুক্ত করুন:

...
import borderColor from './border-color.js';

...

html`
  ...
  <h1 style="color:orange;${borderColor}">This text is orange</h1>
  ...`

প্রতিবার স্টাইল স্ট্রিং গণনা করতে হওয়াটা কিছুটা বিরক্তিকর হতে পারে, তাই এই কাজে সাহায্যের জন্য লিট একটি ডিরেক্টিভ প্রদান করে।

স্টাইলম্যাপ

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 সিনট্যাক্স React-এর style অ্যাট্রিবিউট বাইন্ডিং সিনট্যাক্সের মতোই।

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 কাস্টম প্রোপার্টি পলিফিলের সাথে সামঞ্জস্যপূর্ণ

এছাড়াও, index.html এ থাকা <style> ট্যাগটির দিকে লক্ষ্য করুন:

<!-- index.html -->
<style>
  h1 {
    color: red !important;
  }
</style>

Lit আপনার কম্পোনেন্টগুলোর স্টাইলকে সেগুলোর রুটের মধ্যেই সীমাবদ্ধ রাখবে। এর মানে হলো, স্টাইলগুলো এক কম্পোনেন্ট থেকে অন্য কম্পোনেন্টে ছড়াবে না। কম্পোনেন্টগুলোতে স্টাইল পাঠানোর জন্য Lit টিম CSS কাস্টম প্রোপার্টি ব্যবহার করার পরামর্শ দেয়, কারণ এগুলো Lit-এর স্টাইল স্কোপিং ভেদ করতে পারে।

স্টাইল ট্যাগ

আপনার টেমপ্লেটে সরাসরি <style> ট্যাগ ইনলাইন করাও সম্ভব। ব্রাউজার এই স্টাইল ট্যাগগুলো থেকে ডুপ্লিকেট ট্যাগ বাদ দেবে, কিন্তু টেমপ্লেটের ভেতরে রাখলে, css ট্যাগযুক্ত টেমপ্লেটের মতো প্রতি ক্লাসের পরিবর্তে প্রতিটি কম্পোনেন্ট ইনস্ট্যান্স অনুযায়ী এগুলো পার্স করা হবে। এছাড়াও, CSSResult এর ক্ষেত্রে ব্রাউজারের ডিডুপ্লিকেশন অনেক দ্রুত হয়।

আপনার টেমপ্লেটে স্টাইলের জন্য <link rel="stylesheet"> ব্যবহার করাও একটি উপায়, কিন্তু এটিও সুপারিশ করা হয় না কারণ এর ফলে ইনিশিয়াল ফ্ল্যাশ অফ আনস্টাইলড কন্টেন্ট (FOUC) দেখা দিতে পারে।

১২. উন্নত বিষয়সমূহ (ঐচ্ছিক)

JSX & Templating

Lit & Virtual DOM

Lit-html does not include a conventional Virtual DOM that diffs each individual node. Instead it utilizes performance features intrinsic to ES2015's tagged template literal spec. Tagged template literals are template literal strings with tag functions attached to them.

Here is an example of a template literal:

const str = 'string';
console.log(`This is a template literal ${str}`);

Here is an example of a tagged template literal:

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

In the above example, the tag is the tag function and the f function returns an invocation of a tagged template literal.

A lot of the performance magic in Lit comes from the fact that the string arrays passed into the tag function have the same pointer (as shown in the second console.log ). The browser does not recreate a new strings array on each tag function invocation, because it is using the same template literal (ie in the same location in the AST). So Lit's binding, parsing, and template caching can take advantage of these features without much runtime diffing overhead.

This built-in browser behavior of tagged template literals gives Lit quite a performance advantage. Most conventional Virtual DOMs do the majority of their work in JavaScript. However, tagged template literals do most of their diffing in the browser's C++.

If you'd like to get started using HTML tagged template literals with React or Preact, the Lit team recommends the htm library .

Though, as is the case with the Google Codelabs site and several online code editors, you will notice that tagged template literal syntax highlighting is not very common. Some IDEs and text editors support them by default such as Atom and GitHub's codeblock highlighter. The Lit team also works very closely with the community to maintain projects such as the lit-plugin which is a VS Code plugin that will add syntax highlighting, type checking, and intellisense to your Lit projects.

Lit & JSX + React DOM

JSX does not run in the browser and instead uses a preprocessor to convert JSX to JavaScript function calls (typically via Babel).

For example, Babel will transform this:

const element = <div className="title">Hello World!</div>;
ReactDOM.render(element, mountNode);

into this:

const element = React.createElement('div', {className: 'title'}, 'Hello World!');
ReactDOM.render(element, mountNode);

React DOM then takes the React output and translates it to actual DOM – properties, attributes, event listeners, and all.

Lit-html uses tagged template literals which can run in the browser without transpilation or a preprocessor. This means that in order to get started with Lit, all you need is an HTML file, an ES module script, and a server. Here's a completely browser-runnable script:

<!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>

Additionally, since Lit's templating system, lit-html, does not use a conventional Virtual DOM but rather uses the DOM API directly, Lit 2's size is under 5kb minified and gzipped compared to React (2.8kb) + react-dom's (39.4kb) 40kb minified and gizipped.

ইভেন্টগুলি

React uses a synthetic event system. This means that react-dom must define every event that will be used on every component and provide a camelCase event listener equivalent for each type of node. As a result, JSX does not have a method to define an event listener for a custom event and developers must use a ref and then imperatively apply a listener. This creates a sub-par developer experience when integrating libraries that don't have React in mind thus resulting in having to write a React-specific wrapper.

Lit-html directly accesses the DOM and uses native events, so adding event listeners is as easy as @event-name=${eventNameListener} . This means that less runtime parsing is done for adding event listeners as well as firing events.

Components & Props

React components & custom elements

Under the hood, LitElement uses custom elements to package its components. Custom elements introduce some tradeoffs between React components when it comes to componentization (state and lifecycle is discussed further in the State & Lifecycle section).

Some advantages Custom Elements have as a component system:

  • Native to the browser and do not require any tooling
  • Fit into every browser API from innerHTML and document.createElement to querySelector
  • Can typically be used across frameworks
  • Can be lazily registered with customElements.define and "hydrate" DOM

Some disadvantages Custom Elements have compared to React components:

  • Cannot create a custom element without defining a class (thus no JSX-like functional components)
  • Must contain a closing tag
    • Note: despite the developer convenience browser vendors tend to regret the self-closing tag spec which is why newer specs tend to not include self-closing tags
  • Introduces an extra node to the DOM tree which may cause layout issues
  • Must be registered via JavaScript

Lit has gone with custom elements over a bespoke element system because the custom elements are built into the browser, and the Lit team believes that the cross-framework benefits outweigh the benefits provided by a component abstraction layer. In fact, the Lit team's efforts in the lit-ssr space have overcome the main issues with JavaScript registration. Additionally, some companies such as GitHub take advantage of custom element lazy registration to progressively enhance pages with optional flair.

Passing data to custom elements

A common misconception with custom elements is that data can only be passed in as strings. This misconception likely comes from the fact that element attributes can only be written as strings. Though it is true that Lit will cast string attributes to their defined types, custom elements can also accept complex data as properties.

For example – given the following LitElement definition:

// 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>`;
  }
}

A primitive reactive property num is defined which will convert an attribute's string value into a number , and then complex data structure is introduced with attribute:false which deactivates Lit's attribute handling.

This is how to pass data to this custom element:

<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>

State & Lifecycle

Other React Lifecycle Callbacks

static getDerivedStateFromProps

There is no equivalent in Lit as props and state are both the same class properties

shouldComponentUpdate

  • Lit equivalent is shouldUpdate
  • Called on first render unlike React
  • Similar in function to React's shouldComponentUpdate

getSnapshotBeforeUpdate

In Lit, getSnapshotBeforeUpdate is similar to both update and willUpdate

willUpdate

  • Called before update
  • Unlike getSnapshotBeforeUpdate , willUpdate is called before render
  • Changes to reactive properties in willUpdate do not re-trigger the update cycle
  • Good place to compute property values that depend on other properties and are used in the rest of the update process
  • This method is called on the server in SSR, so accessing the DOM is not advised here

update

  • Called after willUpdate
  • Unlike getSnapshotBeforeUpdate , update is called before render
  • Changes to reactive properties in update do not re-trigger the update cycle if changed before calling super.update
  • Good place to capture information from the DOM surrounding the component before the rendered output is committed to the DOM
  • This method is not called on the server in SSR

Other Lit Lifecycle Callbacks

There are several lifecycle callbacks that were not mentioned in the previous section because there is no analog to them in React. They are:

attributeChangedCallback

It is invoked when one of the element's observedAttributes changes. Both observedAttributes and attributeChangedCallback are part of the custom elements spec and implemented by Lit under the hood to provide an attribute API for Lit elements.

adoptedCallback

Invoked when the component is moved to a new document eg from an HTMLTemplateElement 's documentFragment to the main document . This callback is also a part of the custom elements spec and should only be used for advanced use cases when the component changes documents.

Other lifecycle methods and properties

These methods and properties are class members you can call, override, or await to help manipulate the lifecycle process.

updateComplete

This is a Promise that resolves when the element has finished updating as the update and render lifecycles are asynchronous. An example:

async nextButtonClicked() {
  this.step++;
  // Wait for the next "step" state to render
  await this.updateComplete;
  this.dispatchEvent(new Event('step-rendered'));
}

getUpdateComplete

This is a method that should be overridden to customize when updateComplete resolves. This is common when a component is rendering a child component and their render cycles must be in sync. eg,

class MyElement extends LitElement {
  ...
  async getUpdateComplete() {
    await super.getUpdateComplete();
    await this.myChild.updateComplete;
  }
}

performUpdate

This method is what calls the update lifecycle callbacks. This should generally not be needed except for rare cases where updating must be done synchronously or for custom scheduling.

hasUpdated

This property is true if the component has updated at least once.

isConnected

A part of the custom elements spec, this property will be true if the element is currently attached to the main document tree.

Lit Update Lifecycle Visualization

There are 3 parts to the update lifecycle:

  • Pre-update
  • আপডেট
  • Post-update

Pre-Update

A directed acyclic graph of nodes with callback names. constructor to requestUpdate. @property to Property Setter. attributeChangedCallback to Property Setter. Property Setter to hasChanged. hasChanged to requestUpdate. requestUpdate points out to the next, update lifecycle graph.

After requestUpdate , a scheduled update is awaited.

আপডেট

A directed acyclic graph of nodes with callback names. Arrow from previous image of pre-update lifecycle points to performUpdate. performUpdate to shouldUpdate. shouldUpdate points to both ‘complete update if false’ as well as willUpdate. willUpdate to update. update to both render as well as to the next, post-update lifecycle graph. render also points to the next, post-update lifecycle graph.

Post-Update

A directed acyclic graph of nodes with callback names. Arrow from previous image of update lifecycle points to firstUpdated. firstUpdated to updated. updated to updateComplete.

হুক

Why hooks

Hooks were introduced into React for simple function component use cases that required state. In many simple cases function components with hooks tend to be much simpler and more readable than their class component counterparts. Though, when introducing asynchonous state updates as well as passing data between hooks or effects, the hooks pattern tends to not suffice, and a class-based solution like reactive controllers tend to shine.

API request hooks & controllers

It is common to write a hook that requests data from an API. For example, take this React function component that does the following:

  • index.tsx
    • Renders text
    • Renders useAPI 's response
      • User ID + User name
      • ত্রুটি বার্তা
        • 404 when reaches user 11 (by design)
        • Abort error if API fetch is aborted
      • Loading Message
    • Renders an action button
      • Next user: which fetches the API for the next user
      • Cancel: which aborts the API fetch and displays an error
  • useApi.tsx
    • Defines a useApi custom hook
    • Will async fetch a user object from an api
    • Emits:
      • ব্যবহারকারীর নাম
      • Whether the fetch is loading
      • Any error messages
      • A callback to abort the fetch
    • Aborts fetches in progress if dismounted

Here is the Lit + Reactive Controller implementation .

Takeaways:

  • Reactive Controllers are most like custom hooks
  • Passing non-renderable data between callbacks and effects
    • React uses useRef to pass data between useEffect and useCallback
    • Lit uses a private class property
    • React is essentially mimicking the behavior of a private class property

Additionally, if you really like the React function component syntax with hooks but the same buildless environment of Lit, the Lit team highly recommends the Haunted library.

শিশুরা

Default Slot

When HTML elements are not given a slot attribute, they are assigned to the default unnamed slot. In the example below, MyApp will slot one paragraph into a named slot. The other paragraph will default to the unnamed slot".

@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>
   `;
  }
}

Slot Updates

When the structure of slot descendants change, a slotchange event is fired. A Lit component can bind an event-listener to a slotchange event. In the example below, the first slot found in the shadowRoot will have their assignedNodes logged to the console on 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>
   `;
  }
}

Refs

Reference generation

Lit and React both expose a reference to an HTMLElement after their render functions have been called. But it's worth reviewing how React and Lit compose the DOM that is later returned through a Lit @query decorator or a React Reference.

React is a functional pipeline that creates React Components not HTMLElements. Because a Ref is declared before an HTMLElement is rendered, a space in memory is allocated. This is why you see null as the initial value of a Ref, because the actual DOM element hasn't yet been created (or rendered) ie useRef(null) .

After ReactDOM converts a React Component into an HTMLElement, it looks for an attribute called ref in the ReactComponent. If available, ReactDOM places the HTMLElement's reference to ref.current .

LitElement uses the html template tag function from lit-html to compose a Template Element under the hood. LitElement stamps the template's contents to a custom element's shadow DOM after render. The shadow DOM is a scoped DOM tree encapsulated by a shadow root. The @query decorator then creates a getter for the property which essentially performs a this.shadowRoot.querySelector on the scoped root.

Query Multiple Elements

In the example below, the @queryAll decorator will return the two paragraphs in the shadow root as a 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>
   `;
  }
}

Essentially, @queryAll creates a getter for paragraphs that returns the results of this.shadowRoot.querySelectorAll() . In JavaScript, a getter can be declared to perform the same purpose:

get paragraphs() {
  return this.renderRoot.querySelectorAll('p');
}

Query Changing Elements

The @queryAsync decorator is better suited to handle a node that can change based on the state of another element property.

In the example below, @queryAsync will find the first paragraph element. However, a paragraph element will only be rendered when renderParagraph randomly generates an odd number. The @queryAsync directive will return a promise that will resolve when the first paragraph is available.

@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()}
   `;
  }
}

Mediating State

In React, convention is to use callbacks because state is mediated by React itself. React does it's best to not rely on state provided by elements. The DOM is simply an effect of the rendering process.

External State

It's possible to use Redux, MobX, or any other state management library alongside Lit.

Lit components are created in browser scope. So any library that also exists in browser scope is available to Lit. Many amazing libraries have been built to utilize existing state management systems in Lit.

Here is a series by Vaadin explaining how to leverage Redux in a Lit component.

Take a look at lit-mobx from Adobe to see how a large scale site can leverage MobX in Lit.

Also, check out Apollo Elements to see how developers are including GraphQL in their web components.

Lit works with native browser features and most state management solutions in browser scope can be used in a Lit component.

স্টাইলিং

Shadow DOM

To natively encapsulate styles and DOM within a Custom Element, Lit uses Shadow DOM . Shadow Roots generate a shadow tree separate from the main document tree. This means that most styles are scoped to this document. Certain styles do leak through such as color, and other font-related styles.

Shadow DOM also introduces new concepts and selectors to the CSS spec:

: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.
   */
}

Sharing Styles

Lit makes it easy to share styles between components in the form of CSSTemplateResults via css template tags. For example:

// 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>`
  }
}

Theming

Shadow roots present a bit of a challenge to conventional theming which typically are top-down style tag approaches. The conventional way to tackle theming with Web Components that use Shadow DOM is to expose a style API via CSS Custom Properties . For example, this is a pattern that Material Design uses:

.mdc-textfield-outline {
  border-color: var(--mdc-theme-primary, /* default value */ #...);
}
.mdc-textfield--input {
  caret-color: var(--mdc-theme-primary, #...);
}

The user would then change the theme of the site by applying custom property values:

html {
  --mdc-theme-primary: #F00;
}
html[dark] {
  --mdc-theme-primary: #F88;
}

If top-down theming is a must and you are unable to expose styles, it is always possible to disable Shadow DOM by overriding createRenderRoot to return this which will then render your components' template to the custom element itself rather than to a shadow root attached to the custom element. With this you will lose: style encapsulation, DOM encapsulation, and slots.

Production

IE 11

If you need to support older browsers like IE 11, you will have to load some polyfills which come out to about another 33kb. More information can be found here .

Conditional Bundles

The Lit team recommends serving two different bundles, one for IE 11 and one for modern browsers. There are several benefits to this:

  • Serving ES 6 is faster and will serve most of your clients
  • Transpiled ES 5 significantly increases bundle size
  • Conditional bundles give you the best of both worlds
    • IE 11 support
    • No slowdown on modern browsers

More info on how to build a conditionally served bundle can be found on our documentation site here .