React Geliştiricileri için Edebiyat

1. Giriş

Lit nedir?

Lit, herhangi bir çerçevede veya çerçeve olmadan çalışan hızlı ve hafif web bileşenleri oluşturmak için kullanılan basit bir kitaplıktır. Lit ile paylaşılabilir bileşenler, uygulamalar, tasarım sistemleri ve daha fazlasını oluşturabilirsiniz.

Neler öğreneceksiniz?

Aşağıdakiler gibi çeşitli React kavramlarını Lit'e çevirme:

  • JSX ve Şablon Oluşturma
  • Bileşenler ve Öğeler
  • Durum ve Yaşam Döngüsü
  • Dikkat çekici girişler
  • Çocuklar
  • Refs
  • Aracılık durumu

Ne oluşturacaksınız?

Bu codelab'in sonunda, React bileşen kavramlarını Lit'teki benzerlerine dönüştürebileceksiniz.

İhtiyacınız olanlar

  • Chrome, Safari, Firefox veya Edge'in en son sürümü
  • HTML, CSS, JavaScript ve Chrome Geliştirici Araçları hakkında bilgi sahibi olmak
  • React bilgisi
  • (Gelişmiş) En iyi geliştirme deneyimini istiyorsanız VS Code'u indirin. Ayrıca VS Code için lit-plugin ve NPM'ye de ihtiyacınız olacaktır.

2. Lit ve React

Lit'in temel kavramları ve özellikleri birçok açıdan React'e benzer ancak Lit'in bazı temel farklılıkları ve ayırt edici özellikleri de vardır:

Küçük

Lit çok küçüktür: React + ReactDOM'un 40 KB'den fazla olmasına karşın Lit, küçültülmüş ve sıkıştırılmış olarak yaklaşık 5 KB'tır.

Paket boyutunun kb cinsinden küçültülmüş ve sıkıştırılmış halini gösteren çubuk grafik. Lit çubuğu 5 KB, React + React DOM ise 42,2 KB'tır.

Hızlıdır.

Lit'in şablon sistemini (lit-html) React'in VDOM'uyla karşılaştıran kamu kıyaslamalarında, en kötü durumda lit-html'in React'ten % 8-10 daha hızlı, daha yaygın kullanım alanlarında ise %50'den fazla daha hızlı olduğu görülmektedir.

