1. مقدمه
آخرین به روز رسانی: 2021-08-10
اجزای وب
Web Components مجموعهای از APIهای پلتفرم وب هستند که به شما امکان میدهند تگهای HTML سفارشی، قابل استفاده مجدد و کپسولهشده جدیدی را برای استفاده در صفحات وب و برنامههای وب ایجاد کنید. اجزای سفارشی و ویجتهای ساخته شده بر اساس استانداردهای Web Component در مرورگرهای مدرن کار میکنند و میتوانند با هر کتابخانه یا چارچوب جاوا اسکریپتی که با HTML کار میکند استفاده شوند.
Lit چیست
Lit یک کتابخانه ساده برای ساخت کامپوننت های وب سریع و سبک است که در هر چارچوبی یا اصلاً بدون فریمورک کار می کنند. با Lit می توانید اجزای قابل اشتراک گذاری، برنامه های کاربردی، سیستم های طراحی و موارد دیگر را بسازید.
Lit APIهایی را برای ساده کردن وظایف متداول Web Components مانند مدیریت ویژگی ها، ویژگی ها و رندر ارائه می دهد.
چیزی که یاد خواهید گرفت
- کامپوننت وب چیست؟
- مفاهیم اجزای وب
- چگونه یک کامپوننت وب بسازیم
- lit-html و LitElement چیست
- آنچه Lit در بالای یک مؤلفه وب انجام می دهد
چیزی که خواهی ساخت
- یک کامپوننت وب وانیلی که انگشت شست بالا/پایین است
- یک کامپوننت وب مبتنی بر روشن/شست بالا/پایین
آنچه شما نیاز دارید
- هر مرورگر مدرن به روز شده (Chrome، Safari، Firefox، Chromium Edge). اجزای وب در همه مرورگرهای مدرن کار میکنند و پلیفیلها برای Microsoft Internet Explorer 11 و غیر کرومی مایکروسافت اج در دسترس هستند.
- آشنایی با HTML، CSS، جاوا اسکریپت و ابزار توسعه کروم .
2. راه اندازی و کاوش در زمین بازی
دسترسی به کد
در سرتاسر Codelab پیوندهایی به زمین بازی Lit وجود دارد که به شرح زیر است:
زمین بازی یک جعبه ماسه ای کد است که به طور کامل در مرورگر شما اجرا می شود. میتواند فایلهای TypeScript و JavaScript را کامپایل و اجرا کند، و همچنین میتواند به طور خودکار واردات به ماژولهای گره را حل کند. به عنوان مثال
// before
import './my-file.js';
import 'lit';
// after
import './my-file.js';
import 'https://unpkg.com/lit?module';
شما می توانید کل آموزش را در زمین بازی Lit با استفاده از این نقاط بازرسی به عنوان نقطه شروع انجام دهید. اگر از VS Code استفاده می کنید، می توانید از این نقاط بازرسی برای دانلود کد شروع برای هر مرحله و همچنین برای بررسی کار خود استفاده کنید.
کاوش در رابط کاربری روشنایی زمین بازی
اسکرین شات رابط کاربری زمین بازی Lit بخش هایی را که در این کد لبه استفاده خواهید کرد برجسته می کند.
- انتخابگر فایل به دکمه پلاس توجه کنید...
- ویرایشگر فایل.
- پیش نمایش کد.
- دکمه بارگذاری مجدد
- دکمه دانلود.
تنظیم VS Code (پیشرفته)
در اینجا مزایای استفاده از این تنظیمات VS Code وجود دارد:
- بررسی نوع الگو
- هوشمندسازی الگو و تکمیل خودکار
اگر NPM، VS Code (با افزونه lit-plugin ) را قبلاً نصب کردهاید و میدانید چگونه از آن محیط استفاده کنید، میتوانید به سادگی این پروژهها را با انجام کارهای زیر دانلود و شروع کنید:
- دکمه دانلود را فشار دهید
- محتویات فایل tar را در یک دایرکتوری استخراج کنید
- سرور توسعه دهنده ای را نصب کنید که بتواند مشخصه های ماژول خالی را حل کند (تیم Lit @web/dev-server را توصیه می کند)
- در اینجا یک نمونه
package.json
است
- در اینجا یک نمونه
- سرور توسعه دهنده را اجرا کرده و مرورگر خود را باز کنید (اگر از
@web/dev-server
استفاده می کنید، می توانید ازnpx web-dev-server --node-resolve --watch --open
استفاده کنید)- اگر از نمونه
package.json
استفاده می کنید ازnpm run serve
استفاده کنید
- اگر از نمونه
3. یک عنصر سفارشی را تعریف کنید
عناصر سفارشی
Web Components مجموعه ای از 4 API وب بومی است. آنها عبارتند از:
- ماژول های ES
- عناصر سفارشی
- سایه DOM
- قالب های HTML
شما قبلاً از مشخصات ماژولهای ES استفاده کردهاید، که به شما امکان میدهد ماژولهای جاوا اسکریپت را با واردات و صادرات ایجاد کنید که با <script type="module">
در صفحه بارگذاری میشوند.
تعریف یک عنصر سفارشی
مشخصات عناصر سفارشی به کاربران اجازه می دهد تا عناصر HTML خود را با استفاده از جاوا اسکریپت تعریف کنند. نام ها باید دارای خط فاصله ( -
) باشند تا آنها را از عناصر اصلی مرورگر متمایز کند. فایل index.js
را پاک کنید و یک کلاس عنصر سفارشی تعریف کنید:
index.js
class RatingElement extends HTMLElement {}
customElements.define('rating-element', RatingElement);
یک عنصر سفارشی با مرتبط کردن کلاسی که HTMLElement
با نام تگ خط فاصله گسترش میدهد، تعریف میشود. فراخوانی به customElements.define
به مرورگر میگوید که کلاس RatingElement
با تگName 'rating-element'
مرتبط کند. این بدان معنی است که هر عنصر در سند شما با نام <rating-element>
با این کلاس مرتبط خواهد بود.
یک <rating-element>
را در بدنه سند قرار دهید و ببینید چه چیزی رندر می شود.
index.html
<body>
<rating-element></rating-element>
</body>
اکنون، با نگاه کردن به خروجی، خواهید دید که هیچ چیزی ارائه نشده است. این مورد انتظار است، زیرا شما به مرورگر نگفتید که چگونه <rating-element>
را رندر کند. میتوانید تأیید کنید که تعریف عنصر سفارشی با انتخاب <rating-element>
در انتخابگر عنصر Chrome Dev Tools و فراخوانی در کنسول:
$0.constructor
که باید خروجی داشته باشد:
class RatingElement extends HTMLElement {}
چرخه عمر عنصر سفارشی
عناصر سفارشی با مجموعه ای از قلاب های چرخه حیات ارائه می شوند. آنها عبارتند از:
-
constructor
-
connectedCallback
-
disconnectedCallback
-
attributeChangedCallback
-
adoptedCallback
constructor
زمانی که عنصر برای اولین بار ایجاد می شود فراخوانی می شود: برای مثال، با فراخوانی document.createElement('rating-element')
یا new RatingElement()
. سازنده مکان خوبی برای تنظیم عنصر شما است، اما معمولاً انجام دستکاری های DOM در سازنده به دلایل عملکرد "بوت کردن" عنصر، عمل بدی در نظر گرفته می شود.
connectedCallback
زمانی فراخوانی می شود که عنصر سفارشی به DOM متصل شود. این معمولاً جایی است که دستکاری های اولیه DOM اتفاق می افتد.
پس از حذف عنصر سفارشی از DOM disconnectedCallback
فراخوانی می شود.
attributeChangedCallback(attrName, oldValue, newValue)
زمانی فراخوانی می شود که هر یک از ویژگی های مشخص شده توسط کاربر تغییر کند.
هنگامی که عنصر سفارشی از یک documentFragment
دیگر از طریق adoptNode
در سند اصلی مانند HTMLTemplateElement
استفاده می شود، adoptedCallback
فراخوانی می شود.
DOM را رندر کنید
اکنون به عنصر سفارشی برگردید و مقداری DOM را با آن مرتبط کنید. وقتی عنصر به DOM متصل شد، محتوای آن را تنظیم کنید:
index.js
class RatingElement extends HTMLElement {
constructor() {
super();
this.rating = 0;
}
connectedCallback() {
this.innerHTML = `
<style>
rating-element {
display: inline-flex;
align-items: center;
}
rating-element button {
background: transparent;
border: none;
cursor: pointer;
}
</style>
<button class="thumb_down" >
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"/></svg>
</button>
<span class="rating">${this.rating}</span>
<button class="thumb_up">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"/></svg>
</button>
`;
}
}
customElements.define('rating-element', RatingElement);
در constructor
، یک ویژگی نمونه به نام rating
روی عنصر ذخیره می کنید. در connectedCallback
، فرزندان DOM را به <rating-element>
اضافه میکنید تا رتبهبندی فعلی را همراه با دکمههای شست بالا و پایین نمایش دهد.
4. Shadow DOM
چرا Shadow DOM؟
در مرحله قبل، متوجه خواهید شد که انتخابگرها در تگ سبکی که درج کرده اید، هر عنصر رتبه بندی را در صفحه و همچنین هر دکمه ای را انتخاب می کنند. این ممکن است منجر به نشت سبک ها از عنصر و انتخاب گره های دیگری شود که ممکن است قصد سبک دادن به آنها را نداشته باشید. علاوه بر این، سایر سبکهای خارج از این عنصر سفارشی ممکن است ناخواسته به گرههای داخل عنصر سفارشی شما استایل دهی کنند. به عنوان مثال، سعی کنید یک برچسب سبک در سر سند اصلی قرار دهید:
index.html
<!DOCTYPE html>
<html>
<head>
<script src="./index.js" type="module"></script>
<style>
span {
border: 1px solid red;
}
</style>
</head>
<body>
<rating-element></rating-element>
</body>
</html>
خروجی شما باید یک کادر حاشیه قرمز در اطراف دهانه برای رتبه بندی داشته باشد. این یک مورد بی اهمیت است، اما فقدان محصورسازی DOM ممکن است منجر به مشکلات بزرگتر برای برنامه های پیچیده تر شود. اینجاست که Shadow DOM وارد می شود.
چسباندن ریشه سایه
یک Shadow Root را به عنصر متصل کنید و DOM را در داخل آن ریشه رندر کنید:
index.js
class RatingElement extends HTMLElement {
constructor() {
super();
this.rating = 0;
}
connectedCallback() {
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `
<style>
:host {
display: inline-flex;
align-items: center;
}
button {
background: transparent;
border: none;
cursor: pointer;
}
</style>
<button class="thumb_down" >
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"/></svg>
</button>
<span class="rating">${this.rating}</span>
<button class="thumb_up">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"/></svg>
</button>
`;
}
}
customElements.define('rating-element', RatingElement);
وقتی صفحه را بهروزرسانی میکنید، متوجه میشوید که استایلهای موجود در سند اصلی دیگر نمیتوانند گرههای داخل Shadow Root را انتخاب کنند.
چگونه این کار را انجام دادی؟ در connectedCallback
شما this.attachShadow
نامیدید.attachShadow که یک ریشه سایه را به یک عنصر متصل می کند. حالت open
به این معنی است که محتوای سایه قابل بازرسی است و ریشه سایه از طریق this.shadowRoot
نیز قابل دسترسی است. به مؤلفه وب در بازرس کروم نیز نگاهی بیندازید:
اکنون باید یک ریشه سایه قابل گسترش ببینید که محتویات را در خود نگه می دارد. هر چیزی که در آن ریشه سایه قرار دارد Shadow DOM نامیده می شود. اگر بخواهید عنصر رتبهبندی را در Chrome Dev Tools انتخاب کنید و با $0.children
تماس بگیرید، متوجه میشوید که هیچ فرزندی برنمیگرداند. این به این دلیل است که Shadow DOM بخشی از همان درخت DOM به عنوان فرزندان مستقیم در نظر گرفته نمی شود، بلکه درخت سایه است.
DOM روشن
یک آزمایش: یک گره به عنوان فرزند مستقیم <rating-element>
اضافه کنید:
index.html
<rating-element>
<div>
This is the light DOM!
</div>
</rating-element>
صفحه را بازخوانی کنید، و خواهید دید که این گره DOM جدید در Light DOM این عنصر سفارشی در صفحه نمایش داده نمی شود. این به این دلیل است که Shadow DOM دارای ویژگی هایی برای کنترل نحوه نمایش گره های Light DOM به سایه dom از طریق عناصر <slot>
است.
5. قالب های HTML
چرا قالب ها
استفاده از innerHTML
و رشته های تحت اللفظی الگو بدون پاکسازی ممکن است باعث ایجاد مشکلات امنیتی در تزریق اسکریپت شود. روشها در گذشته شامل استفاده از DocumentFragment
میشدند، اما این روشها با مسائل دیگری مانند بارگیری تصاویر و اجرای اسکریپتها هنگام تعریف الگوها و همچنین ایجاد موانع برای قابلیت استفاده مجدد همراه بود. اینجاست که عنصر <template>
وارد می شود. قالبها DOM بیاثر، روشی بسیار کارآمد برای شبیهسازی گرهها، و قالبهای قابل استفاده مجدد را ارائه میکنند.
استفاده از قالب ها
در مرحله بعد، مؤلفه را به استفاده از قالب های HTML منتقل کنید:
index.html
<body>
<template id="rating-element-template">
<style>
:host {
display: inline-flex;
align-items: center;
}
button {
background: transparent;
border: none;
cursor: pointer;
}
</style>
<button class="thumb_down" >
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewbox="0 0 24 24" width="24"><path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"/></svg>
</button>
<span class="rating"></span>
<button class="thumb_up">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewbox="0 0 24 24" width="24"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"/></svg>
</button>
</template>
<rating-element>
<div>
This is the light DOM!
</div>
</rating-element>
</body>
در اینجا شما محتوای DOM را به یک تگ الگو در DOM سند اصلی منتقل کردید. اکنون تعریف عنصر سفارشی را مجدداً تنظیم کنید:
index.js
class RatingElement extends HTMLElement {
constructor() {
super();
this.rating = 0;
}
connectedCallback() {
const shadowRoot = this.attachShadow({mode: 'open'});
const templateContent = document.getElementById('rating-element-template').content;
const clonedContent = templateContent.cloneNode(true);
shadowRoot.appendChild(clonedContent);
this.shadowRoot.querySelector('.rating').innerText = this.rating;
}
}
customElements.define('rating-element', RatingElement);
برای استفاده از این عنصر الگو، الگو را پرس و جو می کنید، محتویات آن را دریافت می کنید و آن گره ها را با templateContent.cloneNode
که آرگومان true
یک کلون عمیق انجام می دهد، کلون می کنید. سپس dom را با داده ها مقداردهی اولیه می کنید.
تبریک میگوییم، شما اکنون یک کامپوننت وب دارید! متأسفانه هنوز هیچ کاری انجام نمی دهد، بنابراین در مرحله بعد، برخی از قابلیت ها را اضافه کنید.
6. افزودن قابلیت
صحافی اموال
در حال حاضر، تنها راه برای تنظیم رتبه بندی بر روی عنصر درجه بندی، ساختن عنصر، تنظیم ویژگی rating
روی شی و سپس قرار دادن آن در صفحه است. متأسفانه، عناصر بومی HTML اینگونه عمل نمی کنند. عناصر HTML بومی تمایل به به روز رسانی با تغییرات ویژگی و ویژگی دارند.
وقتی ویژگی rating
تغییر می کند، با افزودن خطوط زیر، عنصر سفارشی را به روز کنید:
index.js
constructor() {
super();
this._rating = 0;
}
set rating(value) {
this._rating = value;
if (!this.shadowRoot) {
return;
}
const ratingEl = this.shadowRoot.querySelector('.rating');
if (ratingEl) {
ratingEl.innerText = this._rating;
}
}
get rating() {
return this._rating;
}
شما یک تنظیمکننده و دریافتکننده برای ویژگی رتبهبندی اضافه میکنید، و سپس متن عنصر رتبهبندی را در صورت موجود بودن، بهروزرسانی میکنید. این بدان معناست که اگر بخواهید ویژگی رتبه بندی را روی عنصر تنظیم کنید، view به روز می شود. آن را در کنسول Dev Tools خود آزمایش کنید!
پیوندهای صفت
اکنون، هنگامی که ویژگی تغییر می کند، نمای را به روز کنید. این شبیه به ورودی است که هنگام تنظیم <input value="newValue">
نمای خود را به روز می کند. خوشبختانه، چرخه عمر مؤلفه وب شامل attributeChangedCallback
است. رتبه بندی را با افزودن خطوط زیر به روز کنید:
index.js
static get observedAttributes() {
return ['rating'];
}
attributeChangedCallback(attributeName, oldValue, newValue) {
if (attributeName === 'rating') {
const newRating = Number(newValue);
this.rating = newRating;
}
}
برای اینکه attributeChangedCallback
فعال شود، باید یک گیرنده ثابت برای RatingElement.observedAttributes which defines the attributes to be observed for changes
. سپس رتبه بندی را به صورت اعلامی در DOM تنظیم می کنید. آن را امتحان کنید:
index.html
<rating-element rating="5"></rating-element>
رتبه بندی اکنون باید به صورت اعلامی به روز شود!
عملکرد دکمه
اکنون تنها چیزی که از دست رفته عملکرد دکمه است. رفتار این مؤلفه باید به کاربر این امکان را بدهد که یک امتیاز رأی بالا یا پایین ارائه کند و بازخورد بصری به کاربر بدهد. میتوانید این مورد را با برخی شنوندگان رویداد و یک ویژگی بازتابی پیادهسازی کنید، اما ابتدا سبکها را برای ارائه بازخورد بصری با پیوست کردن خطوط زیر بهروزرسانی کنید:
index.html
<style>
...
:host([vote=up]) .thumb_up {
fill: green;
}
:host([vote=down]) .thumb_down {
fill: red;
}
</style>
در Shadow DOM، انتخابگر :host
به گره یا عنصر سفارشی اشاره دارد که Shadow Root به آن متصل است. در این حالت، اگر ویژگی vote
"up"
باشد، دکمه شست به بالا سبز میشود، اما اگر vote
"down", then it will turn the thumb-down button red
. اکنون، منطق این مورد را با ایجاد یک ویژگی/ویژگی بازتاب دهنده برای vote
، مشابه نحوه اجرای rating
پیاده سازی کنید. با تنظیم کننده و گیرنده ویژگی شروع کنید:
index.js
constructor() {
super();
this._rating = 0;
this._vote = null;
}
set vote(newValue) {
const oldValue = this._vote;
if (newValue === oldValue) {
return;
}
if (newValue === 'up') {
if (oldValue === 'down') {
this.rating += 2;
} else {
this.rating += 1;
}
} else if (newValue === 'down') {
if (oldValue === 'up') {
this.rating -= 2;
} else {
this.rating -= 1;
}
}
this._vote = newValue;
this.setAttribute('vote', newValue);
}
get vote() {
return this._vote;
}
شما ویژگی نمونه _vote
با null
در constructor
مقداردهی اولیه می کنید و در تنظیم کننده بررسی می کنید که آیا مقدار جدید متفاوت است. اگر چنین است، رتبه بندی را بر اساس آن تنظیم می کنید و مهمتر از همه، ویژگی vote
را با this.setAttribute
به میزبان بازتاب می دهید.
سپس، ویژگی binding را تنظیم کنید:
index.js
static get observedAttributes() {
return ['rating', 'vote'];
}
attributeChangedCallback(attributeName, oldValue, newValue) {
if (attributeName === 'rating') {
const newRating = Number(newValue);
this.rating = newRating;
} else if (attributeName === 'vote') {
this.vote = newValue;
}
}
باز هم، این همان فرآیندی است که با اتصال ویژگی rating
انجام دادید. شما vote
به observedAttributes
اضافه می کنید و ویژگی vote
در attributeChangedCallback
تنظیم می کنید. و در نهایت، تعدادی شنونده رویداد کلیک اضافه کنید تا عملکرد دکمه ها را ارائه دهید!
index.js
constructor() {
super();
this._rating = 0;
this._vote = null;
this._boundOnUpClick = this._onUpClick.bind(this);
this._boundOnDownClick = this._onDownClick.bind(this);
}
connectedCallback() {
...
this.shadowRoot.querySelector('.thumb_up')
.addEventListener('click', this._boundOnUpClick);
this.shadowRoot.querySelector('.thumb_down')
.addEventListener('click', this._boundOnDownClick);
}
disconnectedCallback() {
this.shadowRoot.querySelector('.thumb_up')
.removeEventListener('click', this._boundOnUpClick);
this.shadowRoot.querySelector('.thumb_down')
.removeEventListener('click', this._boundOnDownClick);
}
_onUpClick() {
this.vote = 'up';
}
_onDownClick() {
this.vote = 'down';
}
در constructor
شما برخی از شنوندگان کلیک را به عنصر متصل می کنید و مراجع را در اطراف نگه می دارید. در connectedCallback
به رویدادهای کلیک روی دکمه ها گوش می دهید. در disconnectedCallback
شما این شنوندگان را پاک میکنید و روی خود شنوندههای کلیکی، vote
مناسب را تنظیم میکنید.
تبریک میگوییم، شما اکنون یک کامپوننت وب با امکانات کامل دارید. سعی کنید روی چند دکمه کلیک کنید! اکنون مسئله این است که فایل JS من اکنون به 96 خط رسیده است، فایل HTML من به 43 خط رسیده است و کد برای چنین کامپوننت ساده ای کاملاً پیچیده و ضروری است. اینجاست که پروژه لایت گوگل وارد می شود!
7. Lit-html
ایست بازرسی کد
چرا lit-html
اول از همه، تگ <template>
مفید و کارآمد است، اما با منطق کامپوننت بسته بندی نشده است، بنابراین توزیع الگو با بقیه منطق دشوار می شود. علاوه بر این، نحوه استفاده از عناصر الگو به طور ذاتی به کدهای ضروری کمک می کند، که در بسیاری از موارد، در مقایسه با الگوهای کدگذاری اعلانی، منجر به کد خوانایی کمتری می شود.
اینجاست که lit-html وارد می شود! Lit html سیستم رندر Lit است که به شما امکان میدهد قالبهای HTML را در جاوا اسکریپت بنویسید، سپس آن قالبها را همراه با دادهها برای ایجاد و بهروزرسانی DOM به طور موثر رندر و دوباره رندر کنید. این شبیه به کتابخانه های محبوب JSX و VDOM است اما به صورت بومی در مرورگر اجرا می شود و در بسیاری از موارد بسیار کارآمدتر است.
استفاده از Lit HTML
سپس، rating-element
برای استفاده از الگوی Lit که از Tagged Template Literals استفاده میکند، منتقل کنید، که توابعی هستند که رشتههای الگو را بهعنوان آرگومانهایی با یک نحو خاص میگیرند. سپس Lit از عناصر قالب در زیر هود برای ارائه رندر سریع و همچنین ارائه برخی ویژگیهای ضدعفونی برای امنیت استفاده میکند. با انتقال <template>
در index.html
به یک الگوی Lit با افزودن یک متد render()
به وبکامپوننت شروع کنید:
index.js
// Dont forget to import from Lit!
import {render, html} from 'lit';
class RatingElement extends HTMLElement {
...
render() {
if (!this.shadowRoot) {
return;
}
const template = html`
<style>
:host {
display: inline-flex;
align-items: center;
}
button {
background: transparent;
border: none;
cursor: pointer;
}
:host([vote=up]) .thumb_up {
fill: green;
}
:host([vote=down]) .thumb_down {
fill: red;
}
</style>
<button class="thumb_down">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewbox="0 0 24 24" width="24"><path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"/></svg>
</button>
<span class="rating">${this.rating}</span>
<button class="thumb_up">
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewbox="0 0 24 24" width="24"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"/></svg>
</button>`;
render(template, this.shadowRoot);
}
}
همچنین می توانید الگوی خود را از index.html
حذف کنید. در این روش رندر شما متغیری به نام template
تعریف میکنید و تابع تحت اللفظی قالب برچسبگذاری شده html
را فراخوانی میکنید. همچنین متوجه خواهید شد که با استفاده از نحو درونیابی تحت اللفظی الگوی ${...}
یک اتصال ساده داده در عنصر span.rating
انجام داده اید. این بدان معنی است که در نهایت دیگر نیازی به به روز رسانی اجباری آن گره نخواهید داشت. علاوه بر این، شما روش render
روشن را فراخوانی می کنید که به طور همزمان الگو را در ریشه سایه رندر می کند.
مهاجرت به نحو اعلانی
اکنون که از شر عنصر <template>
خلاص شدهاید، کد را تغییر شکل دهید تا متد render
جدید را فراخوانی کنید. برای پاک کردن کد شنونده، میتوانید با استفاده از اتصال شنونده رویداد lit شروع کنید:
index.js
<button
class="thumb_down"
@click=${() => {this.vote = 'down'}}>
...
<button
class="thumb_up"
@click=${() => {this.vote = 'up'}}>
الگوهای روشن میتوانند شنونده رویداد را به گرهای با نحو الزام آور @EVENT_NAME اضافه کنند که در این حالت، هر بار که این دکمهها کلیک میشوند، ویژگی vote
را بهروزرسانی میکنید.
سپس، کد اولیه شنونده رویداد را در constructor
و connectedCallback
و disconnectedCallback
پاک کنید:
index.js
constructor() {
super();
this._rating = 0;
this._vote = null;
}
connectedCallback() {
this.attachShadow({mode: 'open'});
this.render();
}
// remove disonnectedCallback and _onUpClick and _onDownClick
شما توانستید منطق کلیک شنونده را از هر سه تماس بک حذف کنید و حتی disconnectedCallback
به طور کامل حذف کنید! شما همچنین توانستید تمام کدهای اولیه DOM را از connectedCallback
حذف کنید و بسیار زیباتر به نظر برسد. این همچنین به این معنی است که می توانید از شر روش های شنونده _onUpClick
و _onDownClick
خلاص شوید!
در نهایت، تنظیمکنندههای ویژگی را برای استفاده از روش render
بهروزرسانی کنید تا زمانی که ویژگیها یا ویژگیها تغییر میکنند، dom بتواند بهروزرسانی شود:
index.js
set rating(value) {
this._rating = value;
this.render();
}
...
set vote(newValue) {
const oldValue = this._vote;
if (newValue === oldValue) {
return;
}
if (newValue === 'up') {
if (oldValue === 'down') {
this.rating += 2;
} else {
this.rating += 1;
}
} else if (newValue === 'down') {
if (oldValue === 'up') {
this.rating -= 2;
} else {
this.rating -= 1;
}
}
this._vote = newValue;
this.setAttribute('vote', newValue);
// add render method
this.render();
}
در اینجا، شما توانستید منطق بهروزرسانی dom را از تنظیمکننده rating
حذف کنید و یک تماس برای render
از تنظیمکننده vote
اضافه کردید. اکنون الگو بسیار خواناتر است زیرا اکنون میتوانید ببینید که در کجا اتصالها و شنوندگان رویداد اعمال میشوند.
صفحه را بازخوانی کنید، و باید یک دکمه رتبهبندی عملکردی داشته باشید که وقتی رأی موافق فشار داده میشود، شبیه این باشد!
8. LitElement
چرا LitElement
برخی از مشکلات هنوز در کد وجود دارد. ابتدا، اگر ویژگی یا ویژگی vote
را تغییر دهید، ممکن است ویژگی rating
را تغییر دهد که منجر به دو بار فراخوانی render
می شود. علیرغم اینکه تماسهای تکراری رندر اساساً بدون عملیات و کارآمد هستند، جاوا اسکریپت VM هنوز هم برای فراخوانی آن تابع دو بار به صورت همزمان زمان میگذراند. دوم، افزودن ویژگیها و ویژگیهای جدید خستهکننده است، زیرا به کدهای دیگ بخار زیادی نیاز دارد. اینجاست که LitElement
وارد می شود!
LitElement
کلاس پایه Lit برای ایجاد کامپوننت های وب سریع و سبک است که می توانند در فریمورک ها و محیط ها استفاده شوند. در مرحله بعد، نگاهی به آنچه LitElement
می تواند برای ما در rating-element
با تغییر پیاده سازی برای استفاده از آن انجام دهد، بیندازید!
با استفاده از LitElement
با وارد کردن و زیر کلاس بندی کلاس پایه LitElement
از بسته lit
شروع کنید:
index.js
import {LitElement, html, css} from 'lit';
class RatingElement extends LitElement {
// remove connectedCallback()
...
شما LitElement
را وارد می کنید که کلاس پایه جدید برای rating-element
است. سپس وارد کردن html
و در نهایت css
خود را حفظ میکنید که به ما امکان میدهد واژههای قالب برچسبگذاری شده با css را برای ریاضیات css، قالببندی و سایر ویژگیها در زیر هود تعریف کنیم.
سپس، استایل ها را از روش رندر به شیوه نامه استاتیک Lit منتقل کنید:
index.js
class RatingElement extends LitElement {
static get styles() {
return css`
:host {
display: inline-flex;
align-items: center;
}
button {
background: transparent;
border: none;
cursor: pointer;
}
:host([vote=up]) .thumb_up {
fill: green;
}
:host([vote=down]) .thumb_down {
fill: red;
}
`;
}
...
این جایی است که اکثر سبک ها در Lit زندگی می کنند. Lit از این سبکها استفاده میکند و از ویژگیهای مرورگر مانند Constructable Stylesheets برای ارائه زمانهای رندر سریعتر و همچنین در صورت لزوم از طریق Web Components polyfill در مرورگرهای قدیمیتر استفاده میکند.
چرخه زندگی
Lit مجموعهای از روشهای بازگشت به تماس چرخه حیات رندر را در بالای فراخوانهای اصلی Web Component معرفی میکند. این تماسهای برگشتی زمانی فعال میشوند که ویژگیهای Lit اعلام شده تغییر میکنند.
برای استفاده از این ویژگی، باید به صورت ایستا مشخص کنید که کدام ویژگی چرخه حیات رندر را راه اندازی می کند.
index.js
static get properties() {
return {
rating: {
type: Number,
},
vote: {
type: String,
reflect: true,
}
};
}
// remove observedAttributes() and attributeChangedCallback()
// remove set rating() get rating()
در اینجا، شما تعریف میکنید که rating
و vote
چرخه حیات رندر LitElement و همچنین تعریف انواعی که برای تبدیل ویژگیهای رشته به ویژگیها استفاده میشوند، راهاندازی میکند.
<user-profile .name=${this.user.name} .age=${this.user.age}>
${this.user.family.map(member => html`
<family-member
.name=${member.name}
.relation=${member.relation}>
</family-member>`)}
</user-profile>
علاوه بر این، پرچم reflect
در ویژگی vote
به طور خودکار ویژگی vote
عنصر میزبان را که به صورت دستی در تنظیم کننده vote
فعال کرده اید، به روز می کند.
اکنون که بلوک ویژگی های ثابت را دارید، می توانید تمام منطق به روز رسانی رندر ویژگی و ویژگی را حذف کنید. این بدان معنی است که می توانید روش های زیر را حذف کنید:
-
connectedCallback
-
observedAttributes
-
attributeChangedCallback
-
rating
(تنظیم کننده ها و دریافت کنندگان) -
vote
(تنظیم کننده ها و دریافت کننده ها اما منطق تغییر را از تنظیم کننده حفظ کنید)
چیزی که نگه می دارید constructor
و همچنین اضافه کردن یک روش چرخه حیات جدید willUpdate
است:
index.js
constructor() {
super();
this.rating = 0;
this.vote = null;
}
willUpdate(changedProps) {
if (changedProps.has('vote')) {
const newValue = this.vote;
const oldValue = changedProps.get('vote');
if (newValue === 'up') {
if (oldValue === 'down') {
this.rating += 2;
} else {
this.rating += 1;
}
} else if (newValue === 'down') {
if (oldValue === 'up') {
this.rating -= 2;
} else {
this.rating -= 1;
}
}
}
}
// remove set vote() and get vote()
در اینجا، شما به سادگی rating
و vote
را مقداردهی اولیه میکنید و منطق تنظیمکننده vote
را به روش چرخه حیات willUpdate
منتقل میکنید. متد willUpdate
قبل از render
میشود، هرگاه ویژگی بهروزرسانی تغییر کند، زیرا LitElement تغییرات ویژگی را دستهبندی میکند و رندر را ناهمزمان میکند. تغییرات در ویژگیهای واکنشی (مانند this.rating
) در willUpdate
باعث ایجاد تماسهای چرخه حیات render
غیرضروری نمیشود.
در نهایت، render
یک روش چرخه حیات LitElement است که از ما میخواهد یک الگوی Lit را برگردانیم :
index.js
render() {
return html`
<button
class="thumb_down"
@click=${() => {this.vote = 'down'}}>
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewbox="0 0 24 24" width="24"><path d="M15 3H6c-.83 0-1.54.5-1.84 1.22l-3.02 7.05c-.09.23-.14.47-.14.73v2c0 1.1.9 2 2 2h6.31l-.95 4.57-.03.32c0 .41.17.79.44 1.06L9.83 23l6.59-6.59c.36-.36.58-.86.58-1.41V5c0-1.1-.9-2-2-2zm4 0v12h4V3h-4z"/></svg>
</button>
<span class="rating">${this.rating}</span>
<button
class="thumb_up"
@click=${() => {this.vote = 'up'}}>
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewbox="0 0 24 24" width="24"><path d="M1 21h4V9H1v12zm22-11c0-1.1-.9-2-2-2h-6.31l.95-4.57.03-.32c0-.41-.17-.79-.44-1.06L14.17 1 7.59 7.59C7.22 7.95 7 8.45 7 9v10c0 1.1.9 2 2 2h9c.83 0 1.54-.5 1.84-1.22l3.02-7.05c.09-.23.14-.47.14-.73v-2z"/></svg>
</button>`;
}
دیگر لازم نیست ریشه سایه را بررسی کنید، و دیگر لازم نیست تابع render
که قبلاً از بسته 'lit'
وارد شده است را فراخوانی کنید.
عنصر شما باید اکنون در پیش نمایش ارائه شود. آن را یک کلیک کنید!
9. تبریک می گویم
تبریک می گوییم، شما با موفقیت یک کامپوننت وب را از ابتدا ساخته اید و آن را به یک LitElement تبدیل کرده اید!
Lit بسیار کوچک است (< 5 کیلوبایت کوچک شده + gzipped)، فوق العاده سریع، و کدنویسی با آن واقعا سرگرم کننده است! می توانید کامپوننت هایی را بسازید که توسط فریمورک های دیگر مصرف شوند یا می توانید با آن برنامه های کامل بسازید!
اکنون میدانید که Web Component چیست، چگونه میتوان آن را ساخت و چگونه Lit ساخت آنها را آسانتر میکند!
ایست بازرسی کد
آیا می خواهید کد نهایی خود را با کد ما بررسی کنید؟ اینجا را مقایسه کنید.
بعدش چی؟
برخی از کدهای دیگر را بررسی کنید!
- برای توسعه دهندگان React روشن شد
- یک نمایشگر آجری با عنصر روشن بسازید
- یک کامپوننت Stories با عنصر روشن بسازید
در ادامه مطلب
- آموزش تعاملی روشن
- اسناد روشن
- باز کردن مؤلفه های وب - انجمنی که انجمن هدایت و ابزارسازی را اجرا می کند
- WebComponents.dev - یک مؤلفه وب در تمام چارچوب های شناخته شده ایجاد کنید
جامعه
- Lit and Friends Slack - بزرگترین انجمن Web Components
- @buildWithLit در توییتر - حساب توییتر تیم سازنده Lit
- Web Components SF - گردهمایی Web Components برای سانفرانسیسکو