にわかプラス

にわかが玄人になることを夢見るサイトです。社会や国際のトレンド、プログラミングや電子工作のことについて勉強していきたいです。

Lit公式ドキュメントまとめ レンダリング

sponsor

Components

Components

  • 前記事 Litの定義

www.niwaka-plus.com

レンダリング

以下のようにrender()メソッドの中にhtmlタグを用いてhtmlを描画することができます。

import {LitElement, html} from 'lit';

class MyElement extends LitElement {
  render() {
    return html`<p>Hello from my template.</p>`;
  }
}
customElements.define('my-element', MyElement);

Hello from my template.

render()の中にはhtmlタグだけではなくJavaScript/TypeScriptを入れることができます。

レンダリングできる値

先程のrender()はhtmlタグを返しますが、Litがレンダリングできるhtml要素のものであれば何でも返り値にすることができます。

  • string, Booleanのようなプリミティブな値
  • DOMノード
  • noChangeやnothingといったセンチネルな値(後に公式ドキュメントで解説)
  • サポートされた配列やイテレータ

具体的な用法は公式ドキュメントのこちらを参照ください。 https://lit.dev/docs/templates/expressions/#child-expressions

また、html要素ではないがレンダリングできるものとしてはsvgがあります。

render() の良い書き方

render()内は以下のように書くことを推奨しています:

  • コンポーネントのステートを変化させないこと
  • 副作用を生じさせないこと
  • コンポーネントのプロパティはインプットとしてのみ用いること
  • 受け取ったプロパティの値と同じ値を返すこと

これらのガイドラインを守ることで、コードをわかりやすく保つことができます。
render()外でDOMを作ることも避けるべきです。
その代わり、コンポーネントのテンプレートを状態の関数として表現し、その状態をプロパティに取り込むのが良いでしょう。

Litコンポーネントテンプレートの作成

Litテンプレートは他のテンプレートを呼び出すことができます。
以下はヘッダー、フッター、メインコンテンツをもつ<my-page>というテンプレート作成する例です。

import {LitElement, html} from 'lit';

class MyPage extends LitElement {
  static properties = {
    article: {attribute: false},
  };

  constructor() {
    super();
    this.article = {
      title: 'My Nifty Article',
      text: 'Some witty text.',
    };
  }

  headerTemplate() {
    return html`<header>${this.article.title}</header>`;
  }

  articleTemplate() {
    return html`<article>${this.article.text}</article>`;
  }

  footerTemplate() {
    return html`<footer>Your footer here.</footer>`;
  }

  render() {
    return html`
      ${this.headerTemplate()}
      ${this.articleTemplate()}
      ${this.footerTemplate()}
    `;
  }
}
customElements.define('my-page', MyPage);

上記をコンポーネント分割してみます。

// my-page.js
import {LitElement, html} from 'lit';

import './my-header.js';
import './my-article.js';
import './my-footer.js';

class MyPage extends LitElement {
  render() {
    return html`
      <my-header></my-header>
      <my-article></my-article>
      <my-footer></my-footer>
    `;
  }
}
customElements.define('my-page', MyPage);
// my-header.js
import {LitElement, html} from 'lit';

class MyHeader extends LitElement {
  render() {
    return html`
      <header>header</header>
    `;
  }
}
customElements.define('my-header', MyHeader);

my-article.jsとmy-footer.jsも同じ構造なので省略します。
このようにしてLitコンポーネントは別のLitコンポーネントを呼び出すことができます。

テンプレートはいつレンダリングされるか

まず、ページのDOMに追加されると最初にそのテンプレートをレンダリングします。
それ以降はReactiveなプロパティが変更されるたびにアップレードサイクルが走り、再レンダリングされます。(Reactiveなプロパティはユーザが自由に設定することができます。後ほど解説があります。)

Litのアップデード処理は、パフォーマンスと効率を最大限にするよう設計されています。
例えば複数のreactiveな値が一度に変更された場合、アップデードサイクルのトリガーは1度だけ発行され、非同期にマイクロタスクのタイミングでアップデード処理が実行されます。

加えて、DOMはすべてが再レンダリングされるわけではなく、変更の合った部分のみをレンダリングします。
レンダリングについてさらに詳しい公式ドキュメントはこちら。
https://lit.dev/docs/components/properties/#when-properties-change

DOMのカプセル化

LitはShdow DOMを用いて、コンポーネントがレンダリングするDOMをカプセル化しています。
Shadow DOMはhtml要素自体を作成し、ドキュメントツリーとは完全に分離されていて、そのため相互運用やスタイルのカプセル化などが可能となっています。

Shdow DOM自体にいての解説はこちら。
https://web.dev/shadowdom-v1/

LitコンポーネントにおいてShadow DOMがどのように動いているかの解説はこちら。
https://lit.dev/docs/components/shadow-dom/