LitElement (Lit'in bileşen temel sınıfı), lit-html'ye minimum ek yük getirir ancak bellek kullanımı, etkileşim ve başlatma süreleri gibi bileşen özellikleri karşılaştırıldığında React'in performansını%16-30 oranında aşar.

Performansın milisaniye cinsinden (daha düşük değer daha iyidir) Lit ile React karşılaştırmasını gösteren gruplandırılmış çubuk grafik

Derleme gerektirmez

ES modülleri ve etiketlenmiş şablon değişmezleri gibi yeni tarayıcı özellikleriyle Lit'in çalışması için derleme yapılması gerekmez. Bu, geliştirme ortamlarının bir komut dosyası etiketi, bir tarayıcı ve bir sunucu ile kurulabileceği ve hemen çalışmaya başlayabileceğiniz anlamına gelir.

ES modülleri ve Skypack veya UNPKG gibi modern CDN'ler ile başlamak için NPM'ye bile ihtiyacınız olmayabilir.

Ancak isterseniz Lit kodunu oluşturup optimize etmeye devam edebilirsiniz. Yerel ES modülleri etrafındaki son geliştirici birleşimi Lit için iyi oldu. Lit, normal JavaScript'tir ve çerçeveye özgü CLI'ler veya derleme işleme gerekmez.

Çerçeveden bağımsız

Lit'in bileşenleri, Web Bileşenleri adı verilen bir dizi web standardı üzerine kuruludur. Bu, Lit'te oluşturulan bir bileşenin mevcut ve gelecekteki çerçevelerde çalışacağı anlamına gelir. HTML öğelerini destekliyorsa Web Bileşenleri'ni de destekler.

Çerçeveler arası birlikte çalışabilirlik ile ilgili tek sorun, çerçevelerin DOM için kısıtlayıcı destek sunmasıdır. React bu çerçevelerden biridir ancak Refs aracılığıyla kaçış kapılarına izin verir ve React'teki Refs iyi bir geliştirici deneyimi sunmaz.

Lit ekibi, Lit bileşenlerinizi otomatik olarak ayrıştıracak ve referans kullanmanıza gerek kalmaması için bir React sarmalayıcı oluşturacak @lit-labs/react adlı deneysel bir proje üzerinde çalışıyor.

Ayrıca, Custom Elements Everywhere, hangi çerçevelerin ve kitaplıkların özel öğelerle iyi çalıştığını gösterir.

Birinci sınıf TypeScript desteği

Lit kodunuzun tamamını JavaScript'te yazmanız mümkün olsa da Lit, TypeScript'te yazılmıştır ve Lit ekibi, geliştiricilerin de TypeScript kullanmasını önerir.

Lit ekibi, lit-analyzer ve lit-plugin ile hem geliştirme hem de derleme sırasında TypeScript türü kontrolü ve IntelliSense'i Lit şablonlarına getiren projelerin sürdürülmesine yardımcı olmak için Lit topluluğuyla birlikte çalışıyor.

IDE'nin, ana hatları çizilmiş boole'u sayıya ayarlamak için uygunsuz bir tür kontrolü gösteren ekran görüntüsü

IntelliSense önerilerini gösteren bir IDE'nin ekran görüntüsü

Geliştirici araçları tarayıcıya yerleşiktir.

Lit bileşenleri, DOM'daki HTML öğeleridir. Bu nedenle, bileşenlerinizi incelemek için tarayıcınıza herhangi bir araç veya uzantı yüklemeniz gerekmez.

Geliştirici araçlarını açıp bir öğe seçerek özelliklerini veya durumunu inceleyebilirsiniz.

Chrome geliştirici araçlarının, $0 returns <mwc-textfield>, $0.value returns hello world, $0.outlined returns true ve {$0} shows property expansion değerlerini gösterdiği resim

Sunucu tarafı oluşturma (SSR) göz önünde bulundurularak oluşturulmuştur.

Lit 2, SSR desteği göz önünde bulundurularak geliştirilmiştir. Bu codelab yazılırken Lit ekibi henüz SSR araçlarını kararlı bir biçimde yayınlamamıştı ancak Lit ekibi, sunucu tarafında oluşturulan bileşenleri Google ürünlerinde kullanmaya başlamış ve React uygulamalarında SSR'yi test etmişti. Lit ekibi, bu araçları yakında GitHub'da herkese açık olarak yayınlamayı planlıyor.

Bu süre zarfında Lit ekibinin ilerleme durumunu buradan takip edebilirsiniz.

Düşük giriş maliyeti

Lit'i kullanmak için önemli bir taahhüt gerekmez. Lit'te bileşenler oluşturabilir ve bunları mevcut projenize ekleyebilirsiniz. Web bileşenleri diğer çerçevelerde çalıştığı için beğenmediğiniz kısımları varsa uygulamanın tamamını tek seferde dönüştürmeniz gerekmez.

Lit ile tam bir uygulama oluşturduysanız ve başka bir şeye geçmek istiyorsanız Bu durumda, mevcut Lit uygulamanızı yeni çerçevenize yerleştirebilir ve istediğiniz her şeyi yeni çerçevenin bileşenlerine taşıyabilirsiniz.

Ayrıca, birçok modern çerçeve, web bileşenlerinde çıkışı destekler. Bu nedenle, genellikle Lit öğesinin içine sığabilirler.

3. Playground'u kurma ve keşfetme

Bu codelab'i iki şekilde yapabilirsiniz:

  • Bu işlemi tamamen çevrimiçi olarak tarayıcıda yapabilirsiniz.
  • (Gelişmiş) VS Code kullanarak yerel makinenizde yapabilirsiniz.

Koda erişme

Bu codelab'de, Lit playground'a yönlendiren bağlantılar yer alacak. Örneğin:

Playground, tamamen tarayıcınızda çalışan bir kod korumalı alanıdır. TypeScript ve JavaScript dosyalarını derleyip çalıştırabilir, ayrıca içe aktarmaları otomatik olarak düğüm modüllerine çözebilir. Örneğin:

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

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

Bu kontrol noktalarını başlangıç noktası olarak kullanarak eğitimin tamamını Lit Playground'da yapabilirsiniz. VS Code kullanıyorsanız bu kontrol noktalarını kullanarak herhangi bir adımın başlangıç kodunu indirebilir ve çalışmanızı kontrol edebilirsiniz.

Exploring the lit playground UI

Dosya seçici sekme çubuğu 1. Bölüm, kod düzenleme bölümü 2. Bölüm, çıkış önizlemesi 3. Bölüm ve önizlemeyi yeniden yükleme düğmesi 4. Bölüm olarak etiketlenir.

Lit playground kullanıcı arayüzü ekran görüntüsünde, bu codelab'de kullanacağınız bölümler vurgulanmıştır.

  1. Dosya seçici. Artı düğmesini bulun...
  2. Dosya düzenleyici.
  3. Kod önizlemesi.
  4. Yeniden yükle düğmesi.
  5. İndir düğmesi.

VS Code kurulumu (Gelişmiş)

Bu VS Code kurulumunu kullanmanın avantajları şunlardır:

  • Şablon türü kontrolü
  • Şablon intellisense ve otomatik tamamlama

NPM, VS Code (lit-plugin eklentisiyle birlikte) zaten yüklüyse ve bu ortamı nasıl kullanacağınızı biliyorsanız aşağıdaki adımları uygulayarak bu projeleri indirip başlatabilirsiniz:

  • İndir düğmesine basın.
  • Tar dosyasının içeriğini bir dizine çıkarın.
  • (TS ise) ES modüllerini ve ES2015+ değerini çıkaran bir quick tsconfig oluşturun.
  • Çıplak modül tanımlayıcılarını çözümleyebilen bir geliştirme sunucusu yükleyin (Lit ekibi @web/dev-server'ı önerir)
  • Geliştirme sunucusunu çalıştırın ve tarayıcınızı açın (@web/dev-server kullanıyorsanız npx web-dev-server --node-resolve --watch --open kullanabilirsiniz)
    • Örnekteki package.json karakterini kullanıyorsanız npm run dev karakterini kullanın.

4. JSX ve Şablon Oluşturma

Bu bölümde, Lit'te şablon oluşturmanın temellerini öğreneceksiniz.

JSX ve Lit şablonları

JSX, JavaScript'in söz dizimi uzantısıdır. React kullanıcılarının JavaScript kodlarında kolayca şablon yazmasına olanak tanır. Lit şablonları da benzer bir amaca hizmet eder: Bir bileşenin kullanıcı arayüzünü durumunun bir işlevi olarak ifade etmek.

Temel Söz Dizimi

React'te JSX hello world'ü şu şekilde oluşturursunuz:

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

Yukarıdaki örnekte iki öğe ve dahil edilen bir "ad" değişkeni vardır. Lit'te şunları yaparsınız:

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 şablonlarında, şablonlardaki birden fazla öğeyi gruplandırmak için React Fragment'e gerek olmadığını unutmayın.

Lit'te şablonlar, Lit'in adını aldığı yer olan html tagged template LITeral ile sarmalanır.

Şablon değerleri

Lit şablonları, TemplateResult olarak bilinen diğer Lit şablonlarını kabul edebilir. Örneğin, name öğesini italik (<i>) etiketleriyle ve etiketlenmiş bir şablon değişmez değeriyle (N.B.) sarmalayın. Tek tırnak karakteri (') değil, vurgu işareti karakteri (`) kullandığınızdan emin olun.

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 TemplateResults, dizileri, dizeleri, diğer TemplateResults'leri ve yönergeleri kabul edebilir.

Bir alıştırma olarak aşağıdaki React kodunu Lit'e dönüştürmeyi deneyin:

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

Yanıt:

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

Pas ve set oluşturma

JSX ve Lit'in söz dizimleri arasındaki en büyük farklardan biri veri bağlama söz dizimidir. Örneğin, bağlamaları olan şu React girişini ele alalım:

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

Yukarıdaki örnekte, aşağıdakileri yapan bir giriş tanımlanmıştır:

  • Devre dışı bırakılanı tanımlanmış bir değişkene (bu durumda yanlış) ayarlar.
  • Sınıfı static-class artı bir değişkene (bu örnekte "static-class my-class") ayarlar.
  • Varsayılan bir değer ayarlar

Lit'te şunları yaparsınız:

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 örneğinde, disabled özelliğini açıp kapatmak için bir boole bağlaması eklenir.

Ardından, className yerine doğrudan class özelliğiyle bağlama vardır. Sınıfları değiştirmek için bildirim temelli bir yardımcı olan classMap yönergesini kullanmıyorsanız class özelliğine birden fazla bağlama eklenebilir.

Son olarak, girişte value özelliği ayarlanır. React'in aksine, bu işlem giriş öğesinin yerel uygulamasını ve davranışını takip ettiğinden giriş öğesini salt okunur olarak ayarlamaz.

Lit prop bağlama söz dizimi

html`<my-element ?attribute-name=${booleanVar}>`;
  • ? öneki, bir öğedeki özelliği açıp kapatmak için kullanılan bağlama söz dizimidir.
  • inputRef.toggleAttribute('attribute-name', booleanVar) etiketine eş değer.
  • disabled kullanan öğeler için yararlıdır. disabled="false", inputElement.hasAttribute('disabled') === true nedeniyle DOM tarafından hâlâ doğru olarak okunur.
html`<my-element .property-name=${anyVar}>`;
  • . öneki, bir öğenin özelliğini ayarlamak için kullanılan bağlama söz dizimidir.
  • inputRef.propertyName = anyVar tutarına eşdeğer
  • Nesneler, diziler veya sınıflar gibi karmaşık verileri aktarmak için uygundur.
html`<my-element attribute-name=${stringVar}>`;
  • Bir öğenin özelliğine bağlanır.
  • inputRef.setAttribute('attribute-name', stringVar) etiketine eş değer.
  • Temel değerler, stil kuralı seçicileri ve querySelector'lar için uygundur.

İşleyici geçirme

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

Yukarıdaki örnekte, aşağıdakileri yapan bir giriş tanımlanmıştır:

  • Giriş tıklandığında "tıkla" kelimesini günlüğe kaydetme
  • Kullanıcı bir karakter yazdığında girişin değerini günlüğe kaydetme

Lit'te şunları yaparsınız:

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 örneğinde, click etkinliğine @click ile bir işleyici eklenmiştir.

Ardından, onChange yerine <input>'nin yerel input etkinliğine bağlama vardır. Bunun nedeni, yerel change etkinliğinin yalnızca blur üzerinde tetiklenmesidir (React bu etkinlikleri soyutlar).

Lit etkinlik işleyici söz dizimi

html`<my-element @event-name=${() => {...}}></my-element>`;
  • @ öneki, bir etkinlik işleyicinin bağlama söz dizimidir.
  • inputRef.addEventListener('event-name', ...) etiketine eş değer.
  • Yerel DOM etkinlik adlarını kullanır.

5. Bileşenler ve Öğeler

Bu bölümde, Lit sınıfı bileşenleri ve işlevleri hakkında bilgi edineceksiniz. Durum ve kancalar sonraki bölümlerde daha ayrıntılı olarak ele alınmaktadır.

Sınıf Bileşenleri ve LitElement

React sınıf bileşeninin Lit'teki karşılığı LitElement'tir ve Lit'in "tepkisel özellikler" kavramı, React'in props ve state özelliklerinin birleşimidir. Örneğin:

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

Yukarıdaki örnekte, aşağıdakileri yapan bir React bileşeni var:

  • name oluşturur.
  • name politikasının varsayılan değerini boş dize ("") olarak ayarlar.
  • name adlı kullanıcının görevini "Elliott" adlı kullanıcıya yeniden atar.

LitElement'te bu işlemi şu şekilde yapabilirsiniz:

TypeScript'te:

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

JavaScript'te:

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 dosyasında ise:

<!-- index.html -->
<head>
  <script type="module" src="./index.js"></script>
</head>
<body>
  <welcome-banner name="Elliott"></welcome-banner>
</body>

Yukarıdaki örnekte neler olduğuna dair bir inceleme:

@property({type: String})
name = '';
  • Herkese açık reaktif bir özellik tanımlar. Bu özellik, bileşeninizin genel API'sinin bir parçasıdır.
  • Bir özelliği (varsayılan olarak) ve bileşeninizdeki bir mülkü kullanıma sunar.
  • Bileşenin özelliğinin (dizeler) nasıl değere çevrileceğini tanımlar.
static get properties() {
  return {
    name: {type: String}
  }
}
  • Bu, @property TS dekoratörüyle aynı işlevi görür ancak JavaScript'te yerel olarak çalışır.
render() {
  return html`<h1>Hello, ${this.name}</h1>`
}
  • Bu işlev, herhangi bir tepkisel özellik her değiştirildiğinde çağrılır.
@customElement('welcome-banner')
class WelcomeBanner extends LitElement {
  ...
}
  • Bu, bir HTML öğesi etiket adını bir sınıf tanımıyla ilişkilendirir.
  • Custom Elements standardı nedeniyle, etiket adı tire (-) içermelidir.
  • LitElement'teki this, özel öğenin örneğini (bu durumda <welcome-banner>) ifade eder.
customElements.define('welcome-banner', WelcomeBanner);
  • Bu, @customElement TS dekoratörünün JavaScript eşdeğeridir.
<head>
  <script type="module" src="./index.js"></script>
</head>
  • Özel öğe tanımını içe aktarır.
<body>
  <welcome-banner name="Elliott"></welcome-banner>
</body>
  • Özel öğeyi sayfaya ekler.
  • name özelliğini 'Elliott' olarak ayarlar.

İşlev Bileşenleri

Lit, JSX veya bir ön işlemci kullanmadığı için işlev bileşeninin bire bir yorumunu yapmaz. Ancak özellikleri alan ve bu özelliklere göre DOM oluşturan bir işlev oluşturmak oldukça basittir. Örneğin:

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

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

Lit'te bu ifade şu şekilde olur:

import {html, render} from 'lit';

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

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

6. Durum ve Yaşam Döngüsü

Bu bölümde Lit'in durumu ve yaşam döngüsü hakkında bilgi edineceksiniz.

Eyalet

Lit'in "Reactive Properties" (Tepkisel Özellikler) kavramı, React'in durumu ve özelliklerinin bir karışımıdır. Reaktif özellikler değiştirildiğinde bileşen yaşam döngüsünü tetikleyebilir. Reaktif özellikler iki varyantta sunulur:

Herkese açık tepkisel özellikler

// 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 tarafından tanımlanır.
  • React'in props ve state'ine benzer ancak değiştirilebilir
  • Bileşenin tüketicileri tarafından erişilen ve ayarlanan genel API

Dahili reaktif durum

// 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 tarafından tanımlanır.
  • React'in durumuna benzer ancak değiştirilebilir
  • Genellikle bileşenden veya alt sınıflardan erişilen özel dahili durum

Yaşam döngüsü

Lit'in yaşam döngüsü, React'in yaşam döngüsüne oldukça benzer ancak bazı önemli farklılıklar vardır.

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';
  }
}
  • Edebi eşdeğeri de constructor
  • Süper çağrıya herhangi bir şey iletmenize gerek yoktur.
  • Şununla çağrıldı (tamamen kapsayıcı değildir):
    • document.createElement
    • document.innerHTML
    • new ComponentClass()
    • Sayfada yükseltilmemiş bir etiket adı varsa ve tanım @customElement veya customElements.define ile yüklenip kaydedilmişse
  • React'in constructor işlevine benzer

render

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

// Lit
render() {
  return html`<div>Hello World</div>`;
}
  • Edebi eşdeğeri de render
  • TemplateResult veya string gibi oluşturulabilir tüm sonuçları döndürebilir.
  • React'e benzer şekilde, render() saf bir işlev olmalıdır.
  • createRenderRoot() hangi düğümü döndürürse ona göre oluşturulur (varsayılan olarak ShadowRoot)

componentDidMount

componentDidMount, Lit'in firstUpdated ve connectedCallback yaşam döngüsü geri çağırma yöntemlerinin bir kombinasyonuna benzer.

firstUpdated

import Chart from 'chart.js';

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

// Lit
firstUpdated() {
  this._chart = new Chart(this.chartEl, {...});
}
  • Bileşenin şablonu, bileşenin köküne ilk kez oluşturulduğunda çağrılır.
  • Yalnızca öğe bağlandığında çağrılır.Örneğin, bu düğüm DOM ağacına eklenene kadar document.createElement('my-component') aracılığıyla çağrılmaz.
  • Bu, bileşen tarafından oluşturulan DOM'un gerekli olduğu bileşen kurulumunu gerçekleştirmek için iyi bir yerdir.
  • React'in aksine, componentDidMount içindeki reaktif özelliklerde yapılan değişiklikler, tarayıcı genellikle değişiklikleri aynı karede toplasa da firstUpdated içinde yeniden oluşturmaya neden olur. Bu değişiklikler kök DOM'a erişim gerektirmiyorsa genellikle willUpdate içine yerleştirilmelidir.

connectedCallback

// React
componentDidMount() {
  this.window.addEventListener('resize', this.boundOnResize);
}

// Lit
connectedCallback() {
  super.connectedCallback();
  this.window.addEventListener('resize', this.boundOnResize);
}
  • Özel öğe DOM ağacına her eklendiğinde çağrılır.
  • Özel öğeler, React bileşenlerinin aksine DOM'dan ayrıldığında yok edilmez ve bu nedenle birden çok kez "bağlanabilir"
    • firstUpdated tekrar aranmayacak
  • DOM'u yeniden başlatmak veya bağlantı kesildiğinde temizlenen etkinlik işleyicilerini yeniden eklemek için kullanışlıdır.
  • Not: connectedCallback, firstUpdated'den önce çağrılabilir. Bu nedenle, ilk çağrıda DOM kullanılamayabilir.

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);
  }
}
  • Lit.dev'deki eşdeğeri updated (İngilizce "update" fiilinin geçmiş zaman hâli kullanılarak)
  • React'in aksine, updated ilk oluşturma işleminde de çağrılır.
  • React'in componentDidUpdate işlevine benzer

componentWillUnmount

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

// Lit
disconnectedCallback() {
  super.disconnectedCallback();
  this.window.removeEventListener('resize', this.boundOnResize);
}
  • Lit eşdeğeri, disconnectedCallback ile benzerdir.
  • React bileşenlerinin aksine, özel öğeler DOM'dan ayrıldığında bileşen yok edilmez.
  • componentWillUnmount'nın aksine, disconnectedCallback, öğe ağaçtan kaldırıldıktan sonra çağrılır.
  • Kökün içindeki DOM, kökün alt ağacına bağlı kalır.
  • Tarayıcının bileşeni çöp toplama işlemine tabi tutabilmesi için etkinlik işleyicileri ve bellek sızıntısı olan referansları temizlemek için kullanışlıdır.

Egzersiz

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

Yukarıdaki örnekte, aşağıdakileri yapan basit bir saat var:

  • "Hello World! Şu an saat" deyip saati gösterir.
  • Saat her saniye güncellenir.
  • Söküldüğünde, tik işaretini çağıran aralığı temizler.

Öncelikle bileşen sınıfı beyanıyla başlayın:

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

Ardından, bileşenin kullanıcıları date değerini doğrudan ayarlamayacağından date değerini ilk kullanıma hazırlayın ve @state ile dahili bir reaktif özellik olarak tanımlayın.

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

Ardından, şablonu oluşturun.

// Lit (JS & TS)
render() {
  return html`
    <div>
      <h1>Hello, World!</h1>
      <h2>It is ${this.date.toLocaleTimeString()}.</h2>
    </div>
  `;
}

Şimdi de onay işareti yöntemini uygulayın.

tick() {
  this.date = new Date();
}

Ardından componentDidMount'nın uygulanması gelir. Yine, Lit analogu firstUpdated ve connectedCallback karışımıdır. Bu bileşen söz konusu olduğunda, tick işlevini setInterval ile çağırmak için kök içindeki DOM'a erişim gerekmez. Ayrıca, öğe belge ağacından kaldırıldığında aralık temizlenir. Bu nedenle, öğe yeniden eklenirse aralığın tekrar başlatılması gerekir. Bu nedenle, connectedCallback bu durumda daha iyi bir seçimdir.

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

Son olarak, öğe belge ağacından ayrıldıktan sonra tikin yürütülmemesi için aralığı temizleyin.

// Lit (TS & JS)
disconnectedCallback() {
  super.disconnectedCallback();
  clearInterval(this.timerId);
}

Tüm bilgiler bir araya getirildiğinde şu şekilde görünür:

// Lit (TS)
import {LitElement, html} from 'lit';
import {customElement, state} from 'lit/decorators.js';

@customElement('lit-clock')
class LitClock extends LitElement {
  @state()
  private date = new Date();
  private timerId = -1 as unknown as ReturnType<typeof setTimeout>;

  connectedCallback() {
    super.connectedCallback();
    this.timerId = setInterval(
      () => this.tick(),
      1000
    );
  }

  tick() {
    this.date = new Date();
  }

  render() {
    return html`
      <div>
        <h1>Hello, World!</h1>
        <h2>It is ${this.date.toLocaleTimeString()}.</h2>
      </div>
    `;
  }

  disconnectedCallback() {
    super.disconnectedCallback();
    clearInterval(this.timerId);
  }
}

// Lit (JS)
import {LitElement, html} from 'lit';

class LitClock extends LitElement {
  static get properties() {
    return {
      date: {state: true}
    }
  }

  constructor() {
    super();
    this.date = new Date();
  }

  connectedCallback() {
    super.connectedCallback();
    this.timerId = setInterval(
      () => this.tick(),
      1000
    );
  }

  tick() {
    this.date = new Date();
  }

  render() {
    return html`
      <div>
        <h1>Hello, World!</h1>
        <h2>It is ${this.date.toLocaleTimeString()}.</h2>
      </div>
    `;
  }

  disconnectedCallback() {
    super.disconnectedCallback();
    clearInterval(this.timerId);
  }
}

customElements.define('lit-clock', LitClock);

7. Dikkat çekici girişler

Bu bölümde, React Hook kavramlarını Lit'e nasıl çevireceğinizi öğreneceksiniz.

React kancalarıyla ilgili kavramlar

React kancaları, işlev bileşenlerinin duruma "bağlanmasını" sağlar. Bunun çeşitli avantajları vardır.

  • Durumlu mantığın yeniden kullanılmasını kolaylaştırır.
  • Bir bileşeni daha küçük işlevlere ayırmaya yardımcı olma

Ayrıca, işlev tabanlı bileşenlere odaklanma, React'in sınıf tabanlı söz dizimiyle ilgili belirli sorunları (ör. aşağıdakiler) ele almıştır:

  • props'yı constructor'den super'ye aktarmak
  • constructor
      içindeki özelliklerin düzensiz başlatılması
    • Bu, o sırada React ekibi tarafından belirtilen bir nedendi ancak ES2019 ile çözüldü.
  • this artık bileşene başvurmadığı için oluşan sorunlar

Lit'teki React kancası kavramları

Bileşenler ve Özellikler bölümünde belirtildiği gibi Lit, bir işlevden özel öğeler oluşturmanın bir yolunu sunmaz ancak LitElement, React sınıf bileşenleriyle ilgili ana sorunların çoğunu ele alır. Örneğin:

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

Lit bu sorunları nasıl ele alıyor?

  • constructor işlevi bağımsız değişken almaz
  • Tüm @event bağlamaları this ile otomatik olarak bağlanır.
  • this vakaların büyük çoğunluğunda özel öğenin referansını ifade eder.
  • Sınıf özellikleri artık sınıf üyeleri olarak örneklenebilir. Bu, oluşturucu tabanlı uygulamaları temizler.

Reaktif denetleyiciler

Hooks'un temelindeki kavramlar, Lit'te reaktif denetleyiciler olarak bulunur. Tepkisel denetleyici kalıpları, durum bilgisi içeren mantığın paylaşılmasına, bileşenlerin daha küçük ve modüler parçalara ayrılmasına ve bir öğenin güncelleme yaşam döngüsüne bağlanmasına olanak tanır.

Tepkisel denetleyici, LitElement gibi bir denetleyici ana makinesinin güncelleme yaşam döngüsüne bağlanabilen bir nesne arayüzüdür.

ReactiveController ve reactiveControllerHost yaşam döngüsü:

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

Reaktif bir denetleyici oluşturup addController ile bir ana öğeye eklediğinizde, denetleyicinin yaşam döngüsü ana öğenin yaşam döngüsüyle birlikte çağrılır. Örneğin, Durum ve Yaşam Döngüsü bölümündeki saat örneğini hatırlayın:

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

Yukarıdaki örnekte, aşağıdakileri yapan basit bir saat vardır:

  • "Hello World! Şu an saat" deyip saati gösterir.
  • Saat her saniye güncellenir.
  • Söküldüğünde, tik işaretini çağıran aralığı temizler.

Bileşen iskeleti oluşturma

Öncelikle bileşen sınıfı bildirimini yaparak başlayın ve render işlevini ekleyin.

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

Denetleyiciyi oluşturma

Şimdi clock.ts'ya geçin ve ClockController için bir sınıf oluşturup constructor'ı ayarlayın:

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

Reaktif denetleyici, ReactiveController arayüzünü paylaştığı sürece herhangi bir şekilde oluşturulabilir. Ancak Lit ekibi, çoğu temel durumda denetleyiciyi başlatmak için gereken diğer özelliklerin yanı sıra ReactiveControllerHost arayüzünü de alabilen bir constructor ile sınıf kullanmayı tercih eder.

Şimdi React yaşam döngüsü geri çağırmalarını denetleyici geri çağırmalarına çevirmeniz gerekiyor. Kısaca:

  • componentDidMount
    • LitElement'in connectedCallback
    • Kumandanın hostConnected
  • ComponentWillUnmount
    • LitElement'in disconnectedCallback
    • Denetleyicinin hostDisconnected

React yaşam döngüsünü Lit yaşam döngüsüne çevirme hakkında daha fazla bilgi için State & Lifecycle (Durum ve Yaşam Döngüsü) bölümüne bakın.

Ardından, hostConnected geri çağırma ve tick yöntemlerini uygulayın ve hostDisconnected içindeki aralığı Durum ve Yaşam Döngüsü bölümündeki örnekte olduğu gibi temizleyin.

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

Kumandayı kullanma

Saat denetleyicisini kullanmak için denetleyiciyi içe aktarın ve bileşeni index.ts veya index.js'da güncelleyin.

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

Denetleyiciyi kullanmak için denetleyici ana makinesine (<my-element> bileşeni) referans ileterek denetleyiciyi örneklendirmeniz ve ardından render yönteminde denetleyiciyi kullanmanız gerekir.

Kumandada yeniden oluşturmayı tetikleme

Saatin gösterildiğini ancak güncellenmediğini fark edin. Bunun nedeni, kumandanın tarihi her saniye ayarlamasına rağmen ana makinenin güncellemeyi yapmamasıdır. Bunun nedeni, date artık bileşende değil, ClockController sınıfında değişiyor olmasıdır. Bu, kumandada date ayarlandıktan sonra ana makineye güncelleme yaşam döngüsünü host.requestUpdate() ile çalıştırması gerektiğinin söylenmesi gerektiği anlamına gelir.

// Lit (TS & JS) - clock.ts / clock.js
private tick() {
  this.date = new Date();
  this.host.requestUpdate();
}

Artık saat işlemeye başlamalıdır.

Kancalarla ilgili yaygın kullanım alanlarının daha ayrıntılı bir karşılaştırması için lütfen İleri Düzey Konular - Kancalar bölümüne bakın.

8. Çocuklar

Bu bölümde, Lit'te çocukları yönetmek için yuvaları nasıl kullanacağınızı öğreneceksiniz.

Slotlar ve Çocuklar

Yuvalar, bileşenleri iç içe yerleştirmenize olanak tanıyarak birleştirme işlemini mümkün kılar.

React'te alt öğeler, props aracılığıyla devralınır. Varsayılan yuva props.children'dır ve render işlevi, varsayılan yuvanın konumunu tanımlar. Örneğin:

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

props.children öğelerinin HTML öğeleri değil, React bileşenleri olduğunu unutmayın.

Lit'te çocuklar, oluşturma işlevinde yuva öğeleriyle oluşturulur. Çocukların, React'te olduğu gibi aynı şekilde devralınmadığını unutmayın. Lit'te çocuklar, yuvalara eklenmiş HTMLElements'tir. Bu ek, Projeksiyon olarak adlandırılır.

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

Birden fazla slot

React'te birden fazla yuva eklemek, temelde daha fazla prop devralmakla aynıdır.

const MyArticle = (props) => {
  return (
    <article>
      <header>
        {props.headerChildren}
      </header>
      <section>
        {props.sectionChildren}
      </section>
    </article>
  );
};

Benzer şekilde, daha fazla <slot> öğesi eklemek Lit'te daha fazla yer oluşturur. name özelliğiyle birden fazla yuva tanımlandı: <slot name="slot-name">. Bu sayede çocuklar, hangi aralığa atanacaklarını belirleyebilir.

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

Varsayılan Slot İçeriği

Yuvalar, bu yuvaya yansıtılan düğüm olmadığında alt ağaçlarını gösterir. Düğümler bir yuvaya yansıtıldığında, bu yuva alt ağacını göstermez ve bunun yerine yansıtılan düğümleri gösterir.

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

Çocukları aralıklara atama

React'te alt öğeler, bir bileşenin özellikleri aracılığıyla yuvalara atanır. Aşağıdaki örnekte, React öğeleri headerChildren ve sectionChildren özelliklerine iletilir.

const MyNewsArticle = () => {
 return (
   <MyArticle
     headerChildren={<h3>Extry, Extry! Read all about it!</h3>}
     sectionChildren={<p>Children are props in React!</p>}
   />
 );
};

Lit'te çocuklar, slot özelliği kullanılarak yuvalara atanır.

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

Varsayılan bir yuva (ör. <slot>) yoksa ve özel öğenin alt öğelerinin slot özelliğiyle (ör. <div slot="foo">) eşleşen bir name özelliği olan bir yuva (ör. <slot name="foo">) yoksa bu düğüm yansıtılmaz ve görüntülenmez.

9. Refs

Geliştiricilerin zaman zaman bir HTMLElement'in API'sine erişmesi gerekebilir.

Bu bölümde, Lit'te öğe referanslarını nasıl edineceğinizi öğreneceksiniz.

React Referansları

Bir React bileşeni, çağrıldığında sanal DOM oluşturan bir dizi işlev çağrısına dönüştürülür. Bu sanal DOM, ReactDOM tarafından yorumlanır ve HTMLElements öğelerini oluşturur.

React'te Refs, oluşturulan bir HTMLElement'i içerecek şekilde bellekte ayrılan alandır.

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

Yukarıdaki örnekte, React bileşeni şunları yapar:

  • Boş bir metin girişi ve metin içeren bir düğme oluşturun.
  • Düğme tıklandığında girişe odaklanma

İlk oluşturma işleminden sonra React, inputRef.current öğesini ref özelliği aracılığıyla oluşturulan HTMLInputElement olarak ayarlar.

@query ile "Referanslar"ı aydınlatma

Lit, tarayıcıya yakın bir şekilde çalışır ve yerel tarayıcı özelliklerinin üzerinde çok ince bir soyutlama katmanı oluşturur.

Lit'teki refs öğesinin React'teki karşılığı, @query ve @queryAll dekoratörleri tarafından döndürülen HTMLElement'dir.

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

Yukarıdaki örnekte, Lit bileşeni şunları yapar:

  • @query dekoratörünü kullanarak MyElement üzerinde bir özellik tanımlar (HTMLInputElement için alıcı oluşturur).
  • onButtonClick adlı bir tıklama etkinliği geri çağırma işlevini bildirir ve ekler.
  • Düğme tıklama işleminde girişe odaklanır.

JavaScript'te @query ve @queryAll dekoratörleri sırasıyla querySelector ve querySelectorAll işlemlerini gerçekleştirir. Bu, @query('input') inputEl!: HTMLInputElement; öğesinin JavaScript eşdeğeridir.

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

Lit bileşeni, render yönteminin şablonunu my-element'nin köküne uyguladıktan sonra @query dekoratörü, inputEl'nin oluşturma kökünde bulunan ilk input öğesini döndürmesine olanak tanır. @query belirtilen öğeyi bulamazsa null değerini döndürür.

Render kökünde birden fazla input öğesi varsa @queryAll, düğümlerin listesini döndürür.

10. Aracılık durumu

Bu bölümde, Lit'teki bileşenler arasında durumu nasıl uyumlulaştıracağınızı öğreneceksiniz.

Yeniden Kullanılabilir Bileşenler

React, yukarıdan aşağıya veri akışıyla işlevsel oluşturma ardışık düzenlerini taklit eder. Ebeveynler, çocuklara sahne malzemeleri aracılığıyla durum bilgisi verir. Çocuklar, sahne malzemelerinde bulunan geri aramalar aracılığıyla ebeveynleriyle iletişim kurar.

const CounterButton = (props) => {
  const label = props.step < 0
    ? `- ${-1 * props.step}`
    : `+ ${props.step}`;


  return (
    <button
      onClick={() =>
        props.addToCounter(props.step)}>{label}</button>
  );
};

Yukarıdaki örnekte bir React bileşeni şunları yapar:

  • props.step değerine göre bir etiket oluşturur.
  • Etiketi +step veya -step olan bir düğme oluşturur.
  • Tıklama sırasında props.addToCounter işlevini props.step bağımsız değişkeniyle çağırarak üst bileşeni günceller.

Geri çağırmaları Lit'te iletmek mümkün olsa da geleneksel kalıplar farklıdır. Yukarıdaki örnekteki React bileşeni, aşağıdaki örnekte Lit bileşeni olarak yazılabilir:

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

Yukarıdaki örnekte, Lit Bileşeni aşağıdakileri yapar:

  • Reaktif mülk oluşturun step
  • Tıklama işleminde öğenin step değerini taşıyan update-counter adlı özel bir etkinlik gönderme

Tarayıcı etkinlikleri, alt öğelerden üst öğelere doğru yayılır. Etkinlikler, çocukların etkileşim etkinliklerini ve durum değişikliklerini yayınlamasına olanak tanır. React temelde durumu ters yönde iletir. Bu nedenle, React bileşenlerinin etkinlikleri Lit bileşenleriyle aynı şekilde göndermesi ve dinlemesi yaygın değildir.

Durum bilgili bileşenler

React'te durumu yönetmek için kancalar kullanmak yaygın bir uygulamadır. MyCounter bileşeni, CounterButton bileşeni yeniden kullanılarak oluşturulabilir. addToCounter öğesinin, CounterButton öğesinin her iki örneğine de nasıl iletildiğine dikkat edin.

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

Yukarıdaki örnekte şunlar yapılmaktadır:

  • count durumu oluşturur.
  • Bir countdurumuna sayı ekleyen bir geri arama oluşturur.
  • CounterButton, her tıklamada count öğesini step ile güncellemek için addToCounter kullanır.

MyCounter benzer bir uygulama Lit'te de yapılabilir. addToCounter değerinin counter-button değerine aktarılmadığına dikkat edin. Bunun yerine geri çağırma, üst öğedeki @update-counter etkinliğine etkinlik işleyici olarak bağlanır.

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

Yukarıdaki örnekte şunlar yapılmaktadır:

  • Değer değiştiğinde bileşeni güncelleyecek count adlı reaktif bir özellik oluşturur.
  • addToCounter geri çağırma işlevini @update-counter etkinlik işleyicisine bağlar.
  • count, update-counter etkinliğinin detail.step bölümünde bulunan değer eklenerek güncellenir.
  • step özelliği aracılığıyla counter-button'nın step değerini ayarlar.

Lit'te değişiklikleri üst öğelerden alt öğelere yayınlamak için reaktif özellikler kullanmak daha yaygındır. Benzer şekilde, ayrıntıları alttan yukarıya doğru aktarmak için tarayıcının etkinlik sistemini kullanmak da iyi bir uygulamadır.

Bu yaklaşım, en iyi uygulamalara uygundur ve Lit'in web bileşenleri için platformlar arası destek sağlama hedefine bağlıdır.

11. Stil

Bu bölümde Lit'te stil oluşturma hakkında bilgi edineceksiniz.

Stil

Lit, öğeleri stilize etmek için birden fazla yolun yanı sıra yerleşik bir çözüm sunar.

Satır içi stiller

Lit, satır içi stilleri ve bunlara bağlamayı destekler.

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

Yukarıdaki örnekte, her biri satır içi stile sahip 2 başlık vardır.

Şimdi border-color.js'dan turuncu metne bir kenarlık içe aktarıp bağlayın:

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

...

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

Stil dizesini her seferinde hesaplamak biraz can sıkıcı olabilir. Bu nedenle Lit, bu konuda yardımcı olacak bir yönerge sunar.

styleMap

styleMap Yönergesi, satır içi stilleri ayarlamak için JavaScript'in kullanılmasını kolaylaştırır. Örneğin:

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

Yukarıdaki örnekte şunlar yapılmaktadır:

  • Kenarlıklı ve renk seçicili bir h1 gösterir.
  • border-color değerini renk seçiciden alınan değerle değiştirir.

Ayrıca, h1 stillerini ayarlamak için kullanılan styleMap vardır. styleMap, React'in style özellik bağlama söz dizimine benzer bir söz dizimine sahiptir.

CSSResult

Bileşenleri stilize etmek için önerilen yöntem, css etiketli şablon değişmezini kullanmaktır.

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

Yukarıdaki örnekte şunlar yapılmaktadır:

  • Bağlama içeren bir CSS etiketli şablon değişmezi bildirir.
  • Kimlikleri olan iki h1 öğesinin renklerini ayarlar.

css şablon etiketini kullanmanın avantajları:

  • Sınıf başına bir kez ayrıştırılır, örnek başına ayrıştırılmaz.
  • Modülün yeniden kullanılabilirliği göz önünde bulundurularak uygulanır.
  • Stilleri kolayca kendi dosyalarına ayırabilir.
  • CSS özel özellikleri polyfill'iyle uyumludur.

Ayrıca, index.html içindeki <style> etiketine dikkat edin:

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

Lit, bileşenlerinizin stillerini kökleriyle sınırlandırır. Bu, stillerin sızmayacağı anlamına gelir. Lit ekibi, stilleri bileşenlere aktarmak için CSS özel özelliklerinin kullanılmasını önerir. Bu özellikler, Lit stil kapsamına girebilir.

Stil Etiketleri

Şablonlarınıza <style> etiketlerini satır içi olarak da ekleyebilirsiniz. Tarayıcı bu stil etiketlerinin tekilleştirmesini yapar ancak bunları şablonlarınıza yerleştirdiğinizde, css etiketli şablonda olduğu gibi sınıf başına değil, bileşen örneği başına ayrıştırılırlar. Ayrıca, tarayıcıda CSSResult öğelerinin yinelenen kopyalarını kaldırma işlemi çok daha hızlıdır.

Şablonunuzda <link rel="stylesheet"> kullanmak da stiller için bir olasılıktır ancak bu da, stillendirilmemiş içeriklerin ilk yanıp sönmesine (FOUC) neden olabileceğinden önerilmez.

12. İleri Düzey Konular (isteğe bağlı)

JSX ve Şablon Oluşturma

Lit ve Sanal DOM

Lit-html, her bir düğümü karşılaştıran geleneksel bir sanal DOM içermez. Bunun yerine, ES2015'in etiketli şablon değişmez spesifikasyonuna özgü performans özelliklerinden yararlanır. Etiketli şablon değişmezleri, kendilerine eklenmiş etiket işlevlerine sahip şablon değişmez dizelerdir.

Şablon değişmezine bir örnek:

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

Etiketlenmiş şablon değişmezine bir örnek:

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

Yukarıdaki örnekte, etiket tag işlevidir ve f işlevi, etiketlenmiş bir şablon değişmez değerinin çağrılmasını döndürür.

Lit'teki performansın büyük bir kısmı, etiket işlevine iletilen dize dizilerinin aynı işaretçiye sahip olmasından kaynaklanır (ikinci console.log'da gösterildiği gibi). Tarayıcı, aynı şablon değişmezini (yani AST'deki aynı konumda) kullandığı için her etiket işlevi çağrısında yeni bir strings dizisi oluşturmaz. Bu nedenle, Lit'in bağlama, ayrıştırma ve şablon önbelleğe alma işlemleri, çalışma zamanı farklılaştırma yükü olmadan bu özelliklerden yararlanabilir.

Etiketlenmiş şablon değişmezlerinin bu yerleşik tarayıcı davranışı, Lit'e önemli bir performans avantajı sağlar. Geleneksel sanal DOM'ların çoğu işlerinin büyük bir kısmını JavaScript'te yapar. Ancak etiketlenmiş şablon değişmezleri, farklılıkların çoğunu tarayıcının C++'ında yapar.

React veya Preact ile HTML etiketli şablon değişmezlerini kullanmaya başlamak istiyorsanız Lit ekibi htm kitaplığını önerir.

Ancak Google Codelabs sitesinde ve çeşitli online kod düzenleyicilerde olduğu gibi, etiketlenmiş şablon hazır değer söz dizimi vurgulamasının çok yaygın olmadığını fark edeceksiniz. Atom ve GitHub'ın kod bloğu vurgulayıcısı gibi bazı IDE'ler ve metin düzenleyiciler bunları varsayılan olarak destekler. Lit ekibi, VS Code eklentisi olan lit-plugin gibi projeleri sürdürmek için toplulukla da çok yakın bir şekilde çalışır. Bu eklenti, Lit projelerinize söz dizimi vurgulama, tür kontrolü ve IntelliSense özellikleri ekler.

Lit ve JSX + React DOM

JSX, tarayıcıda çalışmaz. Bunun yerine, JSX'i JavaScript işlev çağrılarına dönüştürmek için bir ön işlemci (genellikle Babel aracılığıyla) kullanır.

Örneğin, Babel şu ifadeyi dönüştürür:

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

şuna dönüştürülür:

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

React DOM, React çıkışını alıp gerçek DOM'a (özellikler, nitelikler, etkinlik işleyicileri vb.) çevirir.

Lit-html, tarayıcıda dönüştürme veya ön işlemci olmadan çalıştırılabilen etiketlenmiş şablon değişmezleri kullanır. Bu nedenle, Lit'i kullanmaya başlamak için yalnızca bir HTML dosyası, bir ES modülü komut dosyası ve bir sunucuya ihtiyacınız vardır. Tamamen tarayıcıda çalıştırılabilen bir komut dosyası aşağıda verilmiştir:

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

Ayrıca, Lit'in şablon sistemi olan lit-html, geleneksel bir sanal DOM yerine doğrudan DOM API'yi kullandığından Lit 2'nin boyutu, React (2,8 KB) + react-dom'un (39,4 KB) 40 KB'lık küçültülmüş ve gzip'lenmiş boyutuna kıyasla 5 KB'tan daha küçüktür.

Etkinlikler

React, yapay etkinlik sistemi kullanır. Bu nedenle, react-dom her bileşende kullanılacak her etkinliği tanımlamalı ve her düğüm türü için camelCase etkinlik işleyici eşdeğeri sağlamalıdır. Sonuç olarak, JSX'te özel bir etkinlik için etkinlik işleyici tanımlama yöntemi yoktur ve geliştiricilerin ref kullanıp ardından zorunlu olarak bir işleyici uygulaması gerekir. Bu durum, React'i dikkate almayan kitaplıkları entegre ederken yetersiz bir geliştirici deneyimine yol açar ve React'e özgü bir sarmalayıcı yazılmasına neden olur.

Lit-html, doğrudan DOM'a erişir ve yerel etkinlikleri kullanır. Bu nedenle, etkinlik işleyicileri eklemek @event-name=${eventNameListener} kadar kolaydır. Bu sayede, etkinlik dinleyicileri ekleme ve etkinlikleri tetikleme için daha az çalışma zamanı ayrıştırması yapılır.

Bileşenler ve Öğeler

React bileşenleri ve özel öğeler

LitElement, bileşenlerini paketlemek için özel öğeler kullanır. Özel öğeler, bileşenleştirme söz konusu olduğunda React bileşenleri arasında bazı ödünler verilmesine neden olur (durum ve yaşam döngüsü hakkında daha fazla bilgiyi Durum ve Yaşam Döngüsü bölümünde bulabilirsiniz).

Özel öğelerin bileşen sistemi olarak bazı avantajları:

  • Tarayıcıya özeldir ve herhangi bir araç gerektirmez.
  • innerHTML ve document.createElement'den querySelector'ye kadar her tarayıcı API'sine uyum sağlar.
  • Genellikle çerçeveler arasında kullanılabilir.
  • customElements.define ile geç kaydedilebilir ve DOM "hydrate" edilebilir.

Özel öğelerin React bileşenlerine kıyasla bazı dezavantajları:

  • Sınıf tanımlamadan özel öğe oluşturulamaz (bu nedenle JSX benzeri işlevsel bileşenler yoktur).
  • Kapatma etiketi
      içermelidir.
    • Not: Tarayıcı satıcıları, geliştiricilerin rahatlığına rağmen kendi kendini kapatan etiket spesifikasyonundan pişmanlık duymaktadır. Bu nedenle, daha yeni spesifikasyonlar kendi kendini kapatan etiketleri içermez.
  • DOM ağacına, düzen sorunlarına neden olabilecek ekstra bir düğüm ekler.
  • JavaScript aracılığıyla kaydedilmelidir.

Lit, tarayıcıya yerleştirilmiş özel öğeler olduğu için özel bir öğe sistemi yerine özel öğeleri tercih etti. Lit ekibi, çerçeveler arası avantajların bileşen soyutlama katmanının sağladığı avantajlardan daha fazla olduğuna inanıyor. Hatta Lit ekibinin lit-ssr alanındaki çalışmaları, JavaScript kaydıyla ilgili temel sorunların üstesinden gelmeyi başardı. Ayrıca GitHub gibi bazı şirketler, sayfaları isteğe bağlı özelliklerle kademeli olarak geliştirmek için özel öğe tembel kaydından yararlanır.

Özel öğelere veri aktarma

Özel öğelerle ilgili yaygın bir yanlış kanı, verilerin yalnızca dizeler olarak iletilebileceğidir. Bu yanlış anlaşılma, büyük olasılıkla öğe özelliklerinin yalnızca dizeler olarak yazılabilmesinden kaynaklanmaktadır. Lit'in dize özelliklerini tanımlı türlerine dönüştürdüğü doğru olsa da özel öğeler, karmaşık verileri özellik olarak da kabul edebilir.

Örneğin, aşağıdaki LitElement tanımı verildiğinde:

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

Bir özelliğin dize değerini number değerine dönüştüren temel bir reaktif özellik num tanımlanır. Ardından, Lit'in özellik işlemeyi devre dışı bırakan attribute:false ile karmaşık bir veri yapısı oluşturulur.

Bu özel öğeye veri iletmek için aşağıdaki yöntemi kullanın:

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

Durum ve Yaşam Döngüsü

Diğer React Yaşam Döngüsü Geri Çağırma Yöntemleri

static getDerivedStateFromProps

Lit'te props ve state aynı sınıf özellikleri olduğundan eşdeğeri yoktur.

shouldComponentUpdate

  • Eşdeğer birim shouldUpdate
  • React'in aksine ilk oluşturma işleminde çağrılır.
  • React'in shouldComponentUpdate işlevine benzer

getSnapshotBeforeUpdate

Lit'te getSnapshotBeforeUpdate, hem update hem de willUpdate ile benzerdir.

willUpdate

  • update tarihinden önce arandı
  • getSnapshotBeforeUpdate'dan farklı olarak willUpdate, render'dan önce çağrılır.
  • willUpdate içindeki tepkisel özelliklerde yapılan değişiklikler, güncelleme döngüsünü yeniden tetiklemez.
  • Diğer özelliklere bağlı olan ve güncelleme sürecinin geri kalanında kullanılan özellik değerlerini hesaplamak için iyi bir yerdir.
  • Bu yöntem, SSR'de sunucuda çağrıldığından burada DOM'a erişilmesi önerilmez.

update

  • willUpdate tarihinden sonra arandı
  • getSnapshotBeforeUpdate'dan farklı olarak update, render'dan önce çağrılır.
  • update içindeki tepkisel özelliklerde yapılan değişiklikler, super.update çağrılmadan önce değiştirilirse güncelleme döngüsünü yeniden tetiklemez.
  • Oluşturulan çıktı DOM'a aktarılmadan önce bileşeni çevreleyen DOM'dan bilgi almak için iyi bir yerdir.
  • Bu yöntem, SSR'de sunucuda çağrılmaz.

Diğer Lit Yaşam Döngüsü Geri Çağırma Yöntemleri

React'te benzeri olmadığı için önceki bölümde bahsedilmeyen birkaç yaşam döngüsü geri çağırması vardır. Bunları şöyle sıralayabiliriz:

attributeChangedCallback

Öğenin observedAttributes özelliklerinden biri değiştiğinde çağrılır. Hem observedAttributes hem de attributeChangedCallback, özel öğeler spesifikasyonunun bir parçasıdır ve Lit öğeleri için bir özellik API'si sağlamak üzere Lit tarafından arka planda uygulanır.

adoptedCallback

Bileşen yeni bir dokümana taşındığında (ör. HTMLTemplateElement öğesinin documentFragment öğesinden ana document öğesine) çağrılır. Bu geri çağırma, özel öğeler spesifikasyonunun da bir parçasıdır ve yalnızca bileşen belgeleri değiştirdiğinde ileri düzey kullanım alanları için kullanılmalıdır.

Diğer yaşam döngüsü yöntemleri ve özellikleri

Bu yöntemler ve özellikler, yaşam döngüsü sürecini değiştirmenize yardımcı olmak için çağırabileceğiniz, geçersiz kılabileceğiniz veya bekleyebileceğiniz sınıf üyeleridir.

updateComplete

Bu, güncelleme ve oluşturma yaşam döngüleri eşzamansız olduğundan öğe güncellemeyi tamamladığında çözülen bir Promise'dır. Örnek:

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

getUpdateComplete

Bu, updateComplete öğesinin ne zaman çözümleneceğini özelleştirmek için geçersiz kılınması gereken bir yöntemdir. Bu durum, bir bileşen alt bileşeni oluşturduğunda ve oluşturma döngülerinin senkronize olması gerektiğinde yaygındır. Örneğin:

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

performUpdate

Bu yöntem, güncelleme yaşam döngüsü geri çağırma yöntemlerini çağıran yöntemdir. Bu, genellikle senkronize güncellemenin yapılması gereken veya özel planlamanın kullanıldığı nadir durumlar dışında gerekli değildir.

hasUpdated

Bu özellik, bileşen en az bir kez güncellendiyse true olur.

isConnected

Özel öğeler spesifikasyonunun bir parçası olan bu özellik, öğe şu anda ana belge ağacına eklenmişse true olur.

Lit Güncelleme Yaşam Döngüsü Görselleştirmesi

Güncelleme yaşam döngüsü 3 bölümden oluşur:

  • Güncelleme öncesi
  • Güncelle
  • Güncelleme sonrası

Güncelleme Öncesi

Geri çağırma adlarına sahip düğümlerin yönlendirilmiş döngüsüz grafiği. constructor to requestUpdate. @property, Property Setter&#39;a. attributeChangedCallback, Property Setter&#39;a. Property Setter, hasChanged&#39;e. hasChanged, requestUpdate&#39;e. requestUpdate, sonraki yaşam döngüsü grafiği olan update&#39;e işaret eder.

requestUpdate tarihinden sonra planlanmış bir güncelleme bekleniyor.

Güncelle

Geri çağırma adlarına sahip düğümlerin yönlü düz ağacı. Güncelleme öncesi yaşam döngüsü noktalarının önceki resmindeki ok, performUpdate&#39;e işaret ediyor. performUpdate, shouldUpdate&#39;e işaret ediyor. shouldUpdate, hem &quot;yanlışsa güncellemeyi tamamla&quot; hem de willUpdate&#39;e işaret ediyor. willUpdate, update&#39;e işaret ediyor. update, hem render&#39;a hem de güncelleme sonrası yaşam döngüsü grafiğinin bir sonraki noktasına işaret ediyor. render da güncelleme sonrası yaşam döngüsü grafiğinin bir sonraki noktasına işaret ediyor.

Güncelleme sonrası

Geri çağırma adlarına sahip düğümlerin yönlü düz ağacı. Güncelleme yaşam döngüsünün önceki görüntüsündeki ok, firstUpdated&#39;ı, firstUpdated ise updated&#39;ı gösteriyor. updated, updateComplete&#39;i gösteriyor.

Dikkat çekici girişler

Neden dikkat çekici girişler?

Hooks, durum gerektiren basit işlev bileşeni kullanım alanları için React'e dahil edildi. Kancalı fonksiyon bileşenleri, birçok basit durumda sınıf bileşeni karşılıklarından çok daha basit ve okunabilir olur. Ancak, eşzamansız durum güncellemeleri sunmanın yanı sıra kancalar veya efektler arasında veri aktarırken kanca kalıbı yeterli olmayabilir ve reaktif denetleyiciler gibi sınıfa dayalı bir çözüm daha iyi sonuç verebilir.

API isteği kancaları ve denetleyicileri

Bir API'den veri isteyen bir kanca yazmak yaygın bir uygulamadır. Örneğin, aşağıdakileri yapan bu React işlev bileşenini ele alalım:

  • index.tsx
    • Metni oluşturur
    • useAPI adlı kullanıcının yanıtını oluşturur.
      • Kullanıcı kimliği + kullanıcı adı
      • Hata Mesajı
        • 11. kullanıcıya ulaşıldığında 404 hatası (tasarım gereği)
        • API getirme işlemi iptal edilirse iptal hatası
      • Yükleniyor Mesajı
    • İşlem düğmesi oluşturur.
      • Sonraki kullanıcı: Sonraki kullanıcı için API'yi getirir.
      • İptal: API getirme işlemini durdurur ve bir hata gösterir.
  • useApi.tsx
    • useApi özel kancasını tanımlar.
    • Bir API'den kullanıcı nesnesini eşzamansız olarak getirme
    • Yayınlar:
      • Kullanıcı adı
      • Getirme işleminin yüklenip yüklenmediği
      • Hata mesajları
      • Getirme işlemini iptal etmek için geri çağırma
    • Sökülürse devam eden getirme işlemlerini durdurur.

Lit + Reactive Controller uygulamasını burada bulabilirsiniz.

Çıkarımlar:

  • Reactive Controllers, en çok özel kancalara benzer.
  • Geri çağırma işlevleri ve efektler arasında oluşturulmayan verileri aktarma
    • React, useRef ile useEffect ve useCallback arasında veri aktarmak için kullanılır.
    • Lit, özel bir sınıf özelliği kullanır
    • React, aslında özel bir sınıf özelliğinin davranışını taklit eder.

Ayrıca, kancalarla React işlev bileşeni söz dizimini ancak Lit'in derlemesiz ortamını kullanmak istiyorsanız Lit ekibi Haunted kitaplığını kullanmanızı önerir.

Çocuklar

Varsayılan yuva

HTML öğelerine slot özelliği verilmediğinde, bu öğeler varsayılan adsız yuvaya atanır. Aşağıdaki örnekte, MyApp adlı bir yuvaya bir paragraf yerleştirir. Diğer paragraf varsayılan olarak adsız alana yerleştirilir.

@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 Güncellemeleri

Yuva alt öğelerinin yapısı değiştiğinde slotchange etkinliği tetiklenir. Bir Lit bileşeni, bir slotchange etkinliğine etkinlik dinleyici bağlayabilir. Aşağıdaki örnekte, shadowRoot içinde bulunan ilk yuvanın assignedNodes değeri slotchange tarihinde konsola kaydedilir.

@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

Referans oluşturma

Hem Lit hem de React, render işlevleri çağrıldıktan sonra bir HTMLElement'e referans verir. Ancak React ve Lit'in, daha sonra Lit @query dekoratörü veya React Referansı aracılığıyla döndürülen DOM'u nasıl oluşturduğunu incelemek faydalı olacaktır.

React, HTMLElements değil React Components oluşturan işlevsel bir işlem hattıdır. Bir Ref, bir HTMLElement oluşturulmadan önce bildirildiği için bellekte bir alan ayrılır. Bu nedenle, gerçek DOM öğesi henüz oluşturulmadığı (veya oluşturulmadığı) için null, Ref'in başlangıç değeri olarak görünür.useRef(null)

ReactDOM, bir React bileşenini HTMLElement'e dönüştürdükten sonra ReactComponent'te ref adlı bir özellik arar. Varsa ReactDOM, HTMLElement'ın referansını ref.current içine yerleştirir.

LitElement, arka planda Template Element oluşturmak için lit-html'deki html şablon etiketi işlevini kullanır. LitElement, oluşturma işleminden sonra şablonun içeriğini özel bir öğenin gölge DOM'una damgalar. Gölge DOM, bir gölge kökü tarafından kapsanan, kapsamlı bir DOM ağacıdır. @query dekoratörü, daha sonra mülk için bir alıcı oluşturur. Bu alıcı, kapsamlı kökte temelde bir this.shadowRoot.querySelector işlemi gerçekleştirir.

Birden Çok Öğeyi Sorgulama

Aşağıdaki örnekte, @queryAll dekoratörü, gölge kökündeki iki paragrafı NodeList olarak döndürür.

@customElement("my-element")
export class MyElement extends LitElement {
  @queryAll('p')
  paragraphs!: NodeList;

  render() {
    return html`
      <p>Hello, world!</p>
      <p>How are you?</p>
   `;
  }
}

Temel olarak @queryAll, paragraphs için this.shadowRoot.querySelectorAll() sonuçlarını döndüren bir alıcı oluşturur. JavaScript'te, aynı amaçla bir getter tanımlanabilir:

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

Sorguyu Değiştiren Öğeler

@queryAsync dekoratörü, başka bir öğe özelliğinin durumuna göre değişebilen bir düğümü işlemek için daha uygundur.

Aşağıdaki örnekte, @queryAsync ilk paragraf öğesini bulur. Ancak bir paragraf öğesi yalnızca renderParagraph rastgele bir tek sayı oluşturduğunda oluşturulur. @queryAsync yönergesi, ilk paragraf kullanılabilir olduğunda çözümlenecek bir söz döndürür.

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

Aracılık durumu

React'te durumun aracılığını React'in kendisi yaptığı için geri çağırma işlevlerinin kullanılması gelenekseldir. React, öğeler tarafından sağlanan duruma güvenmemek için elinden geleni yapar. DOM, yalnızca oluşturma sürecinin bir etkisidir.

Harici Durum

Lit ile birlikte Redux, MobX veya başka bir durum yönetimi kitaplığı kullanabilirsiniz.

Lit bileşenleri tarayıcı kapsamında oluşturulur. Bu nedenle, tarayıcı kapsamı içinde de bulunan tüm kitaplıklar Lit tarafından kullanılabilir. Lit'teki mevcut durum yönetimi sistemlerinden yararlanmak için birçok harika kitaplık oluşturuldu.

Vaadin'in, Lit bileşeninde Redux'tan nasıl yararlanılacağını açıklayan bir serisini burada bulabilirsiniz.

Büyük ölçekli bir sitenin Lit'te MobX'ten nasıl yararlanabileceğini görmek için Adobe'un lit-mobx'ine göz atın.

Ayrıca, geliştiricilerin GraphQL'i web bileşenlerine nasıl dahil ettiğini görmek için Apollo Elements'e göz atın.

Lit, yerel tarayıcı özellikleriyle çalışır ve tarayıcı kapsamındaki çoğu durum yönetimi çözümü Lit bileşeninde kullanılabilir.

Stil

Shadow DOM

Lit, stilleri ve DOM'u bir özel öğe içinde doğal olarak kapsamak için gölge DOM'u kullanır. Gölge kökler, ana belge ağacından ayrı bir gölge ağacı oluşturur. Bu, çoğu stilin bu dokümanla sınırlı olduğu anlamına gelir. Renk ve yazı tipiyle ilgili diğer stiller gibi bazı stiller sızabilir.

Shadow DOM, CSS spesifikasyonuna yeni kavramlar ve seçiciler de ekler:

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

Stilleri paylaşma

Lit, css şablon etiketleri aracılığıyla CSSTemplateResults biçiminde bileşenler arasında stil paylaşmayı kolaylaştırır. Örneğin:

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

Tema oluşturma

Geleneksel temalandırma, genellikle yukarıdan aşağıya stil etiketi yaklaşımları olduğundan gölge kökler bu konuda biraz zorluk çıkarır. Gölge DOM kullanan Web Bileşenleri ile temalandırmayı ele almanın geleneksel yolu, CSS Özel Özellikleri aracılığıyla bir stil API'si kullanıma sunmaktır. Örneğin, Materyal Tasarım'da kullanılan bir desen:

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

Kullanıcı daha sonra özel özellik değerleri uygulayarak sitenin temasını değiştirir:

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

Yukarıdan aşağıya tema oluşturma zorunluysa ve stilleri kullanıma sunamıyorsanız createRenderRoot'yı geçersiz kılarak this döndürecek şekilde ayarlayarak Shadow DOM'u devre dışı bırakabilirsiniz. Bu durumda, bileşenlerinizin şablonu, özel öğeye eklenmiş bir gölge köküne değil, özel öğenin kendisine oluşturulur. Bu durumda stil kapsülleme, DOM kapsülleme ve yuvaları kaybedersiniz.

Üretim

IE 11

IE 11 gibi eski tarayıcıları desteklemeniz gerekiyorsa yaklaşık 33 KB'lık bazı polyfill'leri yüklemeniz gerekir. Daha fazla bilgiye buradan ulaşabilirsiniz.

Koşullu paketler

Lit ekibi, biri IE 11 için, diğeri modern tarayıcılar için olmak üzere iki farklı paket sunmanızı önerir. Bu durumun çeşitli avantajları vardır:

  • ES 6'ya hizmet vermek daha hızlıdır ve müşterilerinizin çoğu için uygundur.
  • Transpile edilmiş ES 5, paket boyutunu önemli ölçüde artırıyor
  • Koşullu paketler, iki dünyanın en iyi özelliklerini bir araya getirir.
    • IE 11 desteği
    • Modern tarayıcılarda yavaşlama olmaz

Koşullu olarak sunulan bir paket oluşturma hakkında daha fazla bilgiyi belgeler sitemizde burada bulabilirsiniz